reactive-ruby 0.7.28 → 0.7.29

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +6 -0
  3. data/Gemfile.lock +4 -1
  4. data/README.md +132 -68
  5. data/Rakefile +5 -2
  6. data/example/examples/Gemfile +0 -2
  7. data/example/rails-tutorial/Gemfile +3 -2
  8. data/lib/generators/reactive_ruby/test_app/templates/application.rb +11 -0
  9. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/application.rb +2 -0
  10. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
  11. data/lib/generators/reactive_ruby/test_app/templates/boot.rb +6 -0
  12. data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  13. data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  14. data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  15. data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +105 -0
  16. data/lib/rails-helpers/top_level_rails_component.rb +9 -16
  17. data/lib/{reactive-ruby → react}/api.rb +8 -65
  18. data/lib/{reactive-ruby → react}/callbacks.rb +0 -0
  19. data/lib/react/component.rb +266 -0
  20. data/lib/react/component/api.rb +48 -0
  21. data/lib/react/component/class_methods.rb +183 -0
  22. data/lib/{reactive-ruby → react}/element.rb +10 -11
  23. data/lib/{reactive-ruby → react}/event.rb +0 -0
  24. data/lib/{reactive-ruby → react}/ext/hash.rb +0 -0
  25. data/lib/{reactive-ruby → react}/ext/string.rb +0 -0
  26. data/lib/react/native_library.rb +57 -0
  27. data/lib/{reactive-ruby → react}/observable.rb +0 -4
  28. data/lib/{reactive-ruby → react}/rendering_context.rb +0 -6
  29. data/lib/{reactive-ruby → react}/state.rb +3 -6
  30. data/lib/{reactive-ruby → react}/top_level.rb +2 -3
  31. data/lib/react/validator.rb +127 -0
  32. data/lib/reactive-ruby.rb +20 -20
  33. data/lib/reactive-ruby/version.rb +1 -1
  34. data/{opal-spec/reactjs → spec}/index.html.erb +1 -1
  35. data/{opal-spec → spec/react}/callbacks_spec.rb +8 -9
  36. data/{opal-spec → spec/react}/component_spec.rb +170 -120
  37. data/spec/react/dsl_spec.rb +16 -0
  38. data/{opal-spec → spec/react}/element_spec.rb +7 -20
  39. data/{opal-spec → spec/react}/event_spec.rb +3 -1
  40. data/spec/react/native_library_spec.rb +10 -0
  41. data/spec/react/param_declaration_spec.rb +18 -0
  42. data/{opal-spec → spec/react}/react_spec.rb +3 -2
  43. data/spec/react/react_state_spec.rb +22 -0
  44. data/spec/react/top_level_component_spec.rb +68 -0
  45. data/{opal-spec → spec/react}/tutorial/tutorial_spec.rb +11 -13
  46. data/{opal-spec → spec/react}/validator_spec.rb +50 -4
  47. data/spec/reactive-ruby/isomorphic_helpers_spec.rb +22 -4
  48. data/spec/spec_helper.rb +51 -0
  49. data/spec/support/react/spec_helpers.rb +57 -0
  50. data/spec/vendor/es5-shim.min.js +6 -0
  51. metadata +56 -24
  52. data/lib/reactive-ruby/component.rb +0 -502
  53. data/lib/reactive-ruby/validator.rb +0 -99
  54. data/old-readme +0 -220
  55. data/opal-spec/spec_helper.rb +0 -29
@@ -1,99 +0,0 @@
1
- module React
2
- class Validator
3
-
4
- def self.build(&block)
5
- self.new.build(&block)
6
- end
7
-
8
- def build(&block)
9
- instance_eval(&block)
10
- self
11
- end
12
-
13
- def initialize
14
- @rules = {children: {required: false}}
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 all_others(prop_name)
30
- @all_others = {}
31
- end
32
-
33
- def collect_all_others(params)
34
- Hash[params.collect { |prop_name, value| [prop_name, value] if @rules[prop_name] == nil}.compact]
35
- end
36
-
37
- def type_check(errors, error_prefix, object, klass, nil_allowed)
38
- return if !object and nil_allowed
39
- is_native = !object.respond_to?(:is_a?) rescue true
40
- if is_native or !object.is_a? klass
41
- unless klass.respond_to? :_react_param_conversion and klass._react_param_conversion(object, :validate_only)
42
- errors << "#{error_prefix} could not be converted to #{klass}"
43
- end
44
- end
45
- end
46
-
47
- def validate(props)
48
- errors = []
49
-
50
- if @all_others
51
- props.each do |prop_name, value|
52
- @all_others[prop_name] = value if @rules[prop_name] == nil
53
- end
54
- else
55
- props.keys.each do |prop_name|
56
- errors << "Provided prop `#{prop_name}` not specified in spec" if @rules[prop_name] == nil
57
- end
58
- end
59
-
60
- props = props.select {|key| @rules.keys.include?(key) }
61
-
62
- # requires or not
63
- (@rules.keys - props.keys).each do |prop_name|
64
- errors << "Required prop `#{prop_name}` was not specified" if @rules[prop_name][:required]
65
- end
66
- # type checking
67
- props.each do |prop_name, value|
68
- if klass = @rules[prop_name][:type]
69
- is_klass_array = klass.is_a?(Array) and klass.length > 0 rescue nil
70
- if is_klass_array
71
- value_is_array_like = value.respond_to?(:each_with_index) rescue nil
72
- if value_is_array_like
73
- value.each_with_index { |ele, i| type_check(errors, "Provided prop `#{prop_name}`[#{i}]", ele, klass[0], @rules[prop_name][:allow_nil]) }
74
- else
75
- errors << "Provided prop `#{prop_name}` was not an Array"
76
- end
77
- else
78
- type_check(errors, "Provided prop `#{prop_name}`", value, klass, @rules[prop_name][:allow_nil])
79
- end
80
- end
81
- end
82
-
83
- # values
84
- props.each do |prop_name, value|
85
- if values = @rules[prop_name][:values]
86
- errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value" unless values.include?(value)
87
- end
88
- end
89
-
90
- errors
91
- end
92
-
93
- def default_props
94
- @rules
95
- .select {|key, value| value.keys.include?("default") }
96
- .inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
97
- end
98
- end
99
- end
data/old-readme DELETED
@@ -1,220 +0,0 @@
1
- and in your Opal application,
2
-
3
- ```ruby
4
- require "opal-react"
5
- require "react"
6
-
7
- React.render(React.create_element('h1'){ "Hello World!" }, `document.body`)
8
- ```
9
-
10
- For a complete example covering most key features, as well as integration with a server (Sinatra, etc), see setup of [Examples](example/tutorial). For additional information on integrating Opal with a server see the [official docs](http://opalrb.org/docs/) of Opal.
11
-
12
- ## React Overview
13
-
14
- ### Basics
15
-
16
- The biggest problem with react is that its almost too simple.
17
-
18
- In react you define components. Components are simply classes that have a "render" method. The render method "draws" a chunk of
19
- HTML.
20
-
21
- Here is a very simple component:
22
-
23
- ```ruby
24
-
25
- require 'opal'
26
- require 'opal-react'
27
-
28
- class Hello
29
- def render
30
- "hello world"
31
- end
32
- end
33
-
34
- # to use the component we first create an instance o
35
-
36
- Include the `React::Component` mixin in a class to turn it into a react component
37
-
38
- ```ruby
39
- require 'opal'
40
- require 'opal-react'
41
-
42
- class HelloMessage
43
-
44
- include React::Component # will create a new component named HelloMessage
45
-
46
- MSG = {great: 'Cool!', bad: 'Cheer up!'}
47
-
48
- optional_param :mood
49
- required_param :name
50
- define_state :foo, "Default greeting"
51
-
52
- before_mount do # you can define life cycle callbacks inline
53
- foo! "#{name}: #{MSG[mood]}" if mood # change the state of foo using foo!, read the state using foo
54
- end
55
-
56
- after_mount :log # you can also define life cycle callbacks by reference to a method
57
-
58
- def log
59
- puts "mounted!"
60
- end
61
-
62
- def render # render method MUST return just one component
63
- div do # basic dsl syntax component_name(options) { ...children... }
64
- span { "#{foo} #{name}!" } # all html5 components are defined with lower case text
65
- end
66
- end
67
- end
68
-
69
- class App
70
- include React::Component
71
-
72
- def render
73
- HelloMessage name: 'John', mood: :great # new components are accessed via the class name
74
- end
75
- end
76
-
77
- # later we will talk about nicer ways to do this: For now wait till doc is loaded
78
- # then tell React to create an "App" and render it into the document body.
79
-
80
- `window.onload = #{lambda {React.render(React.create_element(App), `document.body`)}}`
81
-
82
- # -> console says: mounted!
83
- ```
84
-
85
- * Callback of life cycle could be created through helpers `before_mount`, `after_mount`, etc
86
- * `this.props` is accessed through method `self.params`
87
- * Use helper method `define_state` to create setter & getter of `this.state` for you
88
- * For the detailed mapping to the original API, see [this issue](https://github.com/zetachang/react.rb/issues/2) for reference. Complete reference will come soon.
89
-
90
- ### Element Building DSL
91
-
92
- As a replacement of JSX, include `React::Component` and you can build `React.Element` hierarchy without all the `React.create_element` noises.
93
-
94
- ```ruby
95
- def render
96
- div do
97
- h1 { "Title" }
98
- h2 { "subtitle"}
99
- div(class_name: 'fancy', id: 'foo') { span { "some text #{interpolation}"} }
100
- present FancyElement, fancy_props: '10'
101
- end
102
- end
103
- ```
104
-
105
- ### Props validation
106
-
107
- How about props validation? Inspired by [Grape API](https://github.com/intridea/grape), props validation rule could be created easily through `params` class method as below,
108
-
109
- ```ruby
110
- class App
111
- include React::Component
112
-
113
- params do
114
- requires :username, type: String
115
- requires :enum, values: ['foo', 'bar', 'awesome']
116
- requires :payload, type: Todo # yeah, a plain Ruby class
117
- optional :filters, type: Array[String]
118
- optional :flash_message, type: String, default: 'Welcome!' # no need to feed through `getDefaultProps`
119
- end
120
-
121
- def render
122
- div
123
- end
124
- end
125
- ```
126
-
127
- ### Mixins
128
-
129
- Simply create a Ruby module to encapsulate the behavior. Example below is modified from the original [React.js Exmaple on Mixin](http://facebook.github.io/react/docs/reusable-components.html#mixins). [Opal Browser](https://github.com/opal/opal-browser) syntax are used here to make it cleaner.
130
-
131
- ```ruby
132
- module SetInterval
133
- def self.included(base)
134
- base.class_eval do
135
- before_mount { @interval = [] }
136
- before_unmount do
137
- # abort associated timer of a component right before unmount
138
- @interval.each { |i| i.abort }
139
- end
140
- end
141
- end
142
-
143
- def set_interval(seconds, &block)
144
- @interval << $window.every(seconds, &block)
145
- end
146
- end
147
-
148
- class TickTock
149
- include React::Component
150
- include SetInterval
151
-
152
- define_state(:seconds) { 0 }
153
-
154
- before_mount do
155
- set_interval(1) { self.seconds = self.seconds + 1 }
156
- set_interval(1) { puts "Tick!" }
157
- end
158
-
159
- def render
160
- span do
161
- "React has been running for: #{self.seconds}"
162
- end
163
- end
164
- end
165
-
166
- React.render(React.create_element(TickTock), $document.body.to_n)
167
-
168
- $window.after(5) do
169
- React.unmount_component_at_node($document.body.to_n)
170
- end
171
-
172
- # => Tick!
173
- # => ... for 5 times then stop ticking after 5 seconds
174
- ```
175
-
176
-
177
- ### A Simple Component
178
-
179
- A ruby class which define method `render` is a valid component.
180
-
181
- ```ruby
182
- class HelloMessage
183
- def render
184
- React.create_element("div") { "Hello World!" }
185
- end
186
- end
187
-
188
- puts React.render_to_static_markup(React.create_element(HelloMessage))
189
-
190
- # => '<div>Hello World!</div>'
191
- ```
192
-
193
- ### More complicated one
194
-
195
- To hook into native ReactComponent life cycle, the native `this` will be passed to the class's initializer. And all corresponding life cycle methods (`componentDidMount`, etc) will be invoked on the instance using the snake-case method name.
196
-
197
- ```ruby
198
- class HelloMessage
199
- def initialize(native)
200
- @native = Native(native)
201
- end
202
-
203
- def component_will_mount
204
- puts "will mount!"
205
- end
206
-
207
- def render
208
- React.create_element("div") { "Hello #{@native[:props][:name]}!" }
209
- end
210
- end
211
-
212
- puts React.render_to_static_markup(React.create_element(HelloMessage, name: 'John'))
213
-
214
- # => will_mount!
215
- # => '<div>Hello John!</div>'
216
- ```
217
- ## Example
218
-
219
- * React Tutorial: see [example/react-tutorial](example/react-tutorial), the original CommentBox example.
220
- * TodoMVC: see [example/todos](example/todos), your beloved TodoMVC <3.
@@ -1,29 +0,0 @@
1
- require 'react'
2
-
3
- module ReactTestHelpers
4
- `var ReactTestUtils = React.addons.TestUtils`
5
-
6
- def renderToDocument(type, options = {})
7
- element = React.create_element(type, options)
8
- return renderElementToDocument(element)
9
- end
10
-
11
- def renderElementToDocument(element)
12
- instance = Native(`ReactTestUtils.renderIntoDocument(#{element.to_n})`)
13
- instance.class.include(React::Component::API)
14
- return instance
15
- end
16
-
17
- def simulateEvent(event, element, params = {})
18
- simulator = Native(`ReactTestUtils.Simulate`)
19
- simulator[event.to_s].call(`#{element.to_n}.getDOMNode()`, params)
20
- end
21
-
22
- def isElementOfType(element, type)
23
- `React.addons.TestUtils.isElementOfType(#{element.to_n}, #{type.cached_component_class})`
24
- end
25
- end
26
-
27
- RSpec.configure do |config|
28
- config.include ReactTestHelpers
29
- end