clearwater 1.0.0.rc4 → 1.0.0.rc5
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 +4 -4
- data/lib/clearwater/component.rb +2 -6
- data/lib/clearwater/version.rb +1 -1
- data/opal/clearwater/application.rb +5 -1
- data/opal/clearwater/application_registry.rb +4 -0
- data/opal/clearwater/black_box_node.rb +14 -1
- data/opal/clearwater/cached_render/wrapper.rb +2 -1
- data/opal/clearwater/cached_render.rb +0 -1
- data/opal/clearwater/component.rb +11 -6
- data/opal/clearwater/dom_reference.rb +4 -2
- data/opal/clearwater/memoized_component.rb +79 -0
- data/opal/clearwater/virtual_dom.rb +10 -5
- data/shared/clearwater/router/route.rb +6 -4
- data/shared/clearwater/router.rb +39 -14
- data/spec/clearwater/component_spec.rb +11 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36c47513423cf3f1e0a6b3705f3e07d59bbbed31
|
4
|
+
data.tar.gz: 9040af257c8ce168472c92d4ff4dff608bf1da67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 553ce8493b33aa68602b4d4b792083e1ab7c5bb957183e3a5474f9103ac97a3ffa5bfad03f5a1eb6e3075db9e0d04fa8309c8ebe110edc7006a36dacac587faa
|
7
|
+
data.tar.gz: 4341d6edfe0eef28e0a20b1e405a2ad6d93a675bf96471f69268aab0e9088670788da5a1f915128f91a9882fb2c8ee3cda78d267573dae22f05c8a412d6431a0
|
data/lib/clearwater/component.rb
CHANGED
@@ -27,12 +27,8 @@ module Clearwater
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def to_s
|
30
|
-
html = render.
|
31
|
-
|
32
|
-
html = html.html_safe
|
33
|
-
end
|
34
|
-
|
35
|
-
html
|
30
|
+
html = Array(render).join
|
31
|
+
html.respond_to?(:html_safe) ? html.html_safe : html
|
36
32
|
end
|
37
33
|
|
38
34
|
def params
|
data/lib/clearwater/version.rb
CHANGED
@@ -47,11 +47,15 @@ module Clearwater
|
|
47
47
|
|
48
48
|
def unmount
|
49
49
|
AppRegistry.delete self
|
50
|
+
@window.off :popstate, &@popstate_listener
|
51
|
+
|
52
|
+
@popstate_listener = nil # Allow the proc to get GCed
|
53
|
+
@watching_url = false
|
50
54
|
end
|
51
55
|
|
52
56
|
def watch_url
|
53
57
|
unless @watching_url
|
54
|
-
@window.on(
|
58
|
+
@popstate_listener = @window.on(:popstate) { render_current_url }
|
55
59
|
@watching_url = true
|
56
60
|
end
|
57
61
|
end
|
@@ -20,9 +20,15 @@ module Clearwater
|
|
20
20
|
Renderable.new(self)
|
21
21
|
end
|
22
22
|
|
23
|
+
def key
|
24
|
+
end
|
25
|
+
|
23
26
|
class Renderable
|
24
27
|
def initialize delegate
|
25
28
|
@delegate = delegate
|
29
|
+
if delegate.key
|
30
|
+
@key = delegate.key
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
def wrap node
|
@@ -52,7 +58,14 @@ module Clearwater
|
|
52
58
|
// node: a Bowser-wrapped version of the DOM node
|
53
59
|
Opal.defn(self, 'update', function(previous, node) {
|
54
60
|
var self = this;
|
55
|
-
|
61
|
+
if(self.delegate.$$class === previous.delegate.$$class) {
|
62
|
+
#{@delegate.update(`previous.delegate`, wrap(`node`))};
|
63
|
+
} else {
|
64
|
+
previous.destroy(#{wrap(`node`)});
|
65
|
+
var new_node = #{create_element};
|
66
|
+
#{@delegate.mount(`new_node`)};
|
67
|
+
return new_node.native;
|
68
|
+
}
|
56
69
|
});
|
57
70
|
|
58
71
|
// virtual-dom destroy hook
|
@@ -5,7 +5,7 @@ module Clearwater
|
|
5
5
|
|
6
6
|
def initialize content
|
7
7
|
@content = content
|
8
|
-
@key = content.key
|
8
|
+
@key = content.key if content.key
|
9
9
|
end
|
10
10
|
|
11
11
|
# Hook into vdom diff/patch
|
@@ -15,6 +15,7 @@ module Clearwater
|
|
15
15
|
var self = this;
|
16
16
|
|
17
17
|
if(prev && prev.vnode && #{!@content.should_render?(`prev.content`)}) {
|
18
|
+
#{ @content = `prev.content` }
|
18
19
|
return prev.vnode;
|
19
20
|
} else {
|
20
21
|
var content = #{Component.sanitize_content(@content.render)};
|
@@ -43,10 +43,9 @@ module Clearwater
|
|
43
43
|
|
44
44
|
def self.sanitize_content content
|
45
45
|
%x{
|
46
|
+
// Is this a Ruby object?
|
46
47
|
if(content && content.$$class) {
|
47
|
-
if(content.$$is_array) {
|
48
|
-
return #{content.map { |c| sanitize_content(c) }};
|
49
|
-
} else {
|
48
|
+
if(!content.$$is_array) {
|
50
49
|
var render = content.$render;
|
51
50
|
|
52
51
|
if(content.$$cached_render) {
|
@@ -56,7 +55,11 @@ module Clearwater
|
|
56
55
|
} else {
|
57
56
|
return content;
|
58
57
|
}
|
58
|
+
} else {
|
59
|
+
return #{content.map { |c| sanitize_content(c) }};
|
59
60
|
}
|
61
|
+
|
62
|
+
// If it's not a Ruby object, it's probably a virtual-dom node.
|
60
63
|
} else {
|
61
64
|
return content;
|
62
65
|
}
|
@@ -82,11 +85,13 @@ module Clearwater
|
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
88
|
+
`var vdom = #{VirtualDOM}`
|
89
|
+
`var component = self`
|
85
90
|
def tag tag_name, attributes=nil, content=nil
|
86
|
-
|
91
|
+
`vdom`.node(
|
87
92
|
tag_name,
|
88
|
-
|
89
|
-
|
93
|
+
`component`.sanitize_attributes(attributes),
|
94
|
+
`component`.sanitize_content(content)
|
90
95
|
)
|
91
96
|
end
|
92
97
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Clearwater
|
2
2
|
class DOMReference
|
3
|
+
attr_reader :node
|
4
|
+
|
3
5
|
def mount node, previous
|
4
6
|
@node = node
|
5
7
|
end
|
@@ -34,12 +36,12 @@ module Clearwater
|
|
34
36
|
%x{
|
35
37
|
Opal.defn(self, 'hook', function(node, name, previous) {
|
36
38
|
var self = this;
|
37
|
-
#{mount(wrap(`node`), `previous`)};
|
39
|
+
#{mount(wrap(`node`), `previous == null ? nil : previous`)};
|
38
40
|
});
|
39
41
|
|
40
42
|
Opal.defn(self, 'unhook', function(node, name, previous) {
|
41
43
|
var self = this;
|
42
|
-
#{unmount(wrap(`node`),
|
44
|
+
#{unmount(wrap(`node`), `previous == null ? nil : previous`)};
|
43
45
|
});
|
44
46
|
}
|
45
47
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'clearwater/component'
|
2
|
+
require 'clearwater/black_box_node'
|
3
|
+
|
4
|
+
module Clearwater
|
5
|
+
class MemoizedComponent
|
6
|
+
include Clearwater::Component
|
7
|
+
|
8
|
+
def self.memoize *args, &block
|
9
|
+
Placeholder.new(self, args, block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.[] key
|
13
|
+
memoize[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def should_update?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def update
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy
|
24
|
+
end
|
25
|
+
|
26
|
+
class Placeholder
|
27
|
+
include Clearwater::BlackBoxNode
|
28
|
+
|
29
|
+
attr_reader :klass, :key, :vdom
|
30
|
+
|
31
|
+
def initialize klass, args, block
|
32
|
+
@klass = klass
|
33
|
+
@args = args
|
34
|
+
@block = block
|
35
|
+
end
|
36
|
+
|
37
|
+
def memoize *args, &block
|
38
|
+
initialize @klass, args, block
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def [] key
|
43
|
+
@key = key.to_s
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def component
|
48
|
+
@component ||= @klass.new(*@args, &@block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def node
|
52
|
+
@node ||= Clearwater::Component.sanitize_content(component)
|
53
|
+
end
|
54
|
+
|
55
|
+
def mount element
|
56
|
+
@vdom = VirtualDOM::Document.new(element)
|
57
|
+
|
58
|
+
# TODO: add a public interface to generate a pre-initialized VDOM::Doc
|
59
|
+
`#@vdom.tree = #{element.to_n}`
|
60
|
+
`#@vdom.node = #{node}`
|
61
|
+
`#@vdom.rendered = true`
|
62
|
+
end
|
63
|
+
|
64
|
+
def update previous
|
65
|
+
@vdom = previous.vdom
|
66
|
+
@component = previous.component
|
67
|
+
|
68
|
+
if component.should_update?(*@args, &@block)
|
69
|
+
component.update(*@args, &@block)
|
70
|
+
@vdom.render component.render
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def unmount
|
75
|
+
component.destroy
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -2,11 +2,13 @@ require 'clearwater/virtual_dom/js/virtual_dom.js'
|
|
2
2
|
|
3
3
|
module Clearwater
|
4
4
|
module VirtualDOM
|
5
|
+
`var hash_utils;`
|
6
|
+
|
5
7
|
def self.node(tag_name, attributes=nil, content=nil)
|
6
8
|
%x{
|
7
9
|
return virtualDom.h(
|
8
10
|
tag_name,
|
9
|
-
#{
|
11
|
+
#{`hash_utils`.camelized_native(attributes)},
|
10
12
|
#{sanitize_content(content)}
|
11
13
|
);
|
12
14
|
}
|
@@ -36,15 +38,15 @@ module Clearwater
|
|
36
38
|
|
37
39
|
def self.sanitize_content content
|
38
40
|
%x{
|
39
|
-
if(content ===
|
41
|
+
if(content === #{nil} || content == null) return null;
|
40
42
|
if(content.$$is_array)
|
41
43
|
return #{content.map!{ |c| sanitize_content c }};
|
42
|
-
return content;
|
44
|
+
return content.valueOf();
|
43
45
|
}
|
44
46
|
end
|
45
47
|
|
46
48
|
class Document
|
47
|
-
def initialize(root=Bowser.document.create_element(
|
49
|
+
def initialize(root=Bowser.document.create_element(:div))
|
48
50
|
@root = root
|
49
51
|
end
|
50
52
|
|
@@ -91,6 +93,8 @@ module Clearwater
|
|
91
93
|
end
|
92
94
|
|
93
95
|
module HashUtils
|
96
|
+
`var string_utils = #{StringUtils}`
|
97
|
+
|
94
98
|
def self.camelized_native hash
|
95
99
|
return hash.to_n unless `!!hash.$$is_hash`
|
96
100
|
|
@@ -99,7 +103,7 @@ module Clearwater
|
|
99
103
|
for(var index = 0; index < keys.length; index++) {
|
100
104
|
key = keys[index];
|
101
105
|
v = #{hash[`key`]};
|
102
|
-
js_obj[#{
|
106
|
+
js_obj[#{`string_utils`.camelize(`key`)}] = v.$$is_hash
|
103
107
|
? self.$camelized_native(v)
|
104
108
|
: (
|
105
109
|
(v && v.$$class) // If this is a Ruby object, nativize it
|
@@ -111,5 +115,6 @@ module Clearwater
|
|
111
115
|
}
|
112
116
|
end
|
113
117
|
end
|
118
|
+
`hash_utils = #{HashUtils}`
|
114
119
|
end
|
115
120
|
end
|
@@ -10,10 +10,12 @@ module Clearwater
|
|
10
10
|
@target = options.fetch(:target)
|
11
11
|
@parent = options.fetch(:parent)
|
12
12
|
|
13
|
-
if
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
if RUBY_ENGINE == 'opal'
|
14
|
+
if `#@target.type === 'Thunk' && typeof #@target.render === 'function'`
|
15
|
+
warn "Route '#{key}' points to a cached component. Cached " +
|
16
|
+
"components must not be persistent components, such as " +
|
17
|
+
"application roots or routing targets."
|
18
|
+
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
data/shared/clearwater/router.rb
CHANGED
@@ -81,16 +81,26 @@ module Clearwater
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def navigate_to path
|
84
|
+
self.class.previous_path = current_path
|
84
85
|
history.push path
|
85
86
|
set_outlets
|
86
87
|
render_application
|
87
88
|
end
|
88
89
|
|
89
90
|
def self.navigate_to path
|
91
|
+
self.previous_path = current_path
|
90
92
|
Bowser.window.history.push path
|
91
93
|
render_all_apps
|
92
94
|
end
|
93
95
|
|
96
|
+
def self.previous_path=(path)
|
97
|
+
@previous_path = path
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.previous_path
|
101
|
+
@previous_path.to_s
|
102
|
+
end
|
103
|
+
|
94
104
|
def navigate_to_remote path
|
95
105
|
location.href = path
|
96
106
|
end
|
@@ -99,23 +109,38 @@ module Clearwater
|
|
99
109
|
history.back
|
100
110
|
end
|
101
111
|
|
102
|
-
def
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
+
def trigger_routing_callbacks(path:, previous_path:)
|
113
|
+
# If the paths are the same, there are no callbacks to trigger
|
114
|
+
return if path == previous_path
|
115
|
+
|
116
|
+
targets = targets_for_path(path)
|
117
|
+
old_targets = targets_for_path(previous_path)
|
118
|
+
routes = routes_for_path(path)
|
119
|
+
old_params = params(previous_path)
|
120
|
+
new_params = params(path)
|
121
|
+
|
122
|
+
changed_dynamic_segments = new_params
|
123
|
+
.select { |k, v| old_params[k] != v }
|
124
|
+
.map { |key, _| ":#{key}" }
|
125
|
+
|
126
|
+
changed_dynamic_targets = routes.drop_while { |route|
|
127
|
+
!changed_dynamic_segments.include?(route.key)
|
128
|
+
}.map(&:target)
|
129
|
+
|
130
|
+
navigating_from = old_targets - targets
|
131
|
+
navigating_to = targets - old_targets
|
132
|
+
|
133
|
+
(navigating_from | changed_dynamic_targets).each do |target|
|
134
|
+
target.on_route_from if target.respond_to? :on_route_from
|
112
135
|
end
|
113
136
|
|
114
|
-
navigating_to.each do |target|
|
115
|
-
if target.respond_to? :on_route_to
|
116
|
-
target.on_route_to
|
117
|
-
end
|
137
|
+
(navigating_to | changed_dynamic_targets).each do |target|
|
138
|
+
target.on_route_to if target.respond_to? :on_route_to
|
118
139
|
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_outlets targets=targets_for_path(current_path)
|
143
|
+
trigger_routing_callbacks(path: current_path, previous_path: self.class.previous_path)
|
119
144
|
|
120
145
|
if targets.any?
|
121
146
|
(targets.count).times do |index|
|
@@ -13,6 +13,17 @@ module Clearwater
|
|
13
13
|
expect(html).to eq('<div id="foo" class="bar"><p>baz</p></div>')
|
14
14
|
end
|
15
15
|
|
16
|
+
it 'generates html for components rendering an array' do
|
17
|
+
component_class = Class.new do
|
18
|
+
include Clearwater::Component
|
19
|
+
|
20
|
+
def render
|
21
|
+
[ div({}, "1"), div({}, "2") ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
expect(component_class.new.to_s).to eq('<div>1</div><div>2</div>')
|
25
|
+
end
|
26
|
+
|
16
27
|
it 'converts styles into strings' do
|
17
28
|
html = component.div({
|
18
29
|
style: {
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clearwater
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Gaskins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opal
|
@@ -132,6 +132,7 @@ files:
|
|
132
132
|
- opal/clearwater/component.rb
|
133
133
|
- opal/clearwater/dom_reference.rb
|
134
134
|
- opal/clearwater/link.rb
|
135
|
+
- opal/clearwater/memoized_component.rb
|
135
136
|
- opal/clearwater/svg_component.rb
|
136
137
|
- opal/clearwater/virtual_dom.rb
|
137
138
|
- opal/clearwater/virtual_dom/js/virtual_dom.js
|
@@ -164,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
165
|
version: 1.3.1
|
165
166
|
requirements: []
|
166
167
|
rubyforge_project:
|
167
|
-
rubygems_version: 2.
|
168
|
+
rubygems_version: 2.6.12
|
168
169
|
signing_key:
|
169
170
|
specification_version: 4
|
170
171
|
summary: Front-end Ruby web framework for fast, reasonable, and composable applications
|