react-opal 0.14.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bbee40ce70c05f1c8be8904a0516736a898e0965
4
+ data.tar.gz: 3cb8fa21df7d35d38041b72ba48354e4f0bdf183
5
+ SHA512:
6
+ metadata.gz: 9a8506067fcb72df4a5c1b1160229a209bec151a37c3b36e932a1ea27ad27d02d08aafc88d7b8f5ece44e2a8e34cecdc4629500930ed20e2179c58e392206719
7
+ data.tar.gz: c88db5dc1b1a919992ed9d5aafae113b5ca62c75b00e36687aff4ae72172e2137e4765129263f2a8daf46894cee33292700e5a51afbe9bac1ba1c7a6fea41c3b
data/lib/react-opal.rb ADDED
@@ -0,0 +1,15 @@
1
+ if RUBY_ENGINE == 'opal'
2
+ require 'react/opal/top_level'
3
+ require 'react/opal/props_children'
4
+ require 'react/opal/component'
5
+ require 'react/opal/native_element'
6
+ require 'react/opal/event'
7
+ require 'react/opal/component_factory'
8
+ require 'react/opal/validator'
9
+ else
10
+ require 'opal'
11
+ require 'react/opal/version'
12
+ require 'opal-activesupport'
13
+
14
+ Opal.append_path File.expand_path('../', __FILE__).untaint
15
+ end
@@ -0,0 +1,35 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
3
+ module React
4
+ module Callbacks
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def run_callback(name, *args)
10
+ attribute_name = "_#{name}_callbacks"
11
+ callbacks = self.class.send(attribute_name)
12
+ callbacks.each do |callback|
13
+ if callback.is_a?(Proc)
14
+ instance_exec(*args, &callback)
15
+ else
16
+ send(callback, *args)
17
+ end
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ def define_callback(callback_name)
23
+ attribute_name = "_#{callback_name}_callbacks"
24
+ class_attribute(attribute_name)
25
+ self.send("#{attribute_name}=", [])
26
+ define_singleton_method(callback_name) do |*args, &block|
27
+ callbacks = self.send(attribute_name)
28
+ callbacks.concat(args)
29
+ callbacks.push(block) if block_given?
30
+ self.send("#{attribute_name}=", callbacks)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,221 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'react/opal/callbacks'
3
+ require 'react/opal/ext/hash'
4
+ require 'react/opal/component/api'
5
+
6
+ module React
7
+ module Component
8
+ def self.included(base)
9
+ base.include(API)
10
+ base.include(React::Callbacks)
11
+ base.class_eval do
12
+ class_attribute :init_state, :validator, :context_types, :child_context_types, :child_context_get
13
+ define_callback :before_mount
14
+ define_callback :after_mount
15
+ define_callback :before_receive_props
16
+ define_callback :before_update
17
+ define_callback :after_update
18
+ define_callback :before_unmount
19
+ end
20
+ base.extend(ClassMethods)
21
+ end
22
+
23
+ def params
24
+ Hash.new(`#{self}.props`).inject({}) do |memo, (k, v)|
25
+ memo[k.underscore] = v
26
+ memo
27
+ end
28
+ end
29
+
30
+ def refs
31
+ Hash.new(`#{self}.refs`)
32
+ end
33
+
34
+ def context
35
+ Hash.new(`#{self}.context`)
36
+ end
37
+
38
+ def emit(event_name, *args)
39
+ self.params["on_#{event_name.to_s}"].call(*args)
40
+ end
41
+
42
+ def component_will_mount
43
+ self.run_callback(:before_mount)
44
+ end
45
+
46
+ def component_did_mount
47
+ self.run_callback(:after_mount)
48
+ end
49
+
50
+ def component_will_receive_props(next_props)
51
+ self.run_callback(:before_receive_props, Hash.new(next_props))
52
+ end
53
+
54
+ def should_component_update?(next_props, next_state)
55
+ self.respond_to?(:needs_update?) ? self.needs_update?(Hash.new(next_props), Hash.new(next_state)) : true
56
+ end
57
+
58
+ def component_will_update(next_props, next_state)
59
+ self.run_callback(:before_update, Hash.new(next_props), Hash.new(next_state))
60
+ end
61
+
62
+ def component_did_update(prev_props, prev_state)
63
+ self.run_callback(:after_update, Hash.new(prev_props), Hash.new(prev_state))
64
+ end
65
+
66
+ def component_will_unmount
67
+ self.run_callback(:before_unmount)
68
+ end
69
+
70
+ def p(*args, &block)
71
+ if block || args.count == 0 || (args.count == 1 && args.first.is_a?(Hash))
72
+ _p_tag(*args, &block)
73
+ else
74
+ Kernel.p(*args)
75
+ end
76
+ end
77
+
78
+ def method_missing(name, *args, &block)
79
+ unless (React::HTML_TAGS.include?(name) || name == 'present' || name == '_p_tag')
80
+ return super
81
+ end
82
+
83
+ if name == "present"
84
+ name = args.shift
85
+ end
86
+
87
+ if name == "_p_tag"
88
+ name = "p"
89
+ end
90
+
91
+ @buffer = [] unless @buffer
92
+ if block
93
+ current = @buffer
94
+ @buffer = []
95
+ result = block.call
96
+ element = React.create_element(name, *args) { @buffer.count == 0 ? result : @buffer }
97
+ @buffer = current
98
+ else
99
+ element = React.create_element(name, *args)
100
+ end
101
+
102
+ @buffer << element
103
+ element
104
+ end
105
+
106
+ def to_n
107
+ self
108
+ end
109
+
110
+ module ClassMethods
111
+ def prop_types
112
+ if self.validator
113
+ {
114
+ _componentValidator: %x{
115
+ function(props, propName, componentName) {
116
+ var errors = #{validator.validate(Hash.new(`props`))};
117
+ var error = new Error(#{"In component `" + self.name + "`\n" + `errors`.join("\n")});
118
+ return #{`errors`.count > 0 ? `error` : `undefined`};
119
+ }
120
+ }
121
+ }
122
+ else
123
+ {}
124
+ end
125
+ end
126
+
127
+ def initial_state
128
+ self.init_state || {}
129
+ end
130
+
131
+ def default_props
132
+ self.validator ? self.validator.default_props : {}
133
+ end
134
+
135
+ def params(&block)
136
+ if self.validator
137
+ self.validator.evaluate_more_rules(&block)
138
+ else
139
+ self.validator = React::Validator.build(&block)
140
+ end
141
+ end
142
+
143
+ def define_state_prop(prop, &block)
144
+ define_state prop
145
+ update_value = lambda do |new_value|
146
+ new_value = instance_exec(new_value, &block) if block
147
+ self.send("#{prop}=", new_value)
148
+ end
149
+ before_mount do
150
+ # need to execute in context of each object
151
+ instance_exec params[prop], &update_value
152
+ end
153
+ before_receive_props do |new_props|
154
+ # need to execute in context of each object
155
+ instance_exec new_props[prop], &update_value
156
+ end
157
+ end
158
+
159
+ def get_prop_type(klass)
160
+ if klass == Proc
161
+ `React.PropTypes.func`
162
+ elsif klass.is_a?(Proc)
163
+ `React.PropTypes.object`
164
+ elsif klass == Boolean
165
+ `React.PropTypes.bool`
166
+ elsif klass.ancestors.include?(Numeric)
167
+ `React.PropTypes.number`
168
+ elsif klass == String
169
+ `React.PropTypes.string`
170
+ elsif klass == Array
171
+ `React.PropTypes.array`
172
+ else
173
+ `React.PropTypes.object`
174
+ end
175
+ end
176
+
177
+ def consume_context(item, klass)
178
+ self.context_types ||= {}
179
+ self.context_types[item] = get_prop_type(klass)
180
+ end
181
+
182
+ def provide_context(item, klass, &block)
183
+ self.child_context_types ||= {}
184
+ self.child_context_types[item] = get_prop_type(klass)
185
+ self.child_context_get ||= {}
186
+ self.child_context_get[item] = block
187
+ unless method_defined?(:get_child_context)
188
+ define_method(:get_child_context) do
189
+ Hash[self.child_context_get.map do |item, blk|
190
+ [item, instance_eval(&blk)]
191
+ end].to_n
192
+ end
193
+ end
194
+ end
195
+
196
+ def define_state(*states)
197
+ raise "Block could be only given when define exactly one state" if block_given? && states.count > 1
198
+
199
+ self.init_state = {} unless self.init_state
200
+
201
+ if block_given?
202
+ self.init_state[states[0]] = yield
203
+ end
204
+ states.each do |name|
205
+ # getter
206
+ define_method("#{name}") do
207
+ self.state[name]
208
+ end
209
+ # setter
210
+ define_method("#{name}=") do |new_state|
211
+ hash = {}
212
+ hash[name] = new_state
213
+ self.set_state(hash)
214
+
215
+ new_state
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,29 @@
1
+ module React
2
+ module Component
3
+ module API
4
+ def self.included(base)
5
+ base.include(::React::PropsChildren)
6
+ end
7
+
8
+ def state
9
+ Hash.new(`#{self}.state`)
10
+ end
11
+
12
+ def force_update!
13
+ `#{self}.forceUpdate()`
14
+ end
15
+
16
+ def set_state(state, &block)
17
+ %x{
18
+ #{self}.setState(#{state.shallow_to_n}, function(){
19
+ #{block.call if block}
20
+ });
21
+ }
22
+ end
23
+
24
+ def dom_node
25
+ raise "`dom_node` is deprecated in favor of `React.find_dom_node`"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,78 @@
1
+ module React
2
+ class ComponentFactory
3
+ @@component_classes = {}
4
+
5
+ def self.clear_component_class_cache
6
+ @@component_classes = {}
7
+ end
8
+
9
+ def self.native_component_class(klass)
10
+ @@component_classes[klass.to_s] ||= begin
11
+ klass.class_eval do
12
+ include(React::Component::API)
13
+ # In Opal 0.8, native_alias fails if the method isn't there but we don't want to force all of these to be implemented
14
+ optional_native_alias = lambda do |js, ruby|
15
+ not_there = `!(#{self}.$$proto['$' + #{ruby}])`
16
+ native_alias js, ruby unless not_there
17
+ end
18
+ optional_native_alias[:componentWillMount, :component_will_mount]
19
+ optional_native_alias[:componentDidMount, :component_did_mount]
20
+ optional_native_alias[:componentWillReceiveProps, :component_will_receive_props]
21
+ optional_native_alias[:shouldComponentUpdate, :should_component_update?]
22
+ optional_native_alias[:componentWillUpdate, :component_will_update]
23
+ optional_native_alias[:componentDidUpdate, :component_did_update]
24
+ optional_native_alias[:componentWillUnmount, :component_will_unmount]
25
+ optional_native_alias[:getChildContext, :get_child_context]
26
+ native_alias :render, :render
27
+ end
28
+ %x{
29
+ if (!Object.assign) {
30
+ Object.defineProperty(Object, 'assign', {
31
+ enumerable: false,
32
+ configurable: true,
33
+ writable: true,
34
+ value: function(target, firstSource) {
35
+ 'use strict';
36
+ if (target === undefined || target === null) {
37
+ throw new TypeError('Cannot convert first argument to object');
38
+ }
39
+
40
+ var to = Object(target);
41
+ for (var i = 1; i < arguments.length; i++) {
42
+ var nextSource = arguments[i];
43
+ if (nextSource === undefined || nextSource === null) {
44
+ continue;
45
+ }
46
+
47
+ var keysArray = Object.keys(Object(nextSource));
48
+ for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
49
+ var nextKey = keysArray[nextIndex];
50
+ var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
51
+ if (desc !== undefined && desc.enumerable) {
52
+ to[nextKey] = nextSource[nextKey];
53
+ }
54
+ }
55
+ }
56
+ return to;
57
+ }
58
+ });
59
+ }
60
+ function ctor(props){
61
+ this.constructor = ctor;
62
+ this.state = #{klass.respond_to?(:initial_state) ? klass.initial_state.to_n : `{}`};
63
+ React.Component.apply(this, arguments);
64
+ #{klass}.$$alloc.prototype.$initialize.call(this, Opal.Hash.$new(props));
65
+ };
66
+ ctor.prototype = klass.$$proto;
67
+ Object.assign(ctor.prototype, React.Component.prototype);
68
+ ctor.propTypes = #{klass.respond_to?(:prop_types) ? klass.prop_types.to_n : `{}`};
69
+ ctor.contextTypes = #{klass.respond_to?(:context_types) ? klass.context_types.to_n : `{}`};
70
+ ctor.childContextTypes = #{klass.respond_to?(:child_context_types) ? klass.child_context_types.to_n : `{}`};
71
+ ctor.defaultProps = #{klass.respond_to?(:default_props) ? klass.default_props.to_n : `{}`};
72
+ ctor.displayName = #{klass.to_s};
73
+ }
74
+ `ctor`
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,76 @@
1
+ module React
2
+ class Event
3
+ include Native
4
+ alias_native :bubbles, :bubbles
5
+ alias_native :cancelable, :cancelable
6
+ alias_native :current_target, :currentTarget
7
+ alias_native :default_prevented, :defaultPrevented
8
+ alias_native :event_phase, :eventPhase
9
+ alias_native :is_trusted?, :isTrusted
10
+ alias_native :native_event, :nativeEvent
11
+ alias_native :target, :target
12
+ alias_native :timestamp, :timeStamp
13
+ alias_native :event_type, :type
14
+ alias_native :prevent_default, :preventDefault
15
+ alias_native :stop_propagation, :stopPropagation
16
+ # Clipboard
17
+ alias_native :clipboard_data, :clipboardData
18
+ # Keyboard
19
+ alias_native :alt_key, :altKey
20
+ alias_native :char_code, :charCode
21
+ alias_native :ctrl_key, :ctrlKey
22
+ alias_native :get_modifier_state, :getModifierState
23
+ alias_native :key, :key
24
+ alias_native :key_code, :keyCode
25
+ alias_native :locale, :locale
26
+ alias_native :location, :location
27
+ alias_native :meta_key, :metaKey
28
+ alias_native :repeat, :repeat
29
+ alias_native :shift_key, :shiftKey
30
+ alias_native :which, :which
31
+ # Focus
32
+ alias_native :related_target, :relatedTarget
33
+ # Mouse
34
+ alias_native :alt_key, :altKey
35
+ alias_native :button, :button
36
+ alias_native :buttons, :buttons
37
+ alias_native :client_x, :clientX
38
+ alias_native :client_y, :clientY
39
+ alias_native :ctrl_key, :ctrlKey
40
+ alias_native :get_modifier_state, :getModifierState
41
+ alias_native :meta_key, :metaKey
42
+ alias_native :page_x, :pageX
43
+ alias_native :page_y, :pageY
44
+ alias_native :related_target, :relatedTarget
45
+ alias_native :screen_x, :screen_x
46
+ alias_native :screen_y, :screen_y
47
+ alias_native :shift_key, :shift_key
48
+ # Touch
49
+ alias_native :alt_key, :altKey
50
+ alias_native :changed_touches, :changedTouches
51
+ alias_native :ctrl_key, :ctrlKey
52
+ alias_native :get_modifier_state, :getModifierState
53
+ alias_native :meta_key, :metaKey
54
+ alias_native :shift_key, :shiftKey
55
+ alias_native :target_touches, :targetTouches
56
+ alias_native :touches, :touches
57
+ # UI
58
+ alias_native :detail, :detail
59
+ alias_native :view, :view
60
+ # Wheel
61
+ alias_native :delta_mode, :deltaMode
62
+ alias_native :delta_x, :deltaX
63
+ alias_native :delta_y, :deltaY
64
+ alias_native :delta_z, :deltaZ
65
+
66
+ BUILT_IN_EVENTS = %w{onCopy onCut onPaste onKeyDown onKeyPress onKeyUp
67
+ onFocus onBlur onChange onInput onSubmit onClick onDoubleClick onDrag
68
+ onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop
69
+ onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver
70
+ onMouseUp onTouchCancel onTouchEnd onTouchMove onTouchStart onScroll}
71
+
72
+ def initialize(native_element)
73
+ @native = native_element
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,9 @@
1
+ class Hash
2
+ def shallow_to_n
3
+ hash = `{}`
4
+ self.map do |key, value|
5
+ `hash[#{key}] = #{value}`
6
+ end
7
+ hash
8
+ end
9
+ end
@@ -0,0 +1,52 @@
1
+ module React
2
+ class NativeElement
3
+ include PropsChildren
4
+
5
+ # As of React 0.14, elements are now just plain object literals, so we can't inherit anymore
6
+ # We can just set each of the properties on our object though
7
+ # See var ReactElement = function (type, key, ref, self, source, owner, props) in the React source
8
+ def initialize(native)
9
+ %x{
10
+ self.$$typeof = #{native}.$$typeof;
11
+ self.type = #{native}.type;
12
+ self.key = #{native}.key;
13
+ self.ref = #{native}.ref;
14
+ self.props = #{native}.props;
15
+ self._owner = #{native}._owner;
16
+ self._store = #{native}._store;
17
+ self._self = #{native}._self;
18
+ self._source = #{native}._source;
19
+ }
20
+ end
21
+
22
+ def element_type
23
+ self.JS[:type]
24
+ end
25
+
26
+ def on(event_name)
27
+ name = event_name.to_s.camelize
28
+
29
+ prop_key = "on#{name}"
30
+
31
+ if React::Event::BUILT_IN_EVENTS.include?(prop_key)
32
+ callback = %x{
33
+ function(event){
34
+ #{yield React::Event.new(`event`)}
35
+ }
36
+ }
37
+ else
38
+ callback = %x{
39
+ function(){
40
+ #{yield *Array(`arguments`)}
41
+ }
42
+ }
43
+ end
44
+
45
+ new_prop = `{}`
46
+ `new_prop[prop_key] = #{callback}`
47
+
48
+ cloned = `React.cloneElement(#{self}, #{new_prop})`
49
+ React::NativeElement.new cloned
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,50 @@
1
+ module React
2
+ module PropsChildren
3
+ def props
4
+ Hash.new(`#{self}.props`)
5
+ end
6
+
7
+ def children
8
+ nodes = `#{self}.props.children`
9
+
10
+ if `React.Children.count(nodes)` == 0
11
+ `[]`
12
+ elsif `React.Children.count(nodes)` == 1
13
+ if `(typeof nodes === 'string') || (typeof nodes === 'number')`
14
+ [nodes]
15
+ else
16
+ `[React.Children.only(nodes)]`
17
+ end
18
+ else
19
+ # Not sure the overhead of doing this..
20
+ class << nodes
21
+ include Enumerable
22
+
23
+ def to_n
24
+ self
25
+ end
26
+
27
+ def each(&block)
28
+ if block_given?
29
+ %x{
30
+ React.Children.forEach(#{self.to_n}, function(context){
31
+ #{block.call(`context`)}
32
+ })
33
+ }
34
+ else
35
+ Enumerator.new(`React.Children.count(#{self.to_n})`) do |y|
36
+ %x{
37
+ React.Children.forEach(#{self.to_n}, function(context){
38
+ #{y << `context`}
39
+ })
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ nodes
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ module React
2
+ module Testing
3
+ `var ReactTestUtils = React.addons.TestUtils`
4
+
5
+ def simulate_event(event_name, dom_element, event_data = {})
6
+ simulator = Native(`ReactTestUtils.Simulate`)
7
+ simulator[event_name].call(dom_element, event_data)
8
+ end
9
+
10
+ def render_to_document(element)
11
+ `ReactTestUtils.renderIntoDocument(#{element})`
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,101 @@
1
+ require 'native'
2
+ require 'active_support'
3
+
4
+ module React
5
+ HTML_TAGS = %w(a abbr address area article aside audio b base bdi bdo big blockquote body br
6
+ button canvas caption cite code col colgroup data datalist dd del details dfn
7
+ dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
8
+ h6 head header hr html i iframe img input ins kbd keygen label legend li link
9
+ main map mark menu menuitem meta meter nav noscript object ol optgroup option
10
+ output p param picture pre progress q rp rt ruby s samp script section select
11
+ small source span strong style sub summary sup table tbody td textarea tfoot th
12
+ thead time title tr track u ul var video wbr)
13
+
14
+ def self.create_element(type, properties = {})
15
+ params = []
16
+
17
+ # Component Spec or Normal DOM
18
+ native = `(typeof type === 'function')` || HTML_TAGS.include?(type)
19
+ params << if native
20
+ type
21
+ elsif type.kind_of?(Class)
22
+ raise "Provided class should define `render` method" if !(type.method_defined? :render)
23
+ React::ComponentFactory.native_component_class(type)
24
+ else
25
+ raise "#{type} not implemented"
26
+ end
27
+
28
+ # Passed in properties
29
+ props = camel_case_hash_keys(properties) do |key, value|
30
+ if key == "class_name" && value.is_a?(Hash)
31
+ value.inject([]) { |ary, (k, v)| v ? ary.push(k) : ary }.join(" ")
32
+ elsif key == 'value_link'
33
+ process_value_link value
34
+ else
35
+ value
36
+ end
37
+ end
38
+
39
+ params << props.shallow_to_n
40
+
41
+ # Children Nodes
42
+ if block_given?
43
+ [yield].flatten.each do |ele|
44
+ params << ele
45
+ end
46
+ end
47
+
48
+ element = `React.createElement.apply(null, #{params})`
49
+ React::NativeElement.new(element)
50
+ end
51
+
52
+ def self.lower_camelize(str)
53
+ camelized = str.camelize
54
+ camelized[0].downcase + camelized[1..-1]
55
+ end
56
+
57
+ def self.camel_case_hash_keys(input)
58
+ as_array = input.map do |key, value|
59
+ new_value = block_given? ? yield(key, value) : value
60
+ [lower_camelize(key), new_value]
61
+ end
62
+ Hash[as_array]
63
+ end
64
+
65
+ def self.process_value_link(arguments)
66
+ arguments = arguments.call if arguments.is_a? Proc
67
+ camel_case_hash_keys(arguments).to_n
68
+ end
69
+
70
+ def self.render(element, container)
71
+ component = Native(`ReactDOM.render(#{element}, container, function(){#{yield if block_given?}})`)
72
+ component.class.include(React::Component::API)
73
+ component
74
+ end
75
+
76
+ def self.is_valid_element(element)
77
+ `React.isValidElement(#{element})`
78
+ end
79
+
80
+ def self.render_to_string(element)
81
+ `ReactDOMServer.renderToString(#{element})`
82
+ end
83
+
84
+ def self.render_to_static_markup(element)
85
+ `ReactDOMServer.renderToStaticMarkup(#{element})`
86
+ end
87
+
88
+ def self.unmount_component_at_node(node)
89
+ `ReactDOM.unmountComponentAtNode(node)`
90
+ end
91
+
92
+ def self.expose_native_class(*args)
93
+ args.each do |klass|
94
+ `window[#{klass.to_s}] = #{React::ComponentFactory.native_component_class(klass)}`
95
+ end
96
+ end
97
+
98
+ def self.find_dom_node(component)
99
+ `ReactDOM.findDOMNode(#{component})`
100
+ end
101
+ end
@@ -0,0 +1,69 @@
1
+ module React
2
+ class Validator
3
+ def self.build(&block)
4
+ validator = self.new
5
+ validator.instance_eval(&block)
6
+ validator
7
+ end
8
+
9
+ def initialize
10
+ @rules = {}
11
+ end
12
+
13
+ def evaluate_more_rules(&block)
14
+ self.instance_eval(&block)
15
+ end
16
+
17
+ def requires(prop_name, options = {})
18
+ rule = options
19
+ options[:required] = true
20
+ @rules[prop_name] = options
21
+ end
22
+
23
+ def optional(prop_name, options = {})
24
+ rule = options
25
+ options[:required] = false
26
+ @rules[prop_name] = options
27
+ end
28
+
29
+ def validate(props)
30
+ errors = []
31
+ props.keys.each do |prop_name|
32
+ errors << "Provided prop `#{prop_name}` not specified in spec" if @rules[prop_name] == nil
33
+ end
34
+
35
+ props = props.select {|key| @rules.keys.include?(key) }
36
+
37
+ # requires or not
38
+ (@rules.keys - props.keys).each do |prop_name|
39
+ errors << "Required prop `#{prop_name}` was not specified" if @rules[prop_name][:required]
40
+ end
41
+
42
+ # type
43
+ props.each do |prop_name, value|
44
+ if klass = @rules[prop_name][:type]
45
+ if klass.is_a?(Array)
46
+ errors << "Provided prop `#{prop_name}` was not an Array of the specified type `#{klass[0]}`" unless value.all?{ |ele| ele.is_a?(klass[0]) }
47
+ else
48
+ errors << "Provided prop `#{prop_name}` was not the specified type `#{klass}`" unless value.is_a?(klass)
49
+ end
50
+ end
51
+ end
52
+
53
+ # values
54
+ props.each do |prop_name, value|
55
+ if values = @rules[prop_name][:values]
56
+ errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value" unless values.include?(value)
57
+ end
58
+ end
59
+
60
+ errors
61
+ end
62
+
63
+ def default_props
64
+ @rules
65
+ .select {|key, value| value.keys.include?("default") }
66
+ .inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module React
2
+ VERSION = '0.14.1'
3
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: react-opal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.14.1
5
+ platform: ruby
6
+ authors:
7
+ - Brady Wied
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-12 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.8.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: opal-activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.0
41
+ description: Write reactive UI component with Ruby's elegancy and compiled to run
42
+ in Javascript.
43
+ email: brady@bswtechconsulting.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/react-opal.rb
49
+ - lib/react/opal/callbacks.rb
50
+ - lib/react/opal/component.rb
51
+ - lib/react/opal/component/api.rb
52
+ - lib/react/opal/component_factory.rb
53
+ - lib/react/opal/event.rb
54
+ - lib/react/opal/ext/hash.rb
55
+ - lib/react/opal/native_element.rb
56
+ - lib/react/opal/props_children.rb
57
+ - lib/react/opal/testing.rb
58
+ - lib/react/opal/top_level.rb
59
+ - lib/react/opal/validator.rb
60
+ - lib/react/opal/version.rb
61
+ homepage: https://github.com/wied03/react-opal
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.6.1
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Opal Ruby wrapper of React.js library.
85
+ test_files: []