reactive-ruby 0.7.31 → 0.7.32

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 03ea6c95ebe59edebe184cba6f058d429f6e9f03
4
- data.tar.gz: f68ee94ee71d55e877a19f253f012da2cba98fa1
3
+ metadata.gz: 212dc6ace47eb491a9464258c6df6e26e4c35579
4
+ data.tar.gz: faa7ee755e54749e9acce87bc8c08ff1c890bc88
5
5
  SHA512:
6
- metadata.gz: 51dffbcc520e2969b83f04c4820af42c100f7d3b6f340a5468e0dc847330a4f5fe4fdfa54722a0bb7a266de19bc827e6d3ed23cc66b27f1a2f6910ce32be66dd
7
- data.tar.gz: 29ee24cee796f6ada843fbac91dd089a5d421d44be2318ba1dd5b1d600fa8c3b4e48ef4f8bd5a4d21cf3ee616c584cc77d87659f17a741083f7bc66705e14a50
6
+ metadata.gz: 4b721773a255f59e5a852788fd75a4fd2ab7340b9c8f8e5d0846ca009adf0d37150fb83010ad80810e203352467744f81cc10008c3daeff0b97898f1cacf8971
7
+ data.tar.gz: 4b53af87056f9a2dbe19eddcd1217b7ab95daa705edb90011501702e956a97200dd28fff5e4396b30735c0344e408ee3b0cf8b7693294f6c01159307afbe5b15
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reactive-ruby (0.7.30)
4
+ reactive-ruby (0.7.31)
5
5
  opal
6
6
  opal-activesupport (>= 0.2.0)
7
7
  opal-browser
@@ -8,33 +8,33 @@ module React
8
8
 
9
9
  export_component
10
10
 
11
- required_param :component_name
12
- required_param :controller
13
- required_param :render_params
11
+ param :component_name
12
+ param :controller
13
+ param :render_params
14
14
 
15
15
  backtrace :on
16
16
 
17
17
  def render
18
18
  paths_searched = []
19
- if component_name.start_with? "::"
20
- paths_searched << component_name.gsub(/^\:\:/,"")
21
- component = component_name.gsub(/^\:\:/,"").split("::").inject(Module) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
22
- return present component, render_params if component && component.method_defined?(:render)
19
+ if params.component_name.start_with? "::"
20
+ paths_searched << params.component_name.gsub(/^\:\:/,"")
21
+ component = params.component_name.gsub(/^\:\:/,"").split("::").inject(Module) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
22
+ return present component, params.render_params if component && component.method_defined?(:render)
23
23
  else
24
24
  self.class.search_path.each do |path|
25
- # try each path + controller + component_name
26
- paths_searched << "#{path.name + '::' unless path == Module}#{controller}::#{component_name}"
27
- component = "#{controller}::#{component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
28
- return present component, render_params if component && component.method_defined?(:render)
25
+ # try each path + params.controller + params.component_name
26
+ paths_searched << "#{path.name + '::' unless path == Module}#{params.controller}::#{params.component_name}"
27
+ component = "#{params.controller}::#{params.component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
28
+ return present component, params.render_params if component && component.method_defined?(:render)
29
29
  end
30
30
  self.class.search_path.each do |path|
31
- # then try each path + component_name
32
- paths_searched << "#{path.name + '::' unless path == Module}#{component_name}"
33
- component = "#{component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
34
- return present component, render_params if component && component.method_defined?(:render)
31
+ # then try each path + params.component_name
32
+ paths_searched << "#{path.name + '::' unless path == Module}#{params.component_name}"
33
+ component = "#{params.component_name}".split("::").inject(path) { |scope, next_const| scope.const_get(next_const, false) } rescue nil
34
+ return present component, params.render_params if component && component.method_defined?(:render)
35
35
  end
36
36
  end
37
- raise "Could not find component class '#{component_name}' for controller '#{controller}' in any component directory. Tried [#{paths_searched.join(", ")}]"
37
+ raise "Could not find component class '#{params.component_name}' for params.controller '#{params.controller}' in any component directory. Tried [#{paths_searched.join(", ")}]"
38
38
  end
39
39
  end
40
40
  end
@@ -7,9 +7,7 @@ module React
7
7
  end
8
8
 
9
9
  def run_callback(name, *args)
10
- attribute_name = "_#{name}_callbacks"
11
- callbacks = self.class.send(attribute_name)
12
- callbacks.each do |callback|
10
+ self.class.callbacks_for(name).each do |callback|
13
11
  if callback.is_a?(Proc)
14
12
  instance_exec(*args, &callback)
15
13
  else
@@ -30,6 +28,15 @@ module React
30
28
  self.send("#{attribute_name}=", callbacks)
31
29
  end
32
30
  end
31
+
32
+ def callbacks_for(callback_name)
33
+ attribute_name = "_#{callback_name}_callbacks"
34
+ if superclass.respond_to? :callbacks_for
35
+ superclass.callbacks_for(callback_name)
36
+ else
37
+ []
38
+ end + self.send(attribute_name)
39
+ end
33
40
  end
34
41
  end
35
42
  end
@@ -14,6 +14,22 @@ module React
14
14
  def self.included(base)
15
15
  base.include(API)
16
16
  base.include(React::Callbacks)
17
+ base.instance_eval do
18
+
19
+ class PropsWrapper
20
+
21
+ def initialize(props_hash)
22
+ @props_hash = props_hash
23
+ @processed_params = {}
24
+ end
25
+
26
+ def [](prop)
27
+ return unless @props_hash
28
+ @props_hash[prop]
29
+ end
30
+
31
+ end
32
+ end
17
33
  base.class_eval do
18
34
  class_attribute :initial_state
19
35
  define_callback :before_mount
@@ -23,6 +39,11 @@ module React
23
39
  define_callback :after_update
24
40
  define_callback :before_unmount
25
41
 
42
+ def deprecated_params_method(name, *args, &block)
43
+ self.class.deprecation_warning "Direct access to param `#{name}`. Use `params.#{name}` instead."
44
+ params.send(name, *args, &block)
45
+ end
46
+
26
47
  def render
27
48
  raise "no render defined"
28
49
  end unless method_defined?(:render)
@@ -97,6 +118,11 @@ module React
97
118
  end
98
119
 
99
120
  def params
121
+ @props_wrapper
122
+ #Hash.new(`#{@native}.props`)
123
+ end
124
+
125
+ def props
100
126
  Hash.new(`#{@native}.props`)
101
127
  end
102
128
 
@@ -105,8 +131,8 @@ module React
105
131
  end
106
132
 
107
133
  def state
108
- raise "No native ReactComponent associated" unless @native
109
- Hash.new(`#{@native}.state`)
134
+ #raise "No native ReactComponent associated" unless @native
135
+ @state_wrapper ||= StateWrapper.new(@native, self)
110
136
  end
111
137
 
112
138
  def update_react_js_state(object, name, value)
@@ -123,7 +149,7 @@ module React
123
149
 
124
150
  def component_will_mount
125
151
  IsomorphicHelpers.load_context(true) if IsomorphicHelpers.on_opal_client?
126
- @processed_params = {}
152
+ @props_wrapper = self.class.const_get("PropsWrapper").new(Hash.new(`#{@native}.props`))
127
153
  set_state! initial_state if initial_state
128
154
  React::State.initialize_states(self, initial_state)
129
155
  React::State.set_state_context_to(self) { self.run_callback(:before_mount) }
@@ -144,14 +170,13 @@ module React
144
170
  # need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
145
171
  # for now we are just using it to clear processed_params
146
172
  React::State.set_state_context_to(self) { self.run_callback(:before_receive_props, Hash.new(next_props)) }
147
- @processed_params = {}
148
173
  rescue Exception => e
149
174
  self.class.process_exception(e, self)
150
175
  end
151
176
 
152
177
  def props_changed?(next_props)
153
- return true unless params.keys.sort == next_props.keys.sort
154
- params.detect { |k, v| `#{next_props[k]} != #{params[k]}`}
178
+ return true unless props.keys.sort == next_props.keys.sort
179
+ props.detect { |k, v| `#{next_props[k]} != #{params[k]}`}
155
180
  end
156
181
 
157
182
  def should_component_update?(next_props, next_state)
@@ -177,6 +202,7 @@ module React
177
202
 
178
203
  def component_will_update(next_props, next_state)
179
204
  React::State.set_state_context_to(self) { self.run_callback(:before_update, Hash.new(next_props), Hash.new(next_state)) }
205
+ @props_wrapper = self.class.const_get("PropsWrapper").new(Hash.new(next_props))
180
206
  rescue Exception => e
181
207
  self.class.process_exception(e, self)
182
208
  end
@@ -220,7 +246,7 @@ module React
220
246
  end
221
247
 
222
248
  def method_missing(n, *args, &block)
223
- return params[n] if params.key? n
249
+ return props[n] if props.key? n # TODO deprecate and remove - done so that params shadow tags, no longer needed
224
250
  name = n
225
251
  if name =~ /_as_node$/
226
252
  node_only = true
@@ -0,0 +1,11 @@
1
+ require 'react/component'
2
+
3
+ module React
4
+ module Component
5
+ class Base
6
+ def self.inherited(child)
7
+ child.send(:include, React::Component)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -18,8 +18,17 @@ module React
18
18
  raise e if reraise
19
19
  end
20
20
 
21
+ def deprecation_warning(message)
22
+ @deprecation_messages ||= []
23
+ message = "Warning: Deprecated feature used in #{self.name}. #{message}"
24
+ unless @deprecation_messages.include? message
25
+ @deprecation_messages << message
26
+ IsomorphicHelpers.log message, :warning
27
+ end
28
+ end
29
+
21
30
  def validator
22
- @validator ||= React::Validator.new
31
+ @validator ||= React::Validator.new(self)
23
32
  end
24
33
 
25
34
  def prop_types
@@ -47,50 +56,73 @@ module React
47
56
  end
48
57
 
49
58
  def define_param_method(name, param_type)
59
+ wrapper = const_get("PropsWrapper")
50
60
  if param_type == React::Observable
51
- (@two_way_params ||= []) << name
52
- define_method("#{name}") do
53
- params[name].instance_variable_get("@value") if params[name]
61
+ #(@two_way_params ||= []) << name
62
+ wrapper.define_method("#{name}") do
63
+ @props_hash[name].instance_variable_get("@value") if @props_hash[name]
54
64
  end
55
- define_method("#{name}!") do |*args|
56
- return unless params[name]
65
+ wrapper.define_method("#{name}!") do |*args|
66
+ return unless @props_hash[name]
57
67
  if args.count > 0
58
- current_value = params[name].instance_variable_get("@value")
59
- params[name].call args[0]
68
+ current_value = @props_hash[name].instance_variable_get("@value")
69
+ @props_hash[name].call args[0]
60
70
  current_value
61
71
  else
62
- current_value = params[name].instance_variable_get("@value")
63
- params[name].call current_value unless @dont_update_state rescue nil # rescue in case we in middle of render
64
- params[name]
72
+ current_value = @props_hash[name].instance_variable_get("@value")
73
+ @props_hash[name].call current_value unless @dont_update_state rescue nil # rescue in case we in middle of render
74
+ @props_hash[name]
65
75
  end
66
76
  end
77
+ define_method("#{name}") { deprecated_params_method("#{name}") }
78
+ define_method("#{name}!") { |*args| deprecated_params_method("#{name}!", *args) }
67
79
  elsif param_type == Proc
68
- define_method("#{name}") do |*args, &block|
69
- params[name].call(*args, &block) if params[name]
80
+ wrapper.define_method("#{name}") do |*args, &block|
81
+ @props_hash[name].call(*args, &block) if @props_hash[name]
70
82
  end
83
+ define_method("#{name}") { deprecated_params_method("#{name}", *args, &block) }
71
84
  else
72
- define_method("#{name}") do
85
+ wrapper.define_method("#{name}") do
73
86
  @processed_params[name] ||= if param_type.respond_to? :_react_param_conversion
74
- param_type._react_param_conversion params[name]
87
+ param_type._react_param_conversion @props_hash[name]
75
88
  elsif param_type.is_a?(Array) && param_type[0].respond_to?(:_react_param_conversion)
76
- params[name].collect { |param| param_type[0]._react_param_conversion param }
89
+ @props_hash[name].collect { |param| param_type[0]._react_param_conversion param }
77
90
  else
78
- params[name]
91
+ @props_hash[name]
79
92
  end
80
93
  end
94
+ define_method("#{name}") { deprecated_params_method("#{name}") }
95
+ end
96
+ end
97
+
98
+ def param(*args)
99
+ if args[0].is_a? Hash
100
+ options = args[0]
101
+ name = options.first[0]
102
+ default = options.first[1]
103
+ options.delete(name)
104
+ options.merge!({default: default})
105
+ else
106
+ name = args[0]
107
+ options = args[1] || {}
108
+ end
109
+ if options[:default]
110
+ validator.optional(name, options)
111
+ else
112
+ validator.requires(name, options)
81
113
  end
82
114
  end
83
115
 
84
116
  def required_param(name, options = {})
117
+ deprecation_warning "`required_param` is deprecated, use `param` instead."
85
118
  validator.requires(name, options)
86
- define_param_method(name, options[:type])
87
119
  end
88
120
 
89
121
  alias_method :require_param, :required_param
90
122
 
91
123
  def optional_param(name, options = {})
124
+ deprecation_warning "`optional_param` is deprecated, use `param param_name: default_value` instead."
92
125
  validator.optional(name, options)
93
- define_param_method(name, options[:type]) unless name == :params
94
126
  end
95
127
 
96
128
  def collect_other_params_as(name)
@@ -123,14 +155,16 @@ module React
123
155
 
124
156
  def define_state_methods(this, name, from = nil, &block)
125
157
  this.define_method("#{name}") do
158
+ self.class.deprecation_warning "Direct access to state `#{name}`. Use `state.#{name}` instead." if from.nil? || from == this
126
159
  React::State.get_state(from || self, name)
127
160
  end
128
161
  this.define_method("#{name}=") do |new_state|
162
+ self.class.deprecation_warning "Direct assignment to state `#{name}`. Use `#{(from && from != this) ? from : 'state'}.#{name}!` instead."
129
163
  yield name, React::State.get_state(from || self, name), new_state if block && block.arity > 0
130
164
  React::State.set_state(from || self, name, new_state)
131
165
  end
132
166
  this.define_method("#{name}!") do |*args|
133
- #return unless @native
167
+ self.class.deprecation_warning "Direct access to state `#{name}`. Use `state.#{name}` instead." if from.nil? or from == this
134
168
  if args.count > 0
135
169
  yield name, React::State.get_state(from || self, name), args[0] if block && block.arity > 0
136
170
  current_value = React::State.get_state(from || self, name)
@@ -1,4 +1,39 @@
1
1
  module React
2
+ class StateWrapper
3
+
4
+ def initialize(native, from)
5
+ @state_hash = Hash.new(`#{native}.state`)
6
+ @from = from
7
+ end
8
+
9
+ def [](state)
10
+ @state_hash[state]
11
+ end
12
+
13
+ def []=(state, new_value)
14
+ @state_hash[state] = new_value
15
+ end
16
+
17
+ def method_missing(method, *args)
18
+ if match = method.match(/^(.+)\!$/)
19
+ if args.count > 0
20
+ current_value = React::State.get_state(@from, match[1])
21
+ React::State.set_state(@from, $1, args[0])
22
+ current_value
23
+ else
24
+ current_state = React::State.get_state(@from, match[1])
25
+ React::State.set_state(@from, $1, current_state)
26
+ React::Observable.new(current_state) do |update|
27
+ React::State.set_state(@from, $1, update)
28
+ end
29
+ end
30
+ else
31
+ React::State.get_state(@from, method)
32
+ end
33
+ end
34
+ end
35
+
36
+
2
37
  class State
3
38
  class << self
4
39
  attr_reader :current_observer
@@ -80,14 +115,10 @@ module React
80
115
  @nesting_level = (@nesting_level || 0) + 1
81
116
  start_time = Time.now.to_f
82
117
  observer_name = (observer.class.respond_to?(:name) ? observer.class.name : observer.to_s) rescue "object:#{observer.object_id}"
83
- puts "#{' ' * @nesting_level} Timing #{observer_name} Execution"
84
118
  end
85
119
  saved_current_observer = @current_observer
86
120
  @current_observer = observer
87
121
  return_value = yield
88
- if `typeof window.reactive_ruby_timing !== 'undefined'`
89
- puts "#{' ' * @nesting_level} Timing #{observer_name} Completed in #{'%.04f' % (Time.now.to_f-start_time)} seconds"
90
- end
91
122
  return_value
92
123
  ensure
93
124
  @current_observer = saved_current_observer
@@ -1,6 +1,6 @@
1
1
  require "native"
2
2
  require 'active_support'
3
- require 'react/component'
3
+ require 'react/component/base'
4
4
 
5
5
  module React
6
6
  HTML_TAGS = %w(a abbr address area article aside audio b base bdi bdo big blockquote body br
@@ -49,4 +49,13 @@ module React
49
49
  def self.unmount_component_at_node(node)
50
50
  `React.unmountComponentAtNode(node.$$class ? node[0] : node)`
51
51
  end
52
+
52
53
  end
54
+
55
+ Element.instance_eval do
56
+ class ::Element::DummyContext < React::Component::Base
57
+ end
58
+ def render(&block)
59
+ React.render(React::RenderingContext.render(nil) {::Element::DummyContext.new.instance_eval &block}, self)
60
+ end
61
+ end if Object.const_defined?("Element")
@@ -1,8 +1,13 @@
1
1
  module React
2
+
2
3
  class Validator
3
4
  attr_accessor :errors
4
5
  private :errors
5
6
 
7
+ def initialize(component_class)
8
+ @component_class = component_class
9
+ end
10
+
6
11
  def self.build(&block)
7
12
  self.new.build(&block)
8
13
  end
@@ -15,11 +20,13 @@ module React
15
20
  def requires(name, options = {})
16
21
  options[:required] = true
17
22
  define_rule(name, options)
23
+ @component_class.define_param_method(name, options[:type])
18
24
  end
19
25
 
20
26
  def optional(name, options = {})
21
27
  options[:required] = false
22
28
  define_rule(name, options)
29
+ @component_class.define_param_method(name, options[:type]) unless name == :params
23
30
  end
24
31
 
25
32
  def allow_undefined_props=(allow)
@@ -73,11 +80,14 @@ module React
73
80
 
74
81
  def validate_types(prop_name, value)
75
82
  return unless klass = rules[prop_name][:type]
76
- if klass.is_a?(Array) && klass.length > 0
83
+ if !klass.is_a?(Array)
84
+ allow_nil = !!rules[prop_name][:allow_nil]
85
+ type_check("`#{prop_name}`", value, klass, allow_nil)
86
+ elsif klass.length > 0
77
87
  validate_value_array(prop_name, value)
78
88
  else
79
89
  allow_nil = !!rules[prop_name][:allow_nil]
80
- type_check("`#{prop_name}`", value, klass, allow_nil)
90
+ type_check("`#{prop_name}`", value, Array, allow_nil)
81
91
  end
82
92
  end
83
93
 
@@ -2,6 +2,7 @@ if RUBY_ENGINE == 'opal'
2
2
  require 'sources/react.js'
3
3
  require 'react/top_level'
4
4
  require 'react/component'
5
+ require 'react/component/base'
5
6
  require 'react/element'
6
7
  require 'react/event'
7
8
  require 'react/api'
@@ -4,6 +4,9 @@ module ReactiveRuby
4
4
  private :v8_context
5
5
 
6
6
  def initialize(v8_context)
7
+ unless v8_context
8
+ raise ArgumentError.new('Could not obtain ExecJS runtime context')
9
+ end
7
10
  @v8_context = v8_context
8
11
  end
9
12
 
@@ -1,3 +1,3 @@
1
1
  module React
2
- VERSION = "0.7.31"
2
+ VERSION = "0.7.32"
3
3
  end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe React::Component::Base do
5
+ after(:each) do
6
+ React::API.clear_component_class_cache
7
+ end
8
+
9
+ it 'can be inherited to create a component class' do
10
+ stub_const 'Foo', Class.new(React::Component::Base)
11
+ Foo.class_eval do
12
+ before_mount do
13
+ @instance_data = ["working"]
14
+ end
15
+ def render
16
+ @instance_data.first
17
+ end
18
+ end
19
+ stub_const 'Bar', Class.new(Foo)
20
+ Bar.class_eval do
21
+ before_mount do
22
+ @instance_data << "well"
23
+ end
24
+ def render
25
+ @instance_data.join(" ")
26
+ end
27
+ end
28
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq("<span>working</span>")
29
+ expect(React.render_to_static_markup(React.create_element(Bar))).to eq("<span>working well</span>")
30
+ end
31
+
32
+ end
33
+ end
@@ -102,6 +102,58 @@ describe React::Component do
102
102
  end
103
103
  end
104
104
 
105
+ describe 'New style setter & getter' do
106
+ before(:each) do
107
+ stub_const 'Foo', Class.new
108
+ Foo.class_eval do
109
+ include React::Component
110
+ def render
111
+ div { state.foo }
112
+ end
113
+ end
114
+ end
115
+
116
+ it 'implicitly will create a state variable when first written' do
117
+ Foo.class_eval do
118
+ before_mount do
119
+ state.foo! 'bar'
120
+ end
121
+ end
122
+
123
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>bar</div>')
124
+ end
125
+
126
+ it 'returns an observer with the bang method and no arguments' do
127
+ Foo.class_eval do
128
+ before_mount do
129
+ state.foo!(state.baz!.class.name)
130
+ end
131
+ end
132
+
133
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>React::Observable</div>')
134
+ end
135
+
136
+ it 'returns the current value of a state when written' do
137
+ Foo.class_eval do
138
+ before_mount do
139
+ state.baz! 'bar'
140
+ state.foo!(state.baz!('pow'))
141
+ end
142
+ end
143
+
144
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>bar</div>')
145
+ end
146
+
147
+ it 'can access an explicitly defined state`' do
148
+ Foo.class_eval do
149
+ define_state foo: :bar
150
+ end
151
+
152
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq('<div>bar</div>')
153
+ end
154
+
155
+ end
156
+
105
157
  describe 'State setter & getter' do
106
158
  before(:each) do
107
159
  stub_const 'Foo', Class.new
@@ -359,11 +411,11 @@ describe React::Component do
359
411
 
360
412
  %x{
361
413
  var log = [];
362
- var org_console = window.console;
363
- window.console = {warn: function(str){log.push(str)}}
414
+ var org_warn_console = window.console.warn;
415
+ window.console.warn = function(str){log.push(str)}
364
416
  }
365
417
  renderToDocument(Foo, bar: 10, lorem: Lorem.new)
366
- `window.console = org_console;`
418
+ `window.console.warn = org_warn_console;`
367
419
  expect(`log`).to eq(["Warning: Failed propType: In component `Foo`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String"])
368
420
  end
369
421
 
@@ -381,11 +433,11 @@ describe React::Component do
381
433
 
382
434
  %x{
383
435
  var log = [];
384
- var org_console = window.console;
385
- window.console = {warn: function(str){log.push(str)}}
436
+ var org_warn_console = window.console.warn;
437
+ window.console.warn = function(str){log.push(str)}
386
438
  }
387
439
  renderToDocument(Foo, foo: 10, bar: '10', lorem: Lorem.new)
388
- `window.console = org_console;`
440
+ `window.console.warn = org_warn_console;`
389
441
  expect(`log`).to eq([])
390
442
  end
391
443
  end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe 'React::Observable' do
5
+ # tbd
6
+ end
7
+ end
@@ -1,18 +1,187 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  if opal?
4
- describe 'the required_param macro' do
5
- it "can be used to declare a required param" # required_param :foo
6
- it "can have a simple type" # required_param :foo, type: String
7
- it "can use the [] notation for arrays" # required_param :foo, type: []
8
- it "can use the [] notation for arrays of a specific type" # required_param :foo, type: [String]
9
- it "can convert a json hash to a type" # required_param :foo, type: BazWoggle # requires a BazWoggle conversion be provided
10
- it "will alias a Proc type param" # required_param :foo, type: Proc # we can just say foo(...) to call the proc
11
- it "will bind a two way linkage to an observable param" # required_param :foo, type: React::Observable # defines foo, and foo! bound to the observer
12
- # you can create an observable simply by passing the value of some state!
13
- end
14
- describe 'the optional_param macro' do
15
- # works just like a required_param, and
16
- it "can have a default value" # optional_param :foo, type: String, default: ""
4
+
5
+ describe 'the param macro' do
6
+
7
+ it "can create and access a required param" do
8
+ stub_const 'Foo', Class.new(React::Component::Base)
9
+ Foo.class_eval do
10
+ param :foo
11
+
12
+ def render
13
+ div { params.foo }
14
+ end
15
+ end
16
+
17
+ expect(React.render_to_static_markup(React.create_element(Foo, {foo: :bar}))).to eq('<div>bar</div>')
18
+ end
19
+
20
+ it "can create and access an optional params" do
21
+ stub_const 'Foo', Class.new(React::Component::Base)
22
+ Foo.class_eval do
23
+
24
+ param foo1: :no_bar1
25
+ param foo2: :no_bar2
26
+ param :foo3, default: :no_bar3
27
+ param :foo4, default: :no_bar4
28
+
29
+ def render
30
+ div { "#{params.foo1}-#{params.foo2}-#{params.foo3}-#{params.foo4}" }
31
+ end
32
+ end
33
+
34
+ expect(React.render_to_static_markup(React.create_element(Foo, {foo1: :bar1, foo3: :bar3}))).to eq('<div>bar1-no_bar2-bar3-no_bar4</div>')
35
+ end
36
+
37
+ it 'can specify validation rules with the type option' do
38
+ stub_const 'Foo', Class.new(React::Component::Base)
39
+ Foo.class_eval do
40
+ param :foo, type: String
41
+ end
42
+
43
+ expect(Foo.prop_types).to have_key(:_componentValidator)
44
+ end
45
+
46
+ it "can type check params" do
47
+ stub_const 'Foo', Class.new(React::Component::Base)
48
+ Foo.class_eval do
49
+
50
+ param :foo1, type: String
51
+ param :foo2, type: String
52
+
53
+ def render
54
+ div { "#{params.foo1}-#{params.foo2}" }
55
+ end
56
+ end
57
+
58
+ expect(React.render_to_static_markup(React.create_element(Foo, {foo1: 12, foo2: "string"}))).to eq('<div>12-string</div>')
59
+ end
60
+
61
+ it 'logs error in warning if validation failed' do
62
+ stub_const 'Lorem', Class.new
63
+ stub_const 'Foo2', Class.new(React::Component::Base)
64
+ Foo2.class_eval do
65
+ param :foo
66
+ param :lorem, type: Lorem
67
+ param :bar, default: nil, type: String
68
+ def render; div; end
69
+ end
70
+
71
+ %x{
72
+ var log = [];
73
+ var org_warn_console = window.console.warn;
74
+ window.console.warn = function(str){log.push(str)}
75
+ }
76
+ renderToDocument(Foo2, bar: 10, lorem: Lorem.new)
77
+ `window.console.warn = org_warn_console;`
78
+ expect(`log`).to eq(["Warning: Failed propType: In component `Foo2`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String"])
79
+ end
80
+
81
+ it 'should not log anything if validation passes' do
82
+ stub_const 'Lorem', Class.new
83
+ stub_const 'Foo', Class.new(React::Component::Base)
84
+ Foo.class_eval do
85
+ param :foo
86
+ param :lorem, type: Lorem
87
+ param :bar, default: nil, type: String
88
+
89
+ def render; div; end
90
+ end
91
+
92
+ %x{
93
+ var log = [];
94
+ var org_warn_console = window.console.warn;
95
+ window.console.warn = function(str){log.push(str)}
96
+ }
97
+ renderToDocument(Foo, foo: 10, bar: '10', lorem: Lorem.new)
98
+ `window.console.warn = org_warn_console;`
99
+ expect(`log`).to eq([])
100
+ end
101
+
102
+ describe 'advanced type handling' do
103
+ before(:each) do
104
+ %x{
105
+ window.dummy_log = [];
106
+ window.org_warn_console = window.console.warn;
107
+ window.console.warn = function(str){window.dummy_log.push(str)}
108
+ }
109
+ stub_const 'Foo', Class.new(React::Component::Base)
110
+ Foo.class_eval { def render; ""; end }
111
+ end
112
+ after(:each) do
113
+ `window.console.warn = window.org_warn_console;`
114
+ end
115
+
116
+ it "can use the [] notation for arrays" do
117
+ Foo.class_eval do
118
+ param :foo, type: []
119
+ param :bar, type: []
120
+ end
121
+ renderToDocument(Foo, foo: 10, bar: [10])
122
+ expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo` could not be converted to Array"])
123
+ end
124
+
125
+ it "can use the [xxx] notation for arrays of a specific type" do
126
+ Foo.class_eval do
127
+ param :foo, type: [String]
128
+ param :bar, type: [String]
129
+ end
130
+ renderToDocument(Foo, foo: [10], bar: ["10"])
131
+ expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo`[0] could not be converted to String"])
132
+ end
133
+
134
+ it "can convert a json hash to a type" do
135
+ stub_const "BazWoggle", Class.new
136
+ BazWoggle.class_eval do
137
+ def initialize(kind)
138
+ @kind = kind
139
+ end
140
+ attr_accessor :kind
141
+ def self._react_param_conversion(json, validate_only)
142
+ new(json[:bazwoggle]) if json[:bazwoggle]
143
+ end
144
+ end
145
+ Foo.class_eval do
146
+ param :foo, type: BazWoggle
147
+ param :bar, type: BazWoggle
148
+ param :baz, type: [BazWoggle]
149
+ def render
150
+ "#{params.bar.kind}, #{params.baz[0].kind}"
151
+ end
152
+ end
153
+ expect(React.render_to_static_markup(React.create_element(
154
+ Foo, foo: "", bar: {bazwoggle: 1}, baz: [{bazwoggle: 2}]))).to eq('<span>1, 2</span>')
155
+ expect(`window.dummy_log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `foo` could not be converted to BazWoggle"])
156
+ end
157
+
158
+ it "will alias a Proc type param" do
159
+ Foo.class_eval do
160
+ param :foo, type: Proc
161
+ def render
162
+ params.foo
163
+ end
164
+ end
165
+ expect(React.render_to_static_markup(React.create_element(Foo, foo: lambda { 'works!' }))).to eq('<span>works!</span>')
166
+ end
167
+
168
+ it "will create a 'bang' (i.e. update) method if the type is React::Observable" do
169
+ Foo.class_eval do
170
+ param :foo, type: React::Observable
171
+ before_mount do
172
+ params.foo! "ha!"
173
+ end
174
+ def render
175
+ params.foo
176
+ end
177
+ end
178
+ current_state = ""
179
+ observer = React::Observable.new(current_state) { |new_state| current_state = new_state }
180
+ expect(React.render_to_static_markup(React.create_element(Foo, foo: observer))).to eq('<span>ha!</span>')
181
+ expect(current_state).to eq("ha!")
182
+ end
183
+
184
+ end
185
+
17
186
  end
18
187
  end
@@ -2,10 +2,13 @@ require "spec_helper"
2
2
 
3
3
  if opal?
4
4
  describe React::Validator do
5
+ before do
6
+ stub_const 'Foo', Class.new(React::Component::Base)
7
+ end
5
8
  describe '#validate' do
6
9
  describe "Presence validation" do
7
10
  it "should check if required props provided" do
8
- validator = React::Validator.build do
11
+ validator = React::Validator.new(Foo).build do
9
12
  requires :foo
10
13
  requires :bar
11
14
  end
@@ -15,7 +18,7 @@ describe React::Validator do
15
18
  end
16
19
 
17
20
  it "should check if passed non specified prop" do
18
- validator = React::Validator.build do
21
+ validator = React::Validator.new(Foo).build do
19
22
  optional :foo
20
23
  end
21
24
 
@@ -26,7 +29,7 @@ describe React::Validator do
26
29
 
27
30
  describe "Type validation" do
28
31
  it "should check if passed value with wrong type" do
29
- validator = React::Validator.build do
32
+ validator = React::Validator.new(Foo).build do
30
33
  requires :foo, type: String
31
34
  end
32
35
 
@@ -36,7 +39,7 @@ describe React::Validator do
36
39
 
37
40
  it "should check if passed value with wrong custom type" do
38
41
  stub_const 'Bar', Class.new
39
- validator = React::Validator.build do
42
+ validator = React::Validator.new(Foo).build do
40
43
  requires :foo, type: Bar
41
44
  end
42
45
 
@@ -45,7 +48,7 @@ describe React::Validator do
45
48
  end
46
49
 
47
50
  it 'coerces native JS prop types to opal objects' do
48
- validator = React::Validator.build do
51
+ validator = React::Validator.new(Foo).build do
49
52
  requires :foo, type: `{ x: 1 }`
50
53
  end
51
54
 
@@ -54,7 +57,7 @@ describe React::Validator do
54
57
  end
55
58
 
56
59
  it 'coerces native JS values to opal objects' do
57
- validator = React::Validator.build do
60
+ validator = React::Validator.new(Foo).build do
58
61
  requires :foo, type: Array[Fixnum]
59
62
  end
60
63
 
@@ -63,7 +66,7 @@ describe React::Validator do
63
66
  end
64
67
 
65
68
  it "should support Array[Class] validation" do
66
- validator = React::Validator.build do
69
+ validator = React::Validator.new(Foo).build do
67
70
  requires :foo, type: Array[Hash]
68
71
  end
69
72
 
@@ -80,7 +83,7 @@ describe React::Validator do
80
83
 
81
84
  describe "Limited values" do
82
85
  it "should check if passed value is not one of the specified values" do
83
- validator = React::Validator.build do
86
+ validator = React::Validator.new(Foo).build do
84
87
  requires :foo, values: [4,5,6]
85
88
  end
86
89
 
@@ -93,7 +96,7 @@ describe React::Validator do
93
96
  describe '#undefined_props' do
94
97
  let(:props) { { foo: 'foo', bar: 'bar', biz: 'biz', baz: 'baz' } }
95
98
  let(:validator) do
96
- React::Validator.build do
99
+ React::Validator.new(Foo).build do
97
100
  requires :foo
98
101
  optional :bar
99
102
  end
@@ -112,7 +115,7 @@ describe React::Validator do
112
115
 
113
116
  describe "default_props" do
114
117
  it "should return specified default values" do
115
- validator = React::Validator.build do
118
+ validator = React::Validator.new(Foo).build do
116
119
  requires :foo, default: 10
117
120
  requires :bar
118
121
  optional :lorem, default: 20
@@ -12,6 +12,14 @@ RSpec.describe ReactiveRuby::ComponentLoader do
12
12
  let(:context) { ExecJS.compile(GLOBAL_WRAPPER + js) }
13
13
  let(:v8_context) { context.instance_variable_get(:@v8_context) }
14
14
 
15
+ describe '.new' do
16
+ it 'raises a meaningful exception when initialized without a context' do
17
+ expect {
18
+ described_class.new(nil)
19
+ }.to raise_error(/Could not obtain ExecJS runtime context/)
20
+ end
21
+ end
22
+
15
23
  describe '#load' do
16
24
  it 'loads given asset file into context' do
17
25
  loader = described_class.new(v8_context)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reactive-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.31
4
+ version: 0.7.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Chang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-23 00:00:00.000000000 Z
11
+ date: 2015-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opal
@@ -262,6 +262,7 @@ files:
262
262
  - lib/react/callbacks.rb
263
263
  - lib/react/component.rb
264
264
  - lib/react/component/api.rb
265
+ - lib/react/component/base.rb
265
266
  - lib/react/component/class_methods.rb
266
267
  - lib/react/element.rb
267
268
  - lib/react/event.rb
@@ -292,11 +293,13 @@ files:
292
293
  - spec/controller_helper_spec.rb
293
294
  - spec/index.html.erb
294
295
  - spec/react/callbacks_spec.rb
296
+ - spec/react/component_base_spec.rb
295
297
  - spec/react/component_spec.rb
296
298
  - spec/react/dsl_spec.rb
297
299
  - spec/react/element_spec.rb
298
300
  - spec/react/event_spec.rb
299
301
  - spec/react/native_library_spec.rb
302
+ - spec/react/observable_spec.rb
300
303
  - spec/react/param_declaration_spec.rb
301
304
  - spec/react/react_spec.rb
302
305
  - spec/react/react_state_spec.rb
@@ -338,11 +341,13 @@ test_files:
338
341
  - spec/controller_helper_spec.rb
339
342
  - spec/index.html.erb
340
343
  - spec/react/callbacks_spec.rb
344
+ - spec/react/component_base_spec.rb
341
345
  - spec/react/component_spec.rb
342
346
  - spec/react/dsl_spec.rb
343
347
  - spec/react/element_spec.rb
344
348
  - spec/react/event_spec.rb
345
349
  - spec/react/native_library_spec.rb
350
+ - spec/react/observable_spec.rb
346
351
  - spec/react/param_declaration_spec.rb
347
352
  - spec/react/react_spec.rb
348
353
  - spec/react/react_state_spec.rb