reactive-ruby 0.7.28 → 0.7.29

Sign up to get free protection for your applications and to get access to all the features.
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