opal-connect 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/lib/opal/connect/plugins/dom.rb +247 -0
- data/lib/opal/connect/plugins/events.rb +69 -0
- data/lib/opal/connect/plugins/html.rb +79 -0
- data/lib/opal/connect/plugins/server.rb +65 -0
- data/lib/opal/connect/version.rb +5 -0
- data/lib/opal/connect.rb +226 -0
- data/opal-connect.gemspec +26 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7ee622b62ef725481b6c98e606815d1498b4f847
|
4
|
+
data.tar.gz: a27b1855decd4c7af2df0e92974a3be0ee5e9b87
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fd927ee3c2efe5fa14c11bd096b5f4cf261dedf30160ccb269254754941b679e06cc6659d9b9b649221a8a2d3a2120821ab10a7f689027ef39171623644e3ea1
|
7
|
+
data.tar.gz: 33dd26a3bef73247df061b1ed8c52751abd04a58bdb2568fc11be8f8e9ed5f2c785f708366085c952d561f07dee66e17b42e3f061d9e151827ccd5499b4eefc8
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 cj
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Opal::Connect
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'opal-connect'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install opal-connect
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/opal-connect/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
if RUBY_ENGINE != 'opal'
|
2
|
+
require 'oga'
|
3
|
+
end
|
4
|
+
|
5
|
+
module Opal
|
6
|
+
module Connect
|
7
|
+
module ConnectPlugins
|
8
|
+
# https://github.com/jeremyevans/roda/blob/master/lib/roda.rb#L16
|
9
|
+
# A thread safe cache class, offering only #[] and #[]= methods,
|
10
|
+
# each protected by a mutex.
|
11
|
+
module Dom
|
12
|
+
extend self
|
13
|
+
|
14
|
+
if RUBY_ENGINE != 'opal'
|
15
|
+
def html_file(caller)
|
16
|
+
path = "#{Dir.pwd}/.connect/html/#{caller[0][/[^:]*/].sub(Dir.pwd, '')[1..-1].sub('.rb', '.html')}"
|
17
|
+
FileUtils.mkdir_p(File.dirname(path))
|
18
|
+
path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def html(scope = false, &block)
|
23
|
+
if !block_given?
|
24
|
+
HTML::DSL.html(&scope).to_html
|
25
|
+
else
|
26
|
+
HTML::DSL.scope!(scope).html(&block).to_html
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def [](selector)
|
31
|
+
if RUBY_ENGINE == 'opal'
|
32
|
+
file_name = false
|
33
|
+
else
|
34
|
+
file_name = html_file(caller)
|
35
|
+
|
36
|
+
if selector.is_a?(Symbol)
|
37
|
+
file_name = "#{file_name}.#{selector}"
|
38
|
+
selector = File.read(file_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Instance.new selector, file_name
|
43
|
+
end
|
44
|
+
|
45
|
+
class Instance
|
46
|
+
attr_reader :selector, :file_name, :dom
|
47
|
+
|
48
|
+
def initialize(selector, file_name = false)
|
49
|
+
@selector = selector
|
50
|
+
@file_name = file_name
|
51
|
+
|
52
|
+
if selector.is_a?(String)
|
53
|
+
if RUBY_ENGINE == 'opal'
|
54
|
+
@dom = Element[selector]
|
55
|
+
else
|
56
|
+
# multi-line
|
57
|
+
if selector["\n"]
|
58
|
+
@dom = Oga.parse_html(selector)
|
59
|
+
else
|
60
|
+
@dom = Oga.parse_html(File.read(file_name))
|
61
|
+
@dom = dom.css(selector) unless selector == 'html'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@dom = selector
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if RUBY_ENGINE != 'opal'
|
70
|
+
def to_html
|
71
|
+
if node.respond_to?(:first)
|
72
|
+
node.first.to_xml
|
73
|
+
else
|
74
|
+
node.to_xml
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
if dom.respond_to?(:first)
|
80
|
+
dom.first.to_xml
|
81
|
+
else
|
82
|
+
dom.to_xml
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def save template_name = false, remove = true
|
87
|
+
if template_name
|
88
|
+
File.write("#{file_name}.#{template_name.to_s}", self.to_html)
|
89
|
+
dom.remove if remove
|
90
|
+
else
|
91
|
+
File.write(file_name, self.to_html)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
alias save! save
|
95
|
+
|
96
|
+
def text(content)
|
97
|
+
if node.respond_to?(:inner_text)
|
98
|
+
node.inner_text = content
|
99
|
+
else
|
100
|
+
node.each { |n| n.inner_text = content }
|
101
|
+
end
|
102
|
+
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def attr(key, value = false)
|
107
|
+
if value
|
108
|
+
if node.respond_to? :set
|
109
|
+
node.set(key, value)
|
110
|
+
else
|
111
|
+
node.each { |n| n.set(key, value) }
|
112
|
+
end
|
113
|
+
else
|
114
|
+
if node.respond_to? :get
|
115
|
+
node.get(key)
|
116
|
+
else
|
117
|
+
node.first.get(key)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
def remove
|
125
|
+
if node.respond_to? :remove
|
126
|
+
node.remove
|
127
|
+
else
|
128
|
+
node.each { |n| n.remove }
|
129
|
+
end
|
130
|
+
|
131
|
+
self
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def append(content, &block)
|
136
|
+
# content becomes scope in this case
|
137
|
+
content = HTML::DSL.scope!(content).html(&block).to_html if block_given?
|
138
|
+
|
139
|
+
if RUBY_ENGINE == 'opal'
|
140
|
+
node.append(content)
|
141
|
+
else
|
142
|
+
if content.is_a? Dom::Instance
|
143
|
+
content = content.children
|
144
|
+
else
|
145
|
+
content = Oga.parse_html(content).children
|
146
|
+
end
|
147
|
+
|
148
|
+
if node.is_a?(Oga::XML::NodeSet)
|
149
|
+
node.each { |n| n.children = (n.children + content) }
|
150
|
+
else
|
151
|
+
node.children = (children + content)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
def prepend(content, &block)
|
159
|
+
# content becomes scope in this case
|
160
|
+
content = HTML::DSL.scope!(content).html(&block).to_html if block_given?
|
161
|
+
|
162
|
+
if RUBY_ENGINE == 'opal'
|
163
|
+
node.prepend(content)
|
164
|
+
else
|
165
|
+
if content.is_a? Dom::Instance
|
166
|
+
content = content.children
|
167
|
+
else
|
168
|
+
content = Oga.parse_html(content).children
|
169
|
+
end
|
170
|
+
|
171
|
+
if node.is_a?(Oga::XML::NodeSet)
|
172
|
+
node.each { |n| n.children = (content + n.children) }
|
173
|
+
else
|
174
|
+
node.children = (content + children)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
def html(content = false, &block)
|
182
|
+
# content becomes scope in this case
|
183
|
+
content = HTML::DSL.scope!(content).html(&block).to_html if block_given?
|
184
|
+
|
185
|
+
if RUBY_ENGINE == 'opal'
|
186
|
+
node.html(content)
|
187
|
+
else
|
188
|
+
if content.is_a? Dom::Instance
|
189
|
+
content = content.children
|
190
|
+
else
|
191
|
+
content = Oga.parse_html(content).children
|
192
|
+
end
|
193
|
+
|
194
|
+
if node.is_a?(Oga::XML::NodeSet)
|
195
|
+
node.each { |n| n.children = content }
|
196
|
+
else
|
197
|
+
node.children = content
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
self
|
202
|
+
end
|
203
|
+
|
204
|
+
def find(selector)
|
205
|
+
new_node = if RUBY_ENGINE == 'opal'
|
206
|
+
node.find(selector)
|
207
|
+
else
|
208
|
+
if node.is_a? Oga::XML::NodeSet
|
209
|
+
node.first.css(selector)
|
210
|
+
else
|
211
|
+
node.css(selector)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
Instance.new(new_node, file_name)
|
216
|
+
end
|
217
|
+
|
218
|
+
def each
|
219
|
+
node.each { |n| yield Instance.new(n, file_name) }
|
220
|
+
end
|
221
|
+
|
222
|
+
def node
|
223
|
+
if self.is_a?(Dom::Instance)
|
224
|
+
self.dom
|
225
|
+
else
|
226
|
+
dom
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# This allows you to use all the oga or opal jquery methods if a
|
231
|
+
# global one isn't set
|
232
|
+
def method_missing(method, *args, &block)
|
233
|
+
if dom.respond_to?(method, true)
|
234
|
+
dom.send(method, *args, &block)
|
235
|
+
else
|
236
|
+
super
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
register_plugin(:dom, Dom)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
Dom = Opal::Connect::ConnectPlugins::Dom
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Opal
|
2
|
+
module Connect
|
3
|
+
module ConnectPlugins
|
4
|
+
module Events
|
5
|
+
module ClassMethods
|
6
|
+
if RUBY_ENGINE == 'opal'
|
7
|
+
def connect_events
|
8
|
+
$connect_events[self] ||= []
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def connect_el el = false
|
13
|
+
@connect_el = el if el
|
14
|
+
@connect_el
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect_on(name, selector = nil, method = nil, &handler)
|
18
|
+
if RUBY_ENGINE == 'opal'
|
19
|
+
handler = proc { |evt| __send__(method, evt) } if method
|
20
|
+
event = [name, selector, handler]
|
21
|
+
connect_events << event unless connect_events.include? event
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if RUBY_ENGINE == 'opal'
|
27
|
+
module ConnectClassMethods
|
28
|
+
$connect_events = ConnectCache.new unless $connect_events
|
29
|
+
|
30
|
+
def events_teardown
|
31
|
+
if $connect_events
|
32
|
+
$connect_events.each do |klass, events|
|
33
|
+
el = Dom[klass.connect_el || 'body']
|
34
|
+
|
35
|
+
events.each do |event|
|
36
|
+
name, selector, wrapper = event
|
37
|
+
el.off(name, selector, &wrapper)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
$connect_events = ConnectCache.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def events_start
|
46
|
+
$connect_events.each do |klass, events|
|
47
|
+
el = Dom[klass.connect_el || 'body']
|
48
|
+
|
49
|
+
events.map! do |event|
|
50
|
+
name, selector, handler = event
|
51
|
+
wrapper = proc do |e|
|
52
|
+
# gives you access to this, like jquery
|
53
|
+
@this = Dom[e.target]
|
54
|
+
instance_exec(e, &handler)
|
55
|
+
end
|
56
|
+
|
57
|
+
el.on(name, selector, &wrapper)
|
58
|
+
[name, selector, wrapper]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
register_plugin(:events, Events)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Opal
|
2
|
+
module Connect
|
3
|
+
module ConnectPlugins
|
4
|
+
module HTML
|
5
|
+
INDENT = ' '
|
6
|
+
|
7
|
+
TAGS = %w{a button abbr acronym address applet area article aside audio b base basefont bdi
|
8
|
+
bdo big blockquote body br canvas caption center cite code col colgroup command
|
9
|
+
datalist dd del details dfn dialog dir div dl dt em embed fieldset figcaption
|
10
|
+
figure font footer form frame frameset h1 head header hgroup hr html i iframe
|
11
|
+
img input ins kbd keygen label legend li link map mark menu meta meter nav noframes
|
12
|
+
noscript object ol optgroup option output p param pre progress q rp rt ruby s samp
|
13
|
+
script section select small source span strike strong style sub summary sup table tbody
|
14
|
+
td textarea tfoot th thead time title tr track tt u ul var video wbr}
|
15
|
+
|
16
|
+
# http://erikonrails.snowedin.net/?p=379
|
17
|
+
class DSL
|
18
|
+
def initialize(tag, *args, &block)
|
19
|
+
@tag = tag
|
20
|
+
@content = args.find { |a| a.instance_of? String }
|
21
|
+
@attributes = args.find { |a| a.instance_of? Hash }
|
22
|
+
@attr_string = []
|
23
|
+
self.instance_eval(&block) if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_html
|
27
|
+
if @tag
|
28
|
+
@attr_string << " #{@attributes.map {|k,v| "#{k}=#{v.to_s.inspect}" }.join(" ")}" if @attributes
|
29
|
+
"<#{@tag}#{@attr_string.join}>#{@content}#{children.map(&:to_html).join}</#{@tag}>"
|
30
|
+
else
|
31
|
+
"#{@content}#{children.map(&:to_html).join}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def children
|
36
|
+
@children ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
# Some of these are Kernel or Object methods or whatever that we need to explicitly override
|
40
|
+
[:p, :select].each do |name|
|
41
|
+
define_method name do |*args, &block|
|
42
|
+
send :method_missing, name, *args, &block
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def method_missing(tag, *args, &block)
|
47
|
+
if !TAGS.include?(tag.to_s) && scope.respond_to?(tag, true)
|
48
|
+
scope.send(tag, *args, &block)
|
49
|
+
else
|
50
|
+
child = DSL.scope!(scope).new(tag.to_s, *args, &block)
|
51
|
+
children << child
|
52
|
+
child
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def scope
|
57
|
+
self.class.scope
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
attr_accessor :scope
|
62
|
+
|
63
|
+
def html &block
|
64
|
+
DSL.scope!(scope).new(nil, nil, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def scope! scope
|
68
|
+
klass = Class.new(self)
|
69
|
+
klass.instance_variable_set(:@scope, scope)
|
70
|
+
klass
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
register_plugin(:html, HTML)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Opal
|
2
|
+
module Connect
|
3
|
+
module ConnectPlugins
|
4
|
+
module Server
|
5
|
+
module ConnectClassMethods
|
6
|
+
def server_methods
|
7
|
+
@server_methods ||= ConnectCache.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def server_methods=(methods)
|
11
|
+
@server_methods = methods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def connect_server_methods
|
17
|
+
Connect.server_methods[self.name] ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def connect_server(m = false, &block)
|
21
|
+
return if RUBY_ENGINE == 'opal'
|
22
|
+
|
23
|
+
m ||= Module.new(&block)
|
24
|
+
|
25
|
+
yield if block_given?
|
26
|
+
|
27
|
+
m.public_instance_methods(false).each do |meth|
|
28
|
+
connect_server_methods << meth unless connect_server_methods.include? meth
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ConnectInstanceMethods
|
34
|
+
def __connect_server__(method, *args)
|
35
|
+
if Connect.server_methods[self.class.name].include? method
|
36
|
+
promise = Promise.new
|
37
|
+
|
38
|
+
payload = {
|
39
|
+
method: method,
|
40
|
+
args: args,
|
41
|
+
klass: self.class.name
|
42
|
+
}
|
43
|
+
|
44
|
+
HTTP.post('/connect', payload: payload) do |response|
|
45
|
+
if response.ok?
|
46
|
+
res = JSON.from_object(`response`)
|
47
|
+
promise.resolve res[:body], response
|
48
|
+
else
|
49
|
+
promise.reject response
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
promise
|
54
|
+
else
|
55
|
+
raise "#{method} is not a server method"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
alias connect_server __connect_server__
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
register_plugin(:server, Server)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/opal/connect.rb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
require "opal/connect/version"
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
if RUBY_ENGINE == 'opal'
|
5
|
+
module Kernel
|
6
|
+
def puts(*args)
|
7
|
+
require 'console'
|
8
|
+
$console.log(*args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Opal corelib is already loaded from CDN
|
14
|
+
module Opal
|
15
|
+
module Connect
|
16
|
+
class << self
|
17
|
+
def __options__
|
18
|
+
@options ||= Connect::ConnectCache.new(hot_reload: false)
|
19
|
+
|
20
|
+
yield(@options) if block_given?
|
21
|
+
|
22
|
+
@options
|
23
|
+
end
|
24
|
+
alias options __options__
|
25
|
+
|
26
|
+
def included(klass)
|
27
|
+
if RUBY_ENGINE != 'opal'
|
28
|
+
file = caller[0][/[^:]*/].sub(Dir.pwd, '')[1..-1]
|
29
|
+
files << file unless files.include?(file)
|
30
|
+
end
|
31
|
+
|
32
|
+
klass.extend ConnectPlugins::Base::ClassMethods
|
33
|
+
klass.plugin :server
|
34
|
+
klass.plugin :html
|
35
|
+
klass.plugin :dom
|
36
|
+
klass.plugin :events
|
37
|
+
end
|
38
|
+
|
39
|
+
def write_plugins_file
|
40
|
+
ConnectPlugins.plugins.each do |name, klass|
|
41
|
+
path = klass.instance_methods(false).map { |m|
|
42
|
+
klass.instance_method(m).source_location.first
|
43
|
+
}.uniq
|
44
|
+
|
45
|
+
binding.pry
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ConnectError < StandardError; end
|
51
|
+
|
52
|
+
class ConnectCache
|
53
|
+
# Create a new thread safe cache.
|
54
|
+
def initialize(options = false)
|
55
|
+
@mutex = Mutex.new if RUBY_ENGINE != 'opal'
|
56
|
+
@hash = options || {}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Make getting value from underlying hash thread safe.
|
60
|
+
def [](key)
|
61
|
+
if RUBY_ENGINE == 'opal'
|
62
|
+
@hash[key]
|
63
|
+
else
|
64
|
+
@mutex.synchronize { @hash[key] }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Make setting value in underlying hash thread safe.
|
69
|
+
def []=(key, value)
|
70
|
+
if RUBY_ENGINE == 'opal'
|
71
|
+
@hash[key] = value
|
72
|
+
else
|
73
|
+
@mutex.synchronize { @hash[key] = value }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_json
|
78
|
+
@mutex.synchronize { @hash.to_json }
|
79
|
+
end
|
80
|
+
|
81
|
+
def each
|
82
|
+
if RUBY_ENGINE == 'opal'
|
83
|
+
@hash.each { |key, value| yield key, value }
|
84
|
+
else
|
85
|
+
@mutex.synchronize do
|
86
|
+
@hash.each { |key, value| yield key, value }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
module ConnectPlugins
|
93
|
+
@plugins = ConnectCache.new
|
94
|
+
|
95
|
+
def self.plugins
|
96
|
+
@plugins
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.load_plugin(name)
|
100
|
+
h = @plugins
|
101
|
+
unless plugin = h[name]
|
102
|
+
unless RUBY_ENGINE == 'opal'
|
103
|
+
require "opal/connect/plugins/#{name}"
|
104
|
+
raise ConnectError, "Plugin #{name} did not register itself correctly in Roda::RodaPlugins" unless plugin = h[name]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
plugin
|
108
|
+
end
|
109
|
+
|
110
|
+
# Register the given plugin with Roda, so that it can be loaded using #plugin
|
111
|
+
# with a symbol. Should be used by plugin files. Example:
|
112
|
+
#
|
113
|
+
# Roda::RodaPlugins.register_plugin(:plugin_name, PluginModule)
|
114
|
+
def self.register_plugin(name, mod)
|
115
|
+
@plugins[name] = mod
|
116
|
+
end
|
117
|
+
|
118
|
+
module Base
|
119
|
+
module InstanceMethods
|
120
|
+
if RUBY_ENGINE != 'opal'
|
121
|
+
def to_js(method, *options)
|
122
|
+
if hl = Connect.options[:hot_reload]
|
123
|
+
hl = {} unless hl.is_a? Hash
|
124
|
+
hl = { host: 'http://localhost', port: 8080 }.merge hl
|
125
|
+
|
126
|
+
Connect.write_entry_file(self.class, method, *options)
|
127
|
+
|
128
|
+
"#{__send__(method, *options)}<script src='#{hl[:host]}:#{hl[:port]}/main.js'></script>"
|
129
|
+
else
|
130
|
+
js = Connect.build Connect.javascript(self.class, method, *options)
|
131
|
+
|
132
|
+
"#{send(method, *options)}<script>#{js}</script>"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
module ClassMethods
|
139
|
+
def setup(&block)
|
140
|
+
yield
|
141
|
+
end
|
142
|
+
|
143
|
+
# Load a new plugin into the current class. A plugin can be a module
|
144
|
+
# which is used directly, or a symbol represented a registered plugin
|
145
|
+
# which will be required and then used. Returns nil.
|
146
|
+
#
|
147
|
+
# Connect.plugin PluginModule
|
148
|
+
# Connect.plugin :csrf
|
149
|
+
def plugin(plugin, *args, &block)
|
150
|
+
raise ConnectError, "Cannot add a plugin to a frozen Connect class" if RUBY_ENGINE != 'opal' && frozen?
|
151
|
+
plugin = ConnectPlugins.load_plugin(plugin) if plugin.is_a?(Symbol)
|
152
|
+
plugin.load_dependencies(self, *args, &block) if plugin.respond_to?(:load_dependencies)
|
153
|
+
include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods)
|
154
|
+
extend(plugin::ClassMethods) if defined?(plugin::ClassMethods)
|
155
|
+
self.extend(plugin::ConnectClassMethods) if defined?(plugin::ConnectClassMethods)
|
156
|
+
self.include(plugin::ConnectInstanceMethods) if defined?(plugin::ConnectInstanceMethods)
|
157
|
+
plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
|
161
|
+
if RUBY_ENGINE != 'opal'
|
162
|
+
def files
|
163
|
+
@files ||= []
|
164
|
+
end
|
165
|
+
|
166
|
+
def build(code)
|
167
|
+
builder = Opal::Builder.new
|
168
|
+
|
169
|
+
builder.build_str(code, '(inline)' \
|
170
|
+
, dynamic_require_severity: :ignore).to_s
|
171
|
+
end
|
172
|
+
|
173
|
+
def javascript(klass, method, *options)
|
174
|
+
if hot_reload = Connect.options[:hot_reload]
|
175
|
+
required_files = Connect.files.map do |file|
|
176
|
+
"`require('#{file}')`"
|
177
|
+
end.join(';')
|
178
|
+
end
|
179
|
+
|
180
|
+
%{
|
181
|
+
#{!hot_reload ? '' : 'Opal::Connect.events_teardown'}
|
182
|
+
#{!hot_reload ? '' : required_files}
|
183
|
+
|
184
|
+
Opal::Connect.server_methods = JSON.parse(
|
185
|
+
Base64.decode64('#{Base64.encode64 Connect.server_methods.to_json}')
|
186
|
+
)
|
187
|
+
|
188
|
+
Document.ready? do
|
189
|
+
klass = #{klass.name}.new
|
190
|
+
|
191
|
+
if klass.respond_to?(:#{method})
|
192
|
+
klass.__send__(:#{method}, *JSON.parse(Base64.decode64('#{Base64.encode64 options.to_json}')))
|
193
|
+
end
|
194
|
+
|
195
|
+
Opal::Connect.events_start
|
196
|
+
end
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
def write_entry_file(klass, method, *options)
|
201
|
+
Opal.use_gem 'opal-jquery'
|
202
|
+
|
203
|
+
code = %{
|
204
|
+
`if (module.hot) {`
|
205
|
+
`module.hot.accept()`
|
206
|
+
#{Connect.javascript(klass, method, *options)}
|
207
|
+
`}`
|
208
|
+
}
|
209
|
+
|
210
|
+
File.write("#{Dir.pwd}/.connect/entry.js", build(code))
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
if RUBY_ENGINE == 'opal'
|
218
|
+
require ".connect/plugins"
|
219
|
+
end
|
220
|
+
|
221
|
+
extend ConnectPlugins::Base::ClassMethods
|
222
|
+
plugin ConnectPlugins::Base
|
223
|
+
plugin :server
|
224
|
+
plugin :events
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'opal/connect/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "opal-connect"
|
8
|
+
spec.version = Opal::Connect::VERSION
|
9
|
+
spec.authors = ["cj"]
|
10
|
+
spec.email = ["cjlazell@gmail.com"]
|
11
|
+
spec.summary = %q{Connects Opal to Ruby.}
|
12
|
+
spec.description = %q{Connect Opal to Ruby.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "opal", ">= 0.9.2"
|
22
|
+
spec.add_dependency "opal-jquery", ">= 0.4.1"
|
23
|
+
spec.add_dependency "oga", ">= 2.2"
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: opal-connect
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- cj
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-18 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.2
|
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.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: opal-jquery
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.4.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.4.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: oga
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
description: Connect Opal to Ruby.
|
84
|
+
email:
|
85
|
+
- cjlazell@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- Gemfile
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- lib/opal/connect.rb
|
96
|
+
- lib/opal/connect/plugins/dom.rb
|
97
|
+
- lib/opal/connect/plugins/events.rb
|
98
|
+
- lib/opal/connect/plugins/html.rb
|
99
|
+
- lib/opal/connect/plugins/server.rb
|
100
|
+
- lib/opal/connect/version.rb
|
101
|
+
- opal-connect.gemspec
|
102
|
+
homepage: ''
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.2.2
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: Connects Opal to Ruby.
|
126
|
+
test_files: []
|