reactive-ruby 0.7.3

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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +30 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +53 -0
  5. data/LICENSE +19 -0
  6. data/README.md +303 -0
  7. data/config.ru +15 -0
  8. data/example/examples/Gemfile +7 -0
  9. data/example/examples/Gemfile.lock +45 -0
  10. data/example/examples/config.ru +44 -0
  11. data/example/examples/hello.js.rb +43 -0
  12. data/example/react-tutorial/Gemfile +7 -0
  13. data/example/react-tutorial/Gemfile.lock +49 -0
  14. data/example/react-tutorial/README.md +8 -0
  15. data/example/react-tutorial/_comments.json +14 -0
  16. data/example/react-tutorial/config.ru +63 -0
  17. data/example/react-tutorial/example.js.rb +290 -0
  18. data/example/react-tutorial/public/base.css +62 -0
  19. data/example/todos/Gemfile +11 -0
  20. data/example/todos/Gemfile.lock +84 -0
  21. data/example/todos/README.md +37 -0
  22. data/example/todos/Rakefile +8 -0
  23. data/example/todos/app/application.rb +22 -0
  24. data/example/todos/app/components/app.react.rb +61 -0
  25. data/example/todos/app/components/footer.react.rb +31 -0
  26. data/example/todos/app/components/todo_item.react.rb +46 -0
  27. data/example/todos/app/components/todo_list.react.rb +25 -0
  28. data/example/todos/app/models/todo.rb +19 -0
  29. data/example/todos/config.ru +14 -0
  30. data/example/todos/index.html.haml +16 -0
  31. data/example/todos/spec/todo_spec.rb +28 -0
  32. data/example/todos/vendor/base.css +410 -0
  33. data/example/todos/vendor/bg.png +0 -0
  34. data/example/todos/vendor/jquery.js +4 -0
  35. data/lib/rails-helpers/react_component.rb +32 -0
  36. data/lib/reactive-ruby.rb +23 -0
  37. data/lib/reactive-ruby/api.rb +177 -0
  38. data/lib/reactive-ruby/callbacks.rb +35 -0
  39. data/lib/reactive-ruby/component.rb +411 -0
  40. data/lib/reactive-ruby/element.rb +87 -0
  41. data/lib/reactive-ruby/event.rb +76 -0
  42. data/lib/reactive-ruby/ext/hash.rb +9 -0
  43. data/lib/reactive-ruby/ext/string.rb +8 -0
  44. data/lib/reactive-ruby/isomorphic_helpers.rb +223 -0
  45. data/lib/reactive-ruby/observable.rb +33 -0
  46. data/lib/reactive-ruby/rendering_context.rb +91 -0
  47. data/lib/reactive-ruby/serializers.rb +15 -0
  48. data/lib/reactive-ruby/state.rb +90 -0
  49. data/lib/reactive-ruby/top_level.rb +53 -0
  50. data/lib/reactive-ruby/validator.rb +83 -0
  51. data/lib/reactive-ruby/version.rb +3 -0
  52. data/logo1.png +0 -0
  53. data/logo2.png +0 -0
  54. data/logo3.png +0 -0
  55. data/reactive-ruby.gemspec +25 -0
  56. data/spec/callbacks_spec.rb +107 -0
  57. data/spec/component_spec.rb +597 -0
  58. data/spec/element_spec.rb +60 -0
  59. data/spec/event_spec.rb +22 -0
  60. data/spec/react_spec.rb +209 -0
  61. data/spec/reactjs/index.html.erb +11 -0
  62. data/spec/spec_helper.rb +29 -0
  63. data/spec/tutorial/tutorial_spec.rb +37 -0
  64. data/spec/validator_spec.rb +79 -0
  65. data/vendor/active_support/core_ext/array/extract_options.rb +29 -0
  66. data/vendor/active_support/core_ext/class/attribute.rb +127 -0
  67. data/vendor/active_support/core_ext/kernel/singleton_class.rb +13 -0
  68. data/vendor/active_support/core_ext/module/remove_method.rb +11 -0
  69. metadata +205 -0
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+
3
+ describe React::Element do
4
+ it "should bridge `type` of native React.Element attributes" do
5
+ element = React.create_element('div')
6
+ expect(element.element_type).to eq("div")
7
+ end
8
+
9
+ async "should be renderable" do
10
+ element = React.create_element('span')
11
+ div = `document.createElement("div")`
12
+ React.render(element, div) do
13
+ run_async {
14
+ expect(`div.children[0].tagName`).to eq("SPAN")
15
+ }
16
+ end
17
+ end
18
+
19
+ describe "Event subscription" do
20
+ it "should be subscribable through `on(:event_name)` method" do
21
+ expect { |b|
22
+ element = React.create_element("div").on(:click, &b)
23
+ instance = renderElementToDocument(element)
24
+ simulateEvent(:click, instance)
25
+ }.to yield_with_args(React::Event)
26
+
27
+ expect { |b|
28
+ element = React.create_element("div").on(:key_down, &b)
29
+ instance = renderElementToDocument(element)
30
+ simulateEvent(:keyDown, instance, {key: "Enter"})
31
+ }.to yield_control
32
+
33
+ expect { |b|
34
+ element = React.create_element("form").on(:submit, &b)
35
+ instance = renderElementToDocument(element)
36
+ simulateEvent(:submit, instance, {})
37
+ }.to yield_control
38
+ end
39
+
40
+ it "should return self for `on` method" do
41
+ element = React.create_element("div")
42
+ expect(element.on(:click){}).to eq(element)
43
+ end
44
+ end
45
+
46
+ describe "Children" do
47
+ it "should return a Enumerable" do
48
+ ele = React.create_element('div') { [React.create_element('a'), React.create_element('li')] }
49
+ nodes = ele.children.map {|ele| ele.element_type }
50
+ expect(nodes).to eq(["a", "li"])
51
+ end
52
+
53
+ it "should return a Enumerator when not providing a block" do
54
+ ele = React.create_element('div') { [React.create_element('a'), React.create_element('li')] }
55
+ nodes = ele.children.each
56
+ expect(nodes).to be_a(Enumerator)
57
+ expect(nodes.size).to eq(2)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ describe React::Event do
4
+ it "should bridge attributes of native SyntheticEvent (see http://facebook.github.io/react/docs/events.html#syntheticevent)" do
5
+ element = React.create_element('div').on(:click) do |event|
6
+ expect(event.bubbles).to eq(`#{event.to_n}.bubbles`)
7
+ expect(event.cancelable).to eq(`#{event.to_n}.cancelable`)
8
+ expect(event.current_target).to eq(`#{event.to_n}.currentTarget`)
9
+ expect(event.default_prevented).to eq(`#{event.to_n}.defaultPrevented`)
10
+ expect(event.event_phase).to eq(`#{event.to_n}.eventPhase`)
11
+ expect(event.is_trusted?).to eq(`#{event.to_n}.isTrusted`)
12
+ expect(event.native_event).to eq(`#{event.to_n}.nativeEvent`)
13
+ expect(event.target).to eq(`#{event.to_n}.target`)
14
+ expect(event.timestamp).to eq(`#{event.to_n}.timeStamp`)
15
+ expect(event.event_type).to eq(`#{event.to_n}.type`)
16
+ expect(event).to respond_to(:prevent_default)
17
+ expect(event).to respond_to(:stop_propagation)
18
+ end
19
+ instance = renderElementToDocument(element)
20
+ simulateEvent(:click, instance)
21
+ end
22
+ end
@@ -0,0 +1,209 @@
1
+ require "spec_helper"
2
+
3
+ describe React do
4
+ after(:each) do
5
+ React::API.clear_component_class_cache
6
+ end
7
+
8
+ describe "is_valid_element" do
9
+ it "should return true if passed a valid element" do
10
+ element = React::Element.new(`React.createElement('div')`)
11
+ expect(React.is_valid_element(element)).to eq(true)
12
+ end
13
+
14
+ it "should return false is passed a non React element" do
15
+ element = React::Element.new(`{}`)
16
+ expect(React.is_valid_element(element)).to eq(false)
17
+ end
18
+ end
19
+
20
+ describe "create_element" do
21
+ it "should create a valid element with only tag" do
22
+ element = React.create_element('div')
23
+ expect(React.is_valid_element(element)).to eq(true)
24
+ end
25
+
26
+ context "with block" do
27
+ it "should create a valid element with text as only child when block yield String" do
28
+ element = React.create_element('div') { "lorem ipsum" }
29
+ expect(React.is_valid_element(element)).to eq(true)
30
+ expect(element.props.children).to eq("lorem ipsum")
31
+ end
32
+
33
+ it "should create a valid element with children as array when block yield Array of element" do
34
+ element = React.create_element('div') do
35
+ [React.create_element('span'), React.create_element('span'), React.create_element('span')]
36
+ end
37
+ expect(React.is_valid_element(element)).to eq(true)
38
+ expect(element.props.children.length).to eq(3)
39
+ end
40
+
41
+ it "should render element with children as array when block yield Array of element" do
42
+ element = React.create_element('div') do
43
+ [React.create_element('span'), React.create_element('span'), React.create_element('span')]
44
+ end
45
+ instance = renderElementToDocument(element)
46
+ expect(instance.getDOMNode.childNodes.length).to eq(3)
47
+ end
48
+ end
49
+ describe "custom element" do
50
+ before do
51
+ stub_const 'Foo', Class.new
52
+ Foo.class_eval do
53
+ def render
54
+ React.create_element("div") { "lorem" }
55
+ end
56
+ end
57
+ end
58
+
59
+ it "should render element with only one children correctly" do
60
+ element = React.create_element(Foo) { React.create_element('span') }
61
+ instance = renderElementToDocument(element)
62
+ expect(instance.props.children).not_to be_a(Array)
63
+ expect(instance.props.children.type).to eq("span")
64
+ end
65
+
66
+ it "should render element with more than one children correctly" do
67
+ element = React.create_element(Foo) { [React.create_element('span'), React.create_element('span')] }
68
+ instance = renderElementToDocument(element)
69
+ expect(instance.props.children).to be_a(Array)
70
+ expect(instance.props.children.length).to eq(2)
71
+ end
72
+
73
+ it "should create a valid element provided class defined `render`" do
74
+ element = React.create_element(Foo)
75
+ expect(React.is_valid_element(element)).to eq(true)
76
+ end
77
+
78
+ it "should allow creating with properties" do
79
+ element = React.create_element(Foo, foo: "bar")
80
+ expect(element.props.foo).to eq("bar")
81
+ end
82
+
83
+ it "should raise error if provided class doesn't defined `render`" do
84
+ expect { React.create_element(Array) }.to raise_error
85
+ end
86
+
87
+ it "should use the same instance for the same ReactComponent" do
88
+ Foo.class_eval do
89
+ attr_accessor :a
90
+ def initialize(n)
91
+ self.a = 10
92
+ end
93
+
94
+ def component_will_mount
95
+ self.a = 20
96
+ end
97
+
98
+ def render
99
+ React.create_element("div") { self.a.to_s }
100
+ end
101
+ end
102
+
103
+ expect(React.render_to_static_markup(React.create_element(Foo))).to eq("<div>20</div>")
104
+ end
105
+
106
+ it "should match the instance cycle to ReactComponent life cycle" do
107
+ `var count = 0;`
108
+
109
+ Foo.class_eval do
110
+ def initialize
111
+ `count = count + 1;`
112
+ end
113
+ def render
114
+ React.create_element("div")
115
+ end
116
+ end
117
+
118
+ renderToDocument(Foo)
119
+ renderToDocument(Foo)
120
+
121
+ expect(`count`).to eq(2)
122
+ end
123
+ end
124
+
125
+ describe "create element with properties" do
126
+ it "should enforce snake-cased property name" do
127
+ element = React.create_element("div", class_name: "foo")
128
+ expect(element.props.className).to eq("foo")
129
+ end
130
+
131
+ it "should allow custom property" do
132
+ element = React.create_element("div", foo: "bar")
133
+ expect(element.props.foo).to eq("bar")
134
+ end
135
+
136
+ it "should not camel-case custom property" do
137
+ element = React.create_element("div", foo_bar: "foo")
138
+ expect(element.props.foo_bar).to eq("foo")
139
+ end
140
+ end
141
+
142
+ describe "class_name helpers (React.addons.classSet)" do
143
+ it "should transform Hash provided to `class_name` props as string" do
144
+ classes = {foo: true, bar: false, lorem: true}
145
+ element = React.create_element("div", class_name: classes)
146
+
147
+ expect(element.props.className).to eq("foo lorem")
148
+ end
149
+
150
+ it "should not alter behavior when passing a string" do
151
+ element = React.create_element("div", class_name: "foo bar")
152
+
153
+ expect(element.props.className).to eq("foo bar")
154
+ end
155
+ end
156
+ end
157
+
158
+
159
+ describe "render" do
160
+ async "should render element to DOM" do
161
+ div = `document.createElement("div")`
162
+ React.render(React.create_element('span') { "lorem" }, div) do
163
+ run_async {
164
+ expect(`div.children[0].tagName`).to eq("SPAN")
165
+ expect(`div.textContent`).to eq("lorem")
166
+ }
167
+ end
168
+ end
169
+
170
+ it "should work without providing a block" do
171
+ div = `document.createElement("div")`
172
+ React.render(React.create_element('span') { "lorem" }, div)
173
+ end
174
+
175
+ it "should return a React::Component::API compatible object" do
176
+ div = `document.createElement("div")`
177
+ component = React.render(React.create_element('span') { "lorem" }, div)
178
+ React::Component::API.public_instance_methods(true).each do |method_name|
179
+ expect(component).to respond_to(method_name)
180
+ end
181
+ end
182
+
183
+ pending "should return nil to prevent abstraction leakage" do
184
+ div = `document.createElement("div")`
185
+ expect {
186
+ React.render(React.create_element('span') { "lorem" }, div)
187
+ }.to be_nil
188
+ end
189
+ end
190
+
191
+ describe "render_to_string" do
192
+ it "should render a React.Element to string" do
193
+ ele = React.create_element('span') { "lorem" }
194
+ expect(React.render_to_string(ele)).to be_kind_of(String)
195
+ end
196
+ end
197
+
198
+ describe "unmount_component_at_node" do
199
+ async "should unmount component at node" do
200
+ div = `document.createElement("div")`
201
+ React.render(React.create_element('span') { "lorem" }, div ) do
202
+ run_async {
203
+ expect(React.unmount_component_at_node(div)).to eq(true)
204
+ }
205
+ end
206
+ end
207
+ end
208
+
209
+ end
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ </head>
5
+ <body>
6
+ <%= javascript_include_tag 'react-with-addons' %>
7
+ <%= javascript_include_tag @server.main %>
8
+ <div id="placeholder" style="display: none"></div>
9
+ <div id="render_here"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,29 @@
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
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ class HelloMessage
4
+ include React::Component
5
+ def render
6
+ div { "Hello World!" }
7
+ end
8
+ end
9
+
10
+ describe "An Example from the react.rb doc" do
11
+
12
+ it "produces the correct result" do
13
+ expect(React.render_to_static_markup(React.create_element(HelloMessage))).to eq('<div>Hello World!</div>')
14
+ end
15
+
16
+ end
17
+
18
+ class HelloMessage2
19
+ include React::Component
20
+ define_state(:user_name) { '@catmando' }
21
+ def render
22
+ div { "Hello #{user_name}" }
23
+ end
24
+ end
25
+
26
+ describe "Adding state to a component (second tutorial example)" do
27
+
28
+ it "produces the correct result" do
29
+ expect(React.render_to_static_markup(React.create_element(HelloMessage2))).to eq('<div>Hello @catmando</div>')
30
+ end
31
+
32
+ it "renders to the document" do
33
+ React.render(React.create_element(HelloMessage2), `document.getElementById('render_here')`)
34
+ expect(`document.getElementById('render_here').innerHTML`) =~ 'Hello @catmando'
35
+ end
36
+
37
+ end
@@ -0,0 +1,79 @@
1
+ require "spec_helper"
2
+
3
+ describe React::Validator do
4
+ describe "validate" do
5
+ describe "Presence validation" do
6
+ it "should check if required props provided" do
7
+ validator = React::Validator.build do
8
+ requires :foo
9
+ requires :bar
10
+ end
11
+
12
+ expect(validator.validate({})).to eq(["Required prop `foo` was not specified", "Required prop `bar` was not specified"])
13
+ expect(validator.validate({foo: 1, bar: 3})).to eq([])
14
+ end
15
+
16
+ it "should check if passed non specified prop" do
17
+ validator = React::Validator.build do
18
+ optional :foo
19
+ end
20
+
21
+ expect(validator.validate({bar: 10})).to eq(["Provided prop `bar` not specified in spec"])
22
+ expect(validator.validate({foo: 10})).to eq([])
23
+ end
24
+ end
25
+
26
+ describe "Type validation" do
27
+ it "should check if passed value with wrong type" do
28
+ validator = React::Validator.build do
29
+ requires :foo, type: String
30
+ end
31
+
32
+ expect(validator.validate({foo: 10})).to eq(["Provided prop `foo` was not the specified type `String`"])
33
+ expect(validator.validate({foo: "10"})).to eq([])
34
+ end
35
+
36
+ it "should check if passed value with wrong custom type" do
37
+ stub_const 'Bar', Class.new
38
+ validator = React::Validator.build do
39
+ requires :foo, type: Bar
40
+ end
41
+
42
+ expect(validator.validate({foo: 10})).to eq(["Provided prop `foo` was not the specified type `Bar`"])
43
+ expect(validator.validate({foo: Bar.new})).to eq([])
44
+ end
45
+
46
+ it "should support Array[Class] validation" do
47
+ validator = React::Validator.build do
48
+ requires :foo, type: Array[Hash]
49
+ end
50
+
51
+ expect(validator.validate({foo: [1,'2',3]})).to eq(["Provided prop `foo` was not an Array of the specified type `Hash`"])
52
+ expect(validator.validate({foo: [{},{},{}]})).to eq([])
53
+ end
54
+ end
55
+
56
+ describe "Limited values" do
57
+ it "should check if passed value is not one of the specified values" do
58
+ validator = React::Validator.build do
59
+ requires :foo, values: [4,5,6]
60
+ end
61
+
62
+ expect(validator.validate({foo: 3})).to eq(["Value `3` for prop `foo` is not an allowed value"])
63
+ expect(validator.validate({foo: 4})).to eq([])
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "default_props" do
69
+ it "should return specified default values" do
70
+ validator = React::Validator.build do
71
+ requires :foo, default: 10
72
+ requires :bar
73
+ optional :lorem, default: 20
74
+ end
75
+
76
+ expect(validator.default_props).to eq({foo: 10, lorem: 20})
77
+ end
78
+ end
79
+ end