reactive-ruby 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +30 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +53 -0
- data/LICENSE +19 -0
- data/README.md +303 -0
- data/config.ru +15 -0
- data/example/examples/Gemfile +7 -0
- data/example/examples/Gemfile.lock +45 -0
- data/example/examples/config.ru +44 -0
- data/example/examples/hello.js.rb +43 -0
- data/example/react-tutorial/Gemfile +7 -0
- data/example/react-tutorial/Gemfile.lock +49 -0
- data/example/react-tutorial/README.md +8 -0
- data/example/react-tutorial/_comments.json +14 -0
- data/example/react-tutorial/config.ru +63 -0
- data/example/react-tutorial/example.js.rb +290 -0
- data/example/react-tutorial/public/base.css +62 -0
- data/example/todos/Gemfile +11 -0
- data/example/todos/Gemfile.lock +84 -0
- data/example/todos/README.md +37 -0
- data/example/todos/Rakefile +8 -0
- data/example/todos/app/application.rb +22 -0
- data/example/todos/app/components/app.react.rb +61 -0
- data/example/todos/app/components/footer.react.rb +31 -0
- data/example/todos/app/components/todo_item.react.rb +46 -0
- data/example/todos/app/components/todo_list.react.rb +25 -0
- data/example/todos/app/models/todo.rb +19 -0
- data/example/todos/config.ru +14 -0
- data/example/todos/index.html.haml +16 -0
- data/example/todos/spec/todo_spec.rb +28 -0
- data/example/todos/vendor/base.css +410 -0
- data/example/todos/vendor/bg.png +0 -0
- data/example/todos/vendor/jquery.js +4 -0
- data/lib/rails-helpers/react_component.rb +32 -0
- data/lib/reactive-ruby.rb +23 -0
- data/lib/reactive-ruby/api.rb +177 -0
- data/lib/reactive-ruby/callbacks.rb +35 -0
- data/lib/reactive-ruby/component.rb +411 -0
- data/lib/reactive-ruby/element.rb +87 -0
- data/lib/reactive-ruby/event.rb +76 -0
- data/lib/reactive-ruby/ext/hash.rb +9 -0
- data/lib/reactive-ruby/ext/string.rb +8 -0
- data/lib/reactive-ruby/isomorphic_helpers.rb +223 -0
- data/lib/reactive-ruby/observable.rb +33 -0
- data/lib/reactive-ruby/rendering_context.rb +91 -0
- data/lib/reactive-ruby/serializers.rb +15 -0
- data/lib/reactive-ruby/state.rb +90 -0
- data/lib/reactive-ruby/top_level.rb +53 -0
- data/lib/reactive-ruby/validator.rb +83 -0
- data/lib/reactive-ruby/version.rb +3 -0
- data/logo1.png +0 -0
- data/logo2.png +0 -0
- data/logo3.png +0 -0
- data/reactive-ruby.gemspec +25 -0
- data/spec/callbacks_spec.rb +107 -0
- data/spec/component_spec.rb +597 -0
- data/spec/element_spec.rb +60 -0
- data/spec/event_spec.rb +22 -0
- data/spec/react_spec.rb +209 -0
- data/spec/reactjs/index.html.erb +11 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/tutorial/tutorial_spec.rb +37 -0
- data/spec/validator_spec.rb +79 -0
- data/vendor/active_support/core_ext/array/extract_options.rb +29 -0
- data/vendor/active_support/core_ext/class/attribute.rb +127 -0
- data/vendor/active_support/core_ext/kernel/singleton_class.rb +13 -0
- data/vendor/active_support/core_ext/module/remove_method.rb +11 -0
- 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
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
|
+
}
|