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.
- 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
|
+
[](http://badge.fury.io/rb/react.rb)
|
4
|
+
[](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
|
+
}
|