inesita-livereload 0.1.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 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: []