staticme 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c83685ffa08a0f05408fb3fada3051c45ee9b88d
4
- data.tar.gz: 771cecf1e3cd1b65b484a2833e9254be9f423569
3
+ metadata.gz: d051eed734b566be6c2d086913e8c92866e7db57
4
+ data.tar.gz: 9bbdc61c67b0db02b7e12bd494f0c446a8751dde
5
5
  SHA512:
6
- metadata.gz: 832e547bb2acc6bf7f24d025b3cfdaa48c692db8a596c868f4f8f785d4012a84c07cbe2abadd283b2ae094d3c417a5a823117fe9081a2ad0d4930df0dcd00400
7
- data.tar.gz: 0cfcd02b083d1664099833c3c502125c8075827f054bba98f4feb46f6393ac32d3964ed68c702a0c7509ad25ee7d277d58bad572e29611d1d143e8f68858e97d
6
+ metadata.gz: 8d71b11562c1cc46e9bde1a5b13b815c72f06a85919bc585219a20069c1f1dd93d4c6ae061e5bfcdcbbe7c3b1372ec04e9343a71aff710003486517bd3b58168
7
+ data.tar.gz: f9a45456d0e19dba2d2d80b35b7020542eae609dff94f330bf96e5bf9f5753d62e4319f2031f3476ae427cb5af40e1ac029c78ac370baff7c3a6e4f50da547aa
data/README.md CHANGED
@@ -14,9 +14,10 @@ gem install staticme
14
14
 
15
15
  ### Options:
16
16
 
17
- * -f, --path: path to be served in, current if omitted
18
- * -p, --port: port to bind, 8080 by default
19
- * -h, --host: host to start up, 0.0.0.0 by default
17
+ * -f, --path: path to be served in, current if omitted
18
+ * -p, --port: port to bind, 8080 by default
19
+ * -h, --host: host to start up, 0.0.0.0 by default
20
+ * -i, --index: index file name, index.html by default
20
21
 
21
22
  ### Starting up
22
23
 
@@ -30,4 +31,4 @@ staticme
30
31
  >> Listening on 0.0.0.0:8080, CTRL+C to stop
31
32
  ```
32
33
 
33
- In browser open page http://127.0.0.1:8080
34
+ In browser open page http://127.0.0.1:8080
data/bin/staticme CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'staticme'
3
3
 
4
- Staticme.run! Hash[*ARGV]
4
+ Staticme.run! Hash[*ARGV]
data/lib/staticme/app.rb CHANGED
@@ -12,13 +12,15 @@ module Staticme
12
12
 
13
13
  def bind
14
14
  params = self.params
15
-
15
+
16
16
  Rack::StaticmeBuilder.new do
17
-
18
- use Rack::CommonLogger
19
-
20
- index = params[:index]
21
- path = params[:path]
17
+
18
+ index = params[:index]
19
+ path = params[:path]
20
+
21
+ map '/staticme/autoreload.js' do
22
+ run Staticme::Scripts::Autoreload
23
+ end
22
24
 
23
25
  map /^\/.+/ do
24
26
  run Rack::Directory.new( path )
@@ -29,10 +31,10 @@ module Staticme
29
31
  run Rack::File.new( File.join( path, index ) )
30
32
  end
31
33
  end
32
-
34
+
33
35
  end
34
36
  end
35
37
 
36
38
  end
37
39
 
38
- end
40
+ end
@@ -3,28 +3,32 @@ module Staticme
3
3
  module Arguments
4
4
 
5
5
  ARGS = {
6
- :path => {
6
+ :path => {
7
7
  :short => 'f',
8
8
  :default => Proc.new { Dir.pwd }
9
9
  },
10
- :port => {
10
+ :port => {
11
11
  :short => 'p',
12
12
  :default => 8080,
13
13
  :sanitizer => Proc.new { |v| v.to_i }
14
14
  },
15
- :host => {
15
+ :host => {
16
16
  :short => 'h',
17
17
  :default => '0.0.0.0'
18
18
  },
19
- :index => {
19
+ :index => {
20
20
  :short => 'i',
21
21
  :default => 'index.html'
22
+ },
23
+ :ws_port => {
24
+ :short => 'ws',
25
+ :default => 8090,
22
26
  }
23
27
  }
24
28
 
25
29
  def parse_input(argv)
26
30
  params = Hash.new
27
-
31
+
28
32
  ARGS.each_pair do |param_name, param_attrs|
29
33
  param_shorten_name = param_attrs[:shorten]
30
34
  default = param_attrs[:default]
@@ -44,4 +48,4 @@ module Staticme
44
48
 
45
49
  end
46
50
 
47
- end
51
+ end
@@ -0,0 +1,34 @@
1
+ module Staticme
2
+
3
+ module Events
4
+
5
+ module Dispatcher
6
+
7
+ def self.included(base)
8
+ base.class_eval do
9
+
10
+ attr_accessor :events
11
+
12
+ def on(event_name, &event_handler)
13
+ ((self.events ||= {})[event_name.to_sym] ||= []).push(event_handler)
14
+ self
15
+ end
16
+
17
+ def emit(event_name, *args)
18
+ ((self.events ||= {})[event_name.to_sym]).tap do |event_pool|
19
+ return if event_pool.nil?
20
+ event_pool.map do |handler|
21
+ handler.call(*args)
22
+ end
23
+ end
24
+ self
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,76 @@
1
+ require 'rubygems'
2
+ require 'rb-fsevent'
3
+ require 'digest/md5'
4
+
5
+ module Staticme
6
+
7
+ module Events
8
+
9
+ module Emitters
10
+
11
+ class FileChanged
12
+
13
+ attr_accessor :fsevent
14
+ attr_accessor :_broadcast_tail
15
+
16
+ def initialize
17
+ self.fsevent = FSEvent.new
18
+ self._broadcast_tail = Hash.new
19
+ end
20
+
21
+ def bind!
22
+
23
+ params = Staticme.params
24
+
25
+ Staticme.on(:fs_changed) do
26
+ Staticme.broadcast(:event => 'fs_change')
27
+ end.on(:staticme_terminated) do
28
+ self.stop!
29
+ end
30
+
31
+ fsevent.watch params[:path] do |dirs|
32
+ signature = get_signature(dirs)
33
+ next if already_broadcasted?(signature)
34
+ tail_broadcast(signature)
35
+ Staticme.logger.info 'FS change, publishing the event.'
36
+ Staticme.emit(:fs_changed, dirs)
37
+ end
38
+
39
+ Thread.abort_on_exception = true
40
+
41
+ Thread.new do
42
+ fsevent.run
43
+ end
44
+
45
+ end
46
+
47
+ def stop!
48
+ fsevent.stop
49
+ end
50
+
51
+ protected
52
+
53
+ def get_signature(params)
54
+ Digest::MD5.hexdigest(params.to_s)
55
+ end
56
+
57
+ def already_broadcasted?(signature)
58
+ !self._broadcast_tail[signature].nil?
59
+ end
60
+
61
+ def tail_broadcast(signature)
62
+ self._broadcast_tail[signature] = true
63
+ # fsevent triggers 2 events in a row
64
+ # just a simple filtering
65
+ EM.add_timer 1 do
66
+ self._broadcast_tail.delete(signature)
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -6,7 +6,11 @@ module Staticme
6
6
  :server_super
7
7
 
8
8
  def start(app, params, &blk)
9
- server_super.run(app, params) do |server|
9
+ server_params = Hash.new
10
+ %w(host port).map(&:to_sym).each do |k|
11
+ (server_params[k.to_s.capitalize.to_sym] = params[k]) if !params[k].nil?
12
+ end
13
+ server_super.run(app, server_params) do |server|
10
14
  self.server = server
11
15
  blk.call(server) if block_given?
12
16
  end
@@ -18,4 +22,4 @@ module Staticme
18
22
 
19
23
  end
20
24
 
21
- end
25
+ end
@@ -0,0 +1,96 @@
1
+ module Staticme
2
+
3
+ module Scripts
4
+
5
+ module Autoreload
6
+
7
+ extend self
8
+
9
+ def js_output
10
+ <<-eos
11
+ (function() {
12
+ window.addEventListener('DOMContentLoaded', function() {
13
+
14
+ var WS_RECONNECT_TIMEOUT = 1000;
15
+
16
+ var Dispatcher = function(routes) {
17
+ this.init(routes);
18
+ }
19
+
20
+ Dispatcher.prototype.init = function(routes) {
21
+ this.routes = routes;
22
+ }
23
+
24
+ Dispatcher.prototype.dispatch = function(message) {
25
+ console.log('Dispatching data: ', message);
26
+ var event_name = message.event;
27
+ if (this.routes[event_name]) {
28
+ this.routes[event_name](message);
29
+ }
30
+ }
31
+
32
+ var location = window.location,
33
+ ws_url = "ws://" + location.hostname + ':#{Staticme.params[:ws_port].to_i}',
34
+ dispatcher = new Dispatcher({
35
+ 'fs_change': function() {
36
+ console.log('Remote fs change, reloading the page...');
37
+ window.location.reload();
38
+ }
39
+ }),
40
+ ws = _open_ws(),
41
+ _reconnect_timer;
42
+
43
+ function _open_ws() {
44
+
45
+ if (ws) {
46
+ _close_ws();
47
+ }
48
+
49
+ ws = new WebSocket(ws_url);
50
+
51
+ ws.onopen = function() {
52
+ window.clearTimeout(_reconnect_timer);
53
+ console.info('WebSocket connection has been established.');
54
+ }
55
+
56
+ ws.onmessage = function(event) {
57
+ var message = JSON.parse(event.data);
58
+ console.info('A new message received: ' . message);
59
+ dispatcher.dispatch(message);
60
+ }
61
+
62
+ ws.onerror = function(e) {
63
+ window.clearTimeout(_reconnect_timer);
64
+ console.error('An unexpected error occured.', e);
65
+ _reconnect_timer = window.setTimeout(_open_ws, WS_RECONNECT_TIMEOUT);
66
+ }
67
+
68
+ ws.onclose = function() {
69
+ window.clearTimeout(_reconnect_timer);
70
+ console.warn('WebSocket connection has been closed by the server.');
71
+ _reconnect_timer = window.setTimeout(_open_ws, WS_RECONNECT_TIMEOUT);
72
+ }
73
+
74
+ return ws;
75
+ }
76
+
77
+ function _close_ws() {
78
+ ws.close();
79
+ ws = null;
80
+ }
81
+
82
+ }, false);
83
+ })(window);
84
+ eos
85
+ end
86
+
87
+ def call(arg)
88
+ [200, {:'Content-Type' => 'application/javascript'}, [js_output]]
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
@@ -0,0 +1,4 @@
1
+ module Staticme
2
+ module Scripts
3
+ end
4
+ end
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'em-websocket'
3
+ require 'json'
4
+
5
+ module Staticme
6
+
7
+ class WebSocket
8
+
9
+ attr_accessor :params
10
+
11
+ def initialize(params)
12
+ self.params = params
13
+ @pool = []
14
+ end
15
+
16
+ def run!(&block)
17
+ EM.next_tick do
18
+ Staticme.logger.debug "Starting WebSocket server on ws://#{params[:host]}:#{params[:ws_port]}"
19
+ EM::WebSocket.run(:host => params[:host], :port => params[:ws_port]) do |ws|
20
+ @pool.push ws
21
+ ws.onclose do
22
+ @pool.delete ws
23
+ end
24
+ end
25
+ block.call if block_given?
26
+ end
27
+ end
28
+
29
+ def emit(event)
30
+ Staticme.logger.debug("Broadcasting event: #{event.to_json}")
31
+ @pool.each do |ws|
32
+ next if ws.nil?
33
+ ws.send event.to_json
34
+ end
35
+ end
36
+
37
+ def stop!
38
+ Staticme.logger.debug('Stopping WebSocket server')
39
+ EM.stop!
40
+ @pool = []
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
data/lib/staticme.rb CHANGED
@@ -1,9 +1,15 @@
1
1
  require 'rubygems'
2
+ require 'logger'
2
3
 
3
4
  require 'staticme/app'
4
5
  require 'staticme/arguments'
6
+ require 'staticme/events/dispatcher'
7
+ require 'staticme/events/emitters/file_changed'
5
8
  require 'staticme/runner'
6
9
  require 'staticme/thin_runner'
10
+ require 'staticme/scripts'
11
+ require 'staticme/scripts/autoreload'
12
+ require 'staticme/web_socket'
7
13
 
8
14
  module Staticme
9
15
 
@@ -11,22 +17,57 @@ module Staticme
11
17
 
12
18
  extend Staticme::Arguments
13
19
 
14
- attr_accessor :runner
20
+ include Staticme::Events::Dispatcher
21
+
22
+ attr_accessor :app,
23
+ :logger,
24
+ :params,
25
+ :runner,
26
+ :ws
15
27
 
16
28
  def run!(argv, &blk)
17
29
 
18
- params = parse_input(argv)
30
+ self.params = parse_input(argv)
31
+ self.app = Staticme::App.new(params).bind
32
+ self.logger = ::Logger.new(STDOUT)
33
+ self.logger.level = ::Logger::DEBUG
34
+ self.runner = Staticme::ThinRunner.new
35
+ self.ws = Staticme::WebSocket.new(params)
19
36
 
20
- app = Staticme::App.new(params).bind
37
+ register_event_handlers
21
38
 
22
- runner = Staticme::ThinRunner.new
39
+ emit(:staticme_inited)
23
40
 
24
- runner.start(app, params, &blk)
41
+ runner.start(app, params) do |server|
42
+ emit(:web_server_started, server)
43
+ logger.debug('Web server started')
44
+ ws.run!
45
+ blk.call(server) if !blk.nil?
46
+ end
47
+ end
25
48
 
49
+ def broadcast(data)
50
+ begin
51
+ ws.emit(data)
52
+ rescue
53
+ logger.warn('Unable to broadcast event')
54
+ end
26
55
  end
27
56
 
28
57
  def stop!
58
+ self.emit(:staticme_terminated)
29
59
  runner.stop
30
60
  end
31
61
 
62
+ private
63
+
64
+ def register_event_handlers
65
+ %w(FileChanged).each do |emitter_name|
66
+ klass = Object.const_get "Staticme::Events::Emitters::#{emitter_name}"
67
+ logger.debug("Registering event emitter: #{klass.to_s}")
68
+ emitter = klass.new
69
+ emitter.bind!
70
+ end
71
+ end
72
+
32
73
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: staticme
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 4pcbr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-21 00:00:00.000000000 Z
11
+ date: 2014-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: em-websocket
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rb-fsevent
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: shoulda
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -109,8 +137,13 @@ files:
109
137
  - lib/staticme.rb
110
138
  - lib/staticme/app.rb
111
139
  - lib/staticme/arguments.rb
140
+ - lib/staticme/events/dispatcher.rb
141
+ - lib/staticme/events/emitters/file_changed.rb
112
142
  - lib/staticme/runner.rb
143
+ - lib/staticme/scripts.rb
144
+ - lib/staticme/scripts/autoreload.rb
113
145
  - lib/staticme/thin_runner.rb
146
+ - lib/staticme/web_socket.rb
114
147
  - LICENSE.txt
115
148
  - README.md
116
149
  - bin/staticme