inesita-livereload 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0bcad77d3b30fab74bd38b83adfac7a804c8e009
4
+ data.tar.gz: 8087782f82d73274251d4164b6f19bb81484a720
5
+ SHA512:
6
+ metadata.gz: 2f3a59ddd8ce24b41273ac2c00984b85208490aac81fa47099166e18600cf36e36d3ff15c5802a85013c07ace02550ed82a1b50bc374245bbc0fdf242792ad7c
7
+ data.tar.gz: f789eba69737e910090fdfec758218bd6a3d5b29bcc2ccd0946670d79fb9652d5017305e0cb20894c96f244d06fd369c098d8635a5585e34d668057569ee8ff9
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/
2
+ *.gem
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Michał Kalbarczyk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Inesita Livereload Support [![Gem Version](https://badge.fury.io/rb/inesita-livereload.svg)](http://badge.fury.io/rb/inesita-liveraload) [![Code Climate](https://codeclimate.com/github/inesita-rb/inesita-livereload/badges/gpa.svg)](https://codeclimate.com/github/inesita-rb/inesita-livereload) [![Dependency Status](https://gemnasium.com/inesita-rb/inesitai-livereload.svg)](https://gemnasium.com/inesita-rb/inesita-livereload)
2
+
3
+ For more information see [Inesita](https://github.com/inesita-rb/inesita).
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'inesita-livereload'
3
+ s.version = '0.1.0'
4
+ s.authors = ['Michał Kalbarczyk']
5
+ s.email = 'fazibear@gmail.com'
6
+ s.homepage = 'http://github.com/inesita-rb/inesita-livereload'
7
+ s.summary = 'Livereload module for Inesita'
8
+ s.description = s.summary
9
+ s.license = 'MIT'
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.require_paths = ['lib']
15
+
16
+ s.add_dependency 'opal', '~> 0.9'
17
+ #s.add_dependency 'inesita', '~> 0.4.0'
18
+ s.add_dependency 'listen', '~> 3.0'
19
+ s.add_dependency 'websocket', '~> 1.0'
20
+ end
@@ -0,0 +1,7 @@
1
+ require 'opal'
2
+ require 'socket'
3
+ require 'websocket'
4
+ require 'singleton'
5
+ require 'vendor/rubame'
6
+ require 'inesita/app_files_listener'
7
+ require 'inesita/live_reload'
@@ -0,0 +1,42 @@
1
+ module Inesita
2
+ class AppFilesListener
3
+ include Singleton
4
+ CURRENT_DIR = Dir.pwd
5
+
6
+ def initialize
7
+ @websockets = []
8
+ listener = Listen.to(Config::APP_DIR) do |modified, added, _removed|
9
+ (modified + added).each do |file|
10
+ @websockets.each do |ws|
11
+ ws.send transform_filename(file)
12
+ end
13
+ end
14
+ end
15
+ listener.start
16
+ end
17
+
18
+ def add_ws(ws)
19
+ @websockets << ws
20
+ end
21
+
22
+ def rm_ws(ws)
23
+ @websockets.delete(ws)
24
+ end
25
+
26
+ def transform_filename(filename)
27
+ filename.sub!(CURRENT_DIR, '')
28
+ path = filename.split('/')
29
+ path.delete('')
30
+ path.delete(Config::APP_DIR)
31
+ path = path.join('/').split('.')
32
+
33
+ prefix = Config::ASSETS_PREFIX
34
+ name = path.first
35
+ if path.include?('rb') || path.include?('js')
36
+ "#{prefix}|#{name}|js"
37
+ elsif path.include?('sass') || path.include?('css')
38
+ "#{prefix}|stylesheet|css"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ module Inesita
2
+ class LiveReload
3
+ INJECT_CODE = Opal.compile(File.read(File.expand_path('../../../opal/inesita/live_reload.rb', __FILE__)))
4
+
5
+ def initialize(app, _options = {})
6
+ @app = app
7
+ Thread.new do
8
+ begin
9
+ init_live_reload
10
+ rescue => e
11
+ puts e
12
+ end
13
+ end
14
+ end
15
+
16
+ def call(env)
17
+ status, headers, body = @app.call(env)
18
+ if status == 200
19
+ new_body = inject_script(body)
20
+ headers['Content-Length'] = new_body.bytesize.to_s
21
+ [status, headers, [new_body]]
22
+ else
23
+ [status, headers, body]
24
+ end
25
+ end
26
+
27
+ def inject_script(body)
28
+ new_body = ''
29
+ body.each { |line| new_body += line.to_s }
30
+ new_body.gsub('{ Opal.loaded', "{ #{INJECT_CODE} Opal.loaded")
31
+ end
32
+
33
+ def init_live_reload
34
+ AppFilesListener.instance
35
+ server = Rubame::Server.new('0.0.0.0', 23654)
36
+ loop do
37
+ server.run do |ws|
38
+ ws.onopen { AppFilesListener.instance.add_ws(ws) }
39
+ ws.onclose { AppFilesListener.instance.rm_ws(ws) }
40
+ ws.onmessage { |msg| ws.send 'pong' if msg == 'ping' }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,153 @@
1
+ module Rubame
2
+ class Server
3
+ def initialize(host, port)
4
+ Socket.do_not_reverse_lookup
5
+ @hostname = host
6
+ @port = port
7
+
8
+ @reading = []
9
+ @writing = []
10
+
11
+ @clients = {} # Socket as key, and Client as value
12
+
13
+ @socket = TCPServer.new(@hostname, @port)
14
+ @reading.push @socket
15
+ end
16
+
17
+ def accept
18
+ socket = @socket.accept_nonblock
19
+ @reading.push socket
20
+ handshake = WebSocket::Handshake::Server.new
21
+ client = Rubame::Client.new(socket, handshake, self)
22
+
23
+ while line = socket.gets
24
+ client.handshake << line
25
+ break if client.handshake.finished?
26
+ end
27
+ if client.handshake.valid?
28
+ @clients[socket] = client
29
+ client.write handshake.to_s
30
+ client.opened = true
31
+ return client
32
+ else
33
+ close(client)
34
+ end
35
+ return nil
36
+ end
37
+
38
+ def read(client)
39
+
40
+ pairs = client.socket.recvfrom(2000)
41
+ messages = []
42
+
43
+ if pairs[0].length == 0
44
+ close(client)
45
+ else
46
+ client.frame << pairs[0]
47
+
48
+ while f = client.frame.next
49
+ if (f.type == :close)
50
+ close(client)
51
+ return messages
52
+ else
53
+ messages.push f
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ return messages
60
+
61
+ end
62
+
63
+ def close(client)
64
+ @reading.delete client.socket
65
+ @clients.delete client.socket
66
+ begin
67
+ client.socket.close
68
+ ensure
69
+ client.closed = true
70
+ end
71
+ end
72
+
73
+ def run(&blk)
74
+ readable, _writable = IO.select(@reading, @writing)
75
+
76
+ if readable
77
+ readable.each do |socket|
78
+ client = @clients[socket]
79
+ if socket == @socket
80
+ client = accept
81
+ else
82
+ msg = read(client)
83
+ client.messaged = msg
84
+ end
85
+
86
+ blk.call(client) if client and blk
87
+ end
88
+ end
89
+ end
90
+
91
+ def stop
92
+ @socket.close
93
+ end
94
+ end
95
+
96
+ class Client
97
+ attr_accessor :socket, :handshake, :frame, :opened, :messaged, :closed
98
+
99
+ def initialize(socket, handshake, server)
100
+ @socket = socket
101
+ @handshake = handshake
102
+ @frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version)
103
+ @opened = false
104
+ @messaged = []
105
+ @closed = false
106
+ @server = server
107
+ end
108
+
109
+ def write(data)
110
+ @socket.write data
111
+ end
112
+
113
+ def send(data)
114
+ frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => data, :type => :text)
115
+ begin
116
+ @socket.write frame
117
+ @socket.flush
118
+ rescue
119
+ @server.close(self) unless @closed
120
+ end
121
+ end
122
+
123
+ def onopen(&blk)
124
+ if @opened
125
+ begin
126
+ blk.call
127
+ ensure
128
+ @opened = false
129
+ end
130
+ end
131
+ end
132
+
133
+ def onmessage(&blk)
134
+ if @messaged.size > 0
135
+ begin
136
+ @messaged.each do |x|
137
+ blk.call(x.to_s)
138
+ end
139
+ ensure
140
+ @messaged = []
141
+ end
142
+ end
143
+ end
144
+
145
+ def onclose(&blk)
146
+ if @closed
147
+ begin
148
+ blk.call
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,79 @@
1
+ require 'inesita'
2
+
3
+ module Inesita
4
+ WebSocket = JS.global.JS[:WebSocket]
5
+ Document = JS.global.JS[:document]
6
+ Head = Document.JS[:head]
7
+ Window = JS.global
8
+
9
+ module Component
10
+ alias_method :old_mount_to, :mount_to
11
+ def mount_to(element)
12
+ Window.JS.addEventListener('inesita:refresh', -> { render! }, false)
13
+ old_mount_to(element)
14
+ end
15
+ end
16
+
17
+ class LiveReload
18
+ def initialize
19
+ connect
20
+ end
21
+
22
+ def connect
23
+ ws = `new WebSocket('ws://0.0.0.0:23654')`
24
+ ws.JS[:onmessage] = ->(e) { on_file_change(e.JS[:data]) }
25
+ ws.JS[:onclose] = -> { reconnect }
26
+ end
27
+
28
+ def reconnect
29
+ JS.setTimeout(-> { connect }, 1000)
30
+ end
31
+
32
+ def on_file_change(filename)
33
+ prefix, mod, ext = filename.split('|')
34
+ filename = "#{prefix}/#{mod}.self.#{ext}"
35
+ case ext
36
+ when 'js'
37
+ replace_js(filename, mod)
38
+ when 'css'
39
+ replace_css(filename)
40
+ else
41
+ raise Error, "Don't know how to reload #{ext} file!"
42
+ end
43
+ end
44
+
45
+ def replace_js(filename, mod)
46
+ s = create_element(:script, type: 'text/javascript', src: filename, onload: lambda do
47
+ Opal.load(mod)
48
+ Window.JS.dispatchEvent(`new Event('inesita:refresh')`)
49
+ end)
50
+ replace_or_append(s, 'script') { |t| t.JS[:src].match(filename) }
51
+ end
52
+
53
+ def replace_css(filename)
54
+ s = create_element(:link, rel: 'stylesheet', type: 'text/css', href: filename)
55
+ replace_or_append(s, 'link') { |t| t.JS[:href].match(filename) }
56
+ end
57
+
58
+ def create_element(name, attrs = {})
59
+ s = Document.JS.createElement(name)
60
+ s.JS[:onload] = attrs.delete(:onload)
61
+ attrs.each do |k, v|
62
+ s.JS.setAttribute(k, v)
63
+ end
64
+ s
65
+ end
66
+
67
+ def replace_or_append(tag, tags, &block)
68
+ tags = Document.JS.getElementsByTagName(tags)
69
+ tags.JS[:length].times do |i|
70
+ next unless block.call(tags.JS.item(i))
71
+ Head.JS.replaceChild(tag, tags.JS.item(i))
72
+ return
73
+ end
74
+ Head.JS.appendChild(tag)
75
+ end
76
+ end
77
+ end
78
+
79
+ Inesita::LiveReload.new
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: inesita-livereload
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michał Kalbarczyk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: opal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: listen
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: websocket
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: Livereload module for Inesita
56
+ email: fazibear@gmail.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".gitignore"
62
+ - Gemfile
63
+ - LICENSE.md
64
+ - README.md
65
+ - Rakefile
66
+ - inesita-livereload.gemspec
67
+ - lib/inesita-livereload.rb
68
+ - lib/inesita/app_files_listener.rb
69
+ - lib/inesita/live_reload.rb
70
+ - lib/vendor/rubame.rb
71
+ - opal/inesita/live_reload.rb
72
+ homepage: http://github.com/inesita-rb/inesita-livereload
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.5.1
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Livereload module for Inesita
96
+ test_files: []