clearwater 1.0.0.rc4 → 1.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 98660a461c2da1071bbc7a8f0cfd3db413ff2224
4
- data.tar.gz: 53727157dec9793aafce8f6ec538099fc11c57d3
3
+ metadata.gz: 36c47513423cf3f1e0a6b3705f3e07d59bbbed31
4
+ data.tar.gz: 9040af257c8ce168472c92d4ff4dff608bf1da67
5
5
  SHA512:
6
- metadata.gz: 1016b8ce23f014f05d8aa14df5e7721c59b1950896b45e7f60e498ec4722ab24dde35c58fbc24c2ecac9171c216bb9966fc5a0ace8e510f77de571a8dca1f521
7
- data.tar.gz: 38d578979f4aaed936e475d80c577e77a64dc76ce595a4c645eec7bf55d5688b15af2d5d05d604bc4c7d8986d4efd16073975253d0b7fbdcb68b159e45c743e6
6
+ metadata.gz: 553ce8493b33aa68602b4d4b792083e1ab7c5bb957183e3a5474f9103ac97a3ffa5bfad03f5a1eb6e3075db9e0d04fa8309c8ebe110edc7006a36dacac587faa
7
+ data.tar.gz: 4341d6edfe0eef28e0a20b1e405a2ad6d93a675bf96471f69268aab0e9088670788da5a1f915128f91a9882fb2c8ee3cda78d267573dae22f05c8a412d6431a0
@@ -27,12 +27,8 @@ module Clearwater
27
27
  end
28
28
 
29
29
  def to_s
30
- html = render.to_s
31
- if html.respond_to? :html_safe
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
@@ -1,3 +1,3 @@
1
1
  module Clearwater
2
- VERSION = "1.0.0.rc4"
2
+ VERSION = "1.0.0.rc5"
3
3
  end
@@ -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('popstate') { render_current_url }
58
+ @popstate_listener = @window.on(:popstate) { render_current_url }
55
59
  @watching_url = true
56
60
  end
57
61
  end
@@ -6,6 +6,10 @@ module Clearwater
6
6
  @apps = Set.new
7
7
  end
8
8
 
9
+ def each &block
10
+ @apps.each &block
11
+ end
12
+
9
13
  def << app
10
14
  @apps << app
11
15
  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
- #{@delegate.update(`previous.delegate`, wrap(`node`))};
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)};
@@ -13,7 +13,6 @@ module Clearwater
13
13
  end
14
14
 
15
15
  def key
16
- `undefined`
17
16
  end
18
17
  end
19
18
  end
@@ -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
- VirtualDOM.node(
91
+ `vdom`.node(
87
92
  tag_name,
88
- Component.sanitize_attributes(attributes),
89
- Component.sanitize_content(content)
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`), `previous`)};
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
- #{HashUtils.camelized_native(attributes)},
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 === Opal.nil || content === undefined) return null;
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('div'))
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[#{StringUtils.camelize(`key`)}] = v.$$is_hash
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 `#@target.type === 'Thunk' && typeof #@target.render === 'function'`
14
- warn "Route '#{key}' points to a cached component. Cached " +
15
- "components must not be persistent components, such as " +
16
- "application roots or routing targets."
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
 
@@ -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 set_outlets targets=targets_for_path(current_path)
103
- @old_targets = @targets || []
104
- @targets = targets
105
- navigating_from = @old_targets - @targets
106
- navigating_to = @targets - @old_targets
107
-
108
- navigating_from.each do |target|
109
- if target.respond_to? :on_route_from
110
- target.on_route_from
111
- end
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.rc4
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: 2016-09-17 00:00:00.000000000 Z
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.4.8
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