{"action":"created","issue":{"url":"https://api.github.com/repos/joeyespo/grip/issues/135","labels_url":"https://api.github.com/repos/joeyespo/grip/issues/135/labels{/name}","comments_url":"https://api.github.com/repos/joeyespo/grip/issues/135/comments","events_url":"https://api.github.com/repos/joeyespo/grip/issues/135/events","html_url":"https://github.com/joeyespo/grip/pull/135","id":93030887,"number":135,"title":"Add autorefresh","user":{"login":"markbt","id":5471678,"avatar_url":"https://avatars.githubusercontent.com/u/5471678?v=3","gravatar_id":"","url":"https://api.github.com/users/markbt","html_url":"https://github.com/markbt","followers_url":"https://api.github.com/users/markbt/followers","following_url":"https://api.github.com/users/markbt/following{/other_user}","gists_url":"https://api.github.com/users/markbt/gists{/gist_id}","starred_url":"https://api.github.com/users/markbt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markbt/subscriptions","organizations_url":"https://api.github.com/users/markbt/orgs","repos_url":"https://api.github.com/users/markbt/repos","events_url":"https://api.github.com/users/markbt/events{/privacy}","received_events_url":"https://api.github.com/users/markbt/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-07-04T17:07:33Z","updated_at":"2015-07-05T04:46:12Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/joeyespo/grip/pulls/135","html_url":"https://github.com/joeyespo/grip/pull/135","diff_url":"https://github.com/joeyespo/grip/pull/135.diff","patch_url":"https://github.com/joeyespo/grip/pull/135.patch"},"body":"I added autorefresh support.\r\n\r\nThis is somewhat like what's in joeyespo/grip#19 (I must admit, I didn't see that until *after* I'd written the code), but I think it might be a better implementation.\r\n\r\nThis implementation uses an event stream, in-place content replacement, and is triggered by the file changing.  This means:\r\n- We only have HTTP traffic and thus a hit to github when the file actually changes.\r\n- The update happens after about a second (can tweak the polling time in server.py, but I found 1s was adequate).\r\n- If you're scrolled a long way down the document, it won't scroll back up to the top or to the last anchor you were at (as happens with a reload).\r\n"},"comment":{"url":"https://api.github.com/repos/joeyespo/grip/issues/comments/118579476","html_url":"https://github.com/joeyespo/grip/pull/135#issuecomment-118579476","issue_url":"https://api.github.com/repos/joeyespo/grip/issues/135","id":118579476,"user":{"login":"joeyespo","id":634573,"avatar_url":"https://avatars.githubusercontent.com/u/634573?v=3","gravatar_id":"","url":"https://api.github.com/users/joeyespo","html_url":"https://github.com/joeyespo","followers_url":"https://api.github.com/users/joeyespo/followers","following_url":"https://api.github.com/users/joeyespo/following{/other_user}","gists_url":"https://api.github.com/users/joeyespo/gists{/gist_id}","starred_url":"https://api.github.com/users/joeyespo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joeyespo/subscriptions","organizations_url":"https://api.github.com/users/joeyespo/orgs","repos_url":"https://api.github.com/users/joeyespo/repos","events_url":"https://api.github.com/users/joeyespo/events{/privacy}","received_events_url":"https://api.github.com/users/joeyespo/received_events","type":"User","site_admin":false},"created_at":"2015-07-05T04:46:12Z","updated_at":"2015-07-05T04:46:12Z","body":"Wow, yes! This is a great implementation.\r\n\r\nI've been thinking about how to approach this for a few weekends, and I think this is the cleanest form yet. May as well capture some of those approaches here. I tried\r\n- Using livereload, but really didn't want that bulky dependency\r\n- Using websockets with the excellent [flask-sockets](https://github.com/kennethreitz/flask-sockets), but you still had to run an additional server which added some complexity to a local-running app\r\n- Long-polling, but this made the output in the terminal *very* noisy\r\n- This generator technique, but emitted socket errors in the output in the terminal (Your implementation has this problem too, but since you're changing content in-place instead of causing a full page refresh, it only happens *once*. I did find a way to silence them, but it's not great since you need to change something globally using `socket`. It's annoying, but shouldn't keep this PR from being pulled in.)\r\n\r\nI was also thinking about using [watchdog](https://github.com/gorakhargosh/watchdog), but ran into complexities there as well since it's built for watching directories, not files. Your polling approach is much smarter. Especially since it gets this moving. If people aren't satisfied with polling, more advanced monitoring could be added later once the autorefresh logic is ironed out. (Seems so obvious now. Hindsight, right?)\r\n\r\nSo I'm in. Your approach seems like the right one, and the in-place content replacement is an awesome touch!\r\n\r\nCan I suggest a couple things before pulling this in? The *updated at* time is nice to know, however it's the only content that doesn't currently match GitHub's visualization. Think you can `print(' * Detected change in {!r}, refreshing'.format(filename))` in the terminal instead? It should be obvious that the document changed in most cases, and in the edge cases where you're not sure, you can still check the terminal for some confidence. (Keeping the *(updating)* text is fine.)\r\n"}}