reactive-ruby 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4e9023f2e2533170c453fff7af6571e8c95976b9
4
+ data.tar.gz: 74e2553bd21a054c5674f46f1ededa4d7b339093
5
+ SHA512:
6
+ metadata.gz: 341472a072828b2af26cb27ddfe4dfb2ef85dc1dea31af2df3a2fcdab8fc362a7d4b438afe605f89f71d20c5dbe4e2280e1e56c0b03f469f825b8945309530cf
7
+ data.tar.gz: 91e7a1556c9a53c5576c17194d0b416bbae63781e7180a802cfccec4fb8cf8ba409eed68ae208cc79ce83e1b76f6d52d6ba79bb8ecafc6d349682c29a2b339f4
data/.gitignore ADDED
@@ -0,0 +1,30 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+
5
+ # Runtime data
6
+ pids
7
+ *.pid
8
+ *.seed
9
+
10
+ # Directory for instrumented libs generated by jscoverage/JSCover
11
+ lib-cov
12
+
13
+ # Coverage directory used by tools like istanbul
14
+ coverage
15
+
16
+ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17
+ .grunt
18
+
19
+ # node-waf configuration
20
+ .lock-wscript
21
+
22
+ # Compiled binary addons (http://nodejs.org/api/addons.html)
23
+ build/Release
24
+
25
+ # Dependency directory
26
+ # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
27
+ node_modules
28
+
29
+ # Ignore bundler config.
30
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem 'opal', :git => "https://github.com/catprintlabs/opal.git"
data/Gemfile.lock ADDED
@@ -0,0 +1,53 @@
1
+ GIT
2
+ remote: https://github.com/catprintlabs/opal.git
3
+ revision: 3682db6f2b23a584787a68d5338a7ee9cb35565a
4
+ specs:
5
+ opal (0.7.1)
6
+ hike (~> 1.2)
7
+ sourcemap (~> 0.1.0)
8
+ sprockets (>= 2.2.3, < 4.0.0)
9
+ tilt (~> 1.4)
10
+
11
+ PATH
12
+ remote: .
13
+ specs:
14
+ reactive-ruby (0.7.3)
15
+ opal
16
+ opal-activesupport
17
+
18
+ GEM
19
+ remote: https://rubygems.org/
20
+ specs:
21
+ hike (1.2.3)
22
+ opal-activesupport (0.1.0)
23
+ opal (>= 0.5.0, < 1.0.0)
24
+ opal-jquery (0.4.0)
25
+ opal (>= 0.7.0, < 0.9.0)
26
+ opal-rspec (0.4.3)
27
+ opal (>= 0.7.0, < 0.9)
28
+ rack (1.6.4)
29
+ rack-protection (1.5.3)
30
+ rack
31
+ react-source (0.13.3)
32
+ sinatra (1.4.6)
33
+ rack (~> 1.4)
34
+ rack-protection (~> 1.4)
35
+ tilt (>= 1.3, < 3)
36
+ sourcemap (0.1.1)
37
+ sprockets (3.3.2)
38
+ rack (~> 1.0)
39
+ tilt (1.4.1)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ opal!
46
+ opal-jquery
47
+ opal-rspec
48
+ react-source (~> 0.12)
49
+ reactive-ruby!
50
+ sinatra
51
+
52
+ BUNDLED WITH
53
+ 1.10.6
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015 Yi-Cheng Chang (http://github.com/zetachang)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # React.rb
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/react.rb.svg)](http://badge.fury.io/rb/react.rb)
4
+ [![Code Climate](https://codeclimate.com/github/zetachang/react.rb/badges/gpa.svg)](https://codeclimate.com/github/zetachang/react.rb)
5
+
6
+ **React.rb is an [Opal Ruby](http://opalrb.org) wrapper of [React.js library](http://facebook.github.io/react/)**.
7
+
8
+ It lets you write reactive UI components, with Ruby's elegance and compiled to run in JavaScript. :heart:
9
+
10
+ ## Installation
11
+
12
+ Currently this branch (0.8 in catprint labs) is being used with the following configuration.
13
+ It is suggested to begin with this
14
+ set of gems which is known to work and then add / remove them as needed. Let us know if you discover anything.
15
+
16
+ Currently this has been tested to some extent with rails 3x and 4x
17
+
18
+ ```ruby
19
+ gem 'react-rails', git: "https://github.com/catprintlabs/react-rails.git", :branch => 'isomorphic-methods-support' # you need this branch of react-rails
20
+ gem 'opal', git: "https://github.com/catprintlabs/opal.git"
21
+ gem 'opal-jquery', git: "https://github.com/catprintlabs/opal-jquery.git" # optional if you are using jquery
22
+ gem 'opal-rails' # not sure if you need this in all cases
23
+ gem 'opal-browser' # gets you things like intervals (every method)
24
+ gem 'opal-react', git: "https://github.com/catprintlabs/react.rb.git", :branch => 'opal-0.8'
25
+ gem 'reactive_record', git: "https://github.com/catprintlabs/reactive-record.git", :branch => 'rails4' # if you want to interface to active_record
26
+ gem 'react-bootstrap-rails' # if you want to use boot strap styles
27
+ gem 'react-router-rails', '~>0.13.3' # if you want a single page app router
28
+ gem 'reactor-router', git: "https://github.com/catprintlabs/reactor-router.git" # same as above
29
+ ```
30
+
31
+ In addition
32
+
33
+ ```ruby
34
+ # Gemfile
35
+ # gem 'react-rails', git: "https://github.com/catprintlabs/react-rails.git" # include if you want integration with rails
36
+ gem 'opal'
37
+
38
+ # gem 'opal', git: "https://github.com/catprintlabs/opal.git" # use this if you are stuck on rails 3.x
39
+ # gem 'opal-jquery', git: "https://github.com/catprintlabs/opal-jquery.git" # same as above
40
+
41
+ # include if you want integration with rails
42
+ # gem 'opal-rails'
43
+
44
+ # while not absolutely necessary you will probably want this at least for timers and such
45
+ # gem 'opal-browser'
46
+
47
+ gem 'opal-react', git: "https://github.com/catprintlabs/react.rb.git", :branch => 'opal-0.8'
48
+
49
+ # access active record models from opal!
50
+ # gem 'reactive_record', git: "https://github.com/catprintlabs/reactive-record.git"
51
+
52
+ # include if you want to use bootstrap
53
+ # gem 'react-bootstrap-rails'
54
+ ```
55
+
56
+ and in your Opal application,
57
+
58
+ ```ruby
59
+ require "opal-react"
60
+ require "react"
61
+
62
+ React.render(React.create_element('h1'){ "Hello World!" }, `document.body`)
63
+ ```
64
+
65
+ 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.
66
+
67
+ ## React Overview
68
+
69
+ ### Basics
70
+
71
+ The biggest problem with react is that its almost too simple.
72
+
73
+ In react you define components. Components are simply classes that have a "render" method. The render method "draws" a chunk of
74
+ HTML.
75
+
76
+ Here is a very simple component:
77
+
78
+ ```ruby
79
+
80
+ require 'opal'
81
+ require 'opal-react'
82
+
83
+ class Hello
84
+ def render
85
+ "hello world"
86
+ end
87
+ end
88
+
89
+ # to use the component we first create an instance o
90
+
91
+ Include the `React::Component` mixin in a class to turn it into a react component
92
+
93
+ ```ruby
94
+ require 'opal'
95
+ require 'opal-react'
96
+
97
+ class HelloMessage
98
+
99
+ include React::Component # will create a new component named HelloMessage
100
+
101
+ MSG = {great: 'Cool!', bad: 'Cheer up!'}
102
+
103
+ optional_param :mood
104
+ required_param :name
105
+ define_state :foo, "Default greeting"
106
+
107
+ before_mount do # you can define life cycle callbacks inline
108
+ foo! "#{name}: #{MSG[mood]}" if mood # change the state of foo using foo!, read the state using foo
109
+ end
110
+
111
+ after_mount :log # you can also define life cycle callbacks by reference to a method
112
+
113
+ def log
114
+ puts "mounted!"
115
+ end
116
+
117
+ def render # render method MUST return just one component
118
+ div do # basic dsl syntax component_name(options) { ...children... }
119
+ span { "#{foo} #{name}!" } # all html5 components are defined with lower case text
120
+ end
121
+ end
122
+ end
123
+
124
+ class App
125
+ include React::Component
126
+
127
+ def render
128
+ HelloMessage name: 'John', mood: :great # new components are accessed via the class name
129
+ end
130
+ end
131
+
132
+ # later we will talk about nicer ways to do this: For now wait till doc is loaded
133
+ # then tell React to create an "App" and render it into the document body.
134
+
135
+ `window.onload = #{lambda {React.render(React.create_element(App), `document.body`)}}`
136
+
137
+ # -> console says: mounted!
138
+ ```
139
+
140
+ * Callback of life cycle could be created through helpers `before_mount`, `after_mount`, etc
141
+ * `this.props` is accessed through method `self.params`
142
+ * Use helper method `define_state` to create setter & getter of `this.state` for you
143
+ * 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.
144
+
145
+ ### Element Building DSL
146
+
147
+ As a replacement of JSX, include `React::Component` and you can build `React.Element` hierarchy without all the `React.create_element` noises.
148
+
149
+ ```ruby
150
+ def render
151
+ div do
152
+ h1 { "Title" }
153
+ h2 { "subtitle"}
154
+ div(class_name: 'fancy', id: 'foo') { span { "some text #{interpolation}"} }
155
+ present FancyElement, fancy_props: '10'
156
+ end
157
+ end
158
+ ```
159
+
160
+ ### Props validation
161
+
162
+ 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,
163
+
164
+ ```ruby
165
+ class App
166
+ include React::Component
167
+
168
+ params do
169
+ requires :username, type: String
170
+ requires :enum, values: ['foo', 'bar', 'awesome']
171
+ requires :payload, type: Todo # yeah, a plain Ruby class
172
+ optional :filters, type: Array[String]
173
+ optional :flash_message, type: String, default: 'Welcome!' # no need to feed through `getDefaultProps`
174
+ end
175
+
176
+ def render
177
+ div
178
+ end
179
+ end
180
+ ```
181
+
182
+ ### Mixins
183
+
184
+ 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.
185
+
186
+ ```ruby
187
+ module SetInterval
188
+ def self.included(base)
189
+ base.class_eval do
190
+ before_mount { @interval = [] }
191
+ before_unmount do
192
+ # abort associated timer of a component right before unmount
193
+ @interval.each { |i| i.abort }
194
+ end
195
+ end
196
+ end
197
+
198
+ def set_interval(seconds, &block)
199
+ @interval << $window.every(seconds, &block)
200
+ end
201
+ end
202
+
203
+ class TickTock
204
+ include React::Component
205
+ include SetInterval
206
+
207
+ define_state(:seconds) { 0 }
208
+
209
+ before_mount do
210
+ set_interval(1) { self.seconds = self.seconds + 1 }
211
+ set_interval(1) { puts "Tick!" }
212
+ end
213
+
214
+ def render
215
+ span do
216
+ "React has been running for: #{self.seconds}"
217
+ end
218
+ end
219
+ end
220
+
221
+ React.render(React.create_element(TickTock), $document.body.to_n)
222
+
223
+ $window.after(5) do
224
+ React.unmount_component_at_node($document.body.to_n)
225
+ end
226
+
227
+ # => Tick!
228
+ # => ... for 5 times then stop ticking after 5 seconds
229
+ ```
230
+
231
+
232
+ ### A Simple Component
233
+
234
+ A ruby class which define method `render` is a valid component.
235
+
236
+ ```ruby
237
+ class HelloMessage
238
+ def render
239
+ React.create_element("div") { "Hello World!" }
240
+ end
241
+ end
242
+
243
+ puts React.render_to_static_markup(React.create_element(HelloMessage))
244
+
245
+ # => '<div>Hello World!</div>'
246
+ ```
247
+
248
+ ### More complicated one
249
+
250
+ 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.
251
+
252
+ ```ruby
253
+ class HelloMessage
254
+ def initialize(native)
255
+ @native = Native(native)
256
+ end
257
+
258
+ def component_will_mount
259
+ puts "will mount!"
260
+ end
261
+
262
+ def render
263
+ React.create_element("div") { "Hello #{@native[:props][:name]}!" }
264
+ end
265
+ end
266
+
267
+ puts React.render_to_static_markup(React.create_element(HelloMessage, name: 'John'))
268
+
269
+ # => will_mount!
270
+ # => '<div>Hello John!</div>'
271
+ ```
272
+ ## Example
273
+
274
+ * React Tutorial: see [example/react-tutorial](example/react-tutorial), the original CommentBox example.
275
+ * TodoMVC: see [example/todos](example/todos), your beloved TodoMVC <3.
276
+
277
+ ## TODOS
278
+
279
+ * Documentation
280
+ * API wrapping coverage of the original js library (pretty close though)
281
+ * React Native?
282
+
283
+ ## Developing
284
+
285
+ To run the test case of the project yourself.
286
+
287
+ 1. `git clone` the project
288
+ 2. `bundle install`
289
+ 3. `bundle exec rackup`
290
+ 4. Open `http://localhost:9292` to run the spec
291
+
292
+ ## Contributions
293
+
294
+ This project is still in early stage, so discussion, bug report and PR are really welcome :wink:.
295
+
296
+ ## Contact
297
+
298
+ [David Chang](http://github.com/zetachang)
299
+ [@zetachang](https://twitter.com/zetachang)
300
+
301
+ ## License
302
+
303
+ In short, React.rb is available under the MIT license. See the LICENSE file for more info.
data/config.ru ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ require "opal-rspec"
5
+ require "react/source"
6
+
7
+ Opal.append_path File.expand_path('../spec', __FILE__)
8
+
9
+ run Opal::Server.new { |s|
10
+ s.main = 'opal/rspec/sprockets_runner'
11
+ s.append_path 'spec'
12
+ s.append_path File.dirname(::React::Source.bundled_path_for("react-with-addons.js"))
13
+ s.debug = true
14
+ s.index_path = 'spec/reactjs/index.html.erb'
15
+ }
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'opal'
4
+ gem 'opal-react', :path => '../..'
5
+ gem 'sinatra'
6
+ #gem 'opal-jquery'
7
+ gem 'react-source'