react-rails 0.13.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +200 -37
- data/lib/assets/javascripts/react_ujs.js.erb +104 -0
- data/lib/generators/react/component_generator.rb +127 -0
- data/lib/generators/react/install_generator.rb +58 -0
- data/lib/generators/templates/component.js.jsx +23 -0
- data/lib/react-rails.rb +2 -0
- data/lib/react/console.rb +30 -0
- data/lib/react/jsx.rb +24 -9
- data/lib/react/jsx/template.rb +2 -2
- data/lib/react/rails.rb +1 -0
- data/lib/react/rails/engine.rb +1 -1
- data/lib/react/rails/railtie.rb +70 -19
- data/lib/react/rails/version.rb +2 -4
- data/lib/react/rails/view_helper.rb +28 -0
- data/lib/react/renderer.rb +83 -0
- metadata +115 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ecb96344ad4094e2bb7d7ffe7b45455b807fc7a3
|
4
|
+
data.tar.gz: 8003608ada04a2d20573621278faa29daf6c838c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa90a0d40e3c856840d1b17bc207da5e2bc48f3b1587368eb97c6f4291776253a3b859fca7dbe01da62dd3c1ddd50dc2299bf80c3502bcc513b6e7fb3a9927b7
|
7
|
+
data.tar.gz: b126df93a0c17fba3c78e71433fc77d9688a6d3be7fb3ba45f1e968a24a8caaa151b29a5a9c3cccd29a446ddccf17f8409268c54476b7127ef78319c3ad26dfa
|
data/README.md
CHANGED
@@ -1,90 +1,253 @@
|
|
1
|
-
|
1
|
+
[![Gem](https://img.shields.io/gem/v/react-rails.svg?style=flat-square)](http://rubygems.org/gems/react-rails)
|
2
|
+
[![Build Status](https://img.shields.io/travis/reactjs/react-rails/master.svg?style=flat-square)](https://travis-ci.org/reactjs/react-rails)
|
3
|
+
[![Gemnasium](https://img.shields.io/gemnasium/reactjs/react-rails.svg?style=flat-square)](https://gemnasium.com/reactjs/react-rails)
|
4
|
+
[![Code Climate](https://img.shields.io/codeclimate/github/reactjs/react-rails.svg?style=flat-square)](https://codeclimate.com/github/reactjs/react-rails)
|
2
5
|
|
3
|
-
|
6
|
+
* * *
|
4
7
|
|
5
|
-
|
8
|
+
# react-rails
|
6
9
|
|
7
|
-
1. making it easy to include `react.js` as part of your dependencies in `application.js`.
|
8
|
-
2. transforming JSX into regular JS on request, or as part of asset precompilation.
|
9
10
|
|
11
|
+
`react-rails` makes it easy to use [React](http://facebook.github.io/react/) and [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) in your Ruby on Rails (3.1+) application. `react-rails` can:
|
10
12
|
|
11
|
-
|
13
|
+
- Provide [various `react` builds](#reactjs-builds) to your asset bundle
|
14
|
+
- Transform [`.jsx` in the asset pipeline](#jsx)
|
15
|
+
- [Render components into views and mount them](#rendering--mounting) via view helper & `react_ujs`
|
16
|
+
- [Render components server-side](#server-rendering) with `prerender: true`.
|
17
|
+
- [Generate components](#component-generator) with a Rails generator
|
12
18
|
|
13
|
-
|
19
|
+
## Installation
|
14
20
|
|
15
|
-
|
21
|
+
Add `react-rails` to your gemfile:
|
16
22
|
|
17
23
|
```ruby
|
18
|
-
|
24
|
+
gem 'react-rails', '~> 1.0'
|
25
|
+
```
|
19
26
|
|
20
|
-
|
27
|
+
Next, run the installation script.
|
28
|
+
|
29
|
+
```bash
|
30
|
+
rails g react:install
|
21
31
|
```
|
22
32
|
|
33
|
+
This will:
|
34
|
+
- create a `components.js` manifest file and a `app/assets/javascripts/components/` directory, where you will put your components
|
35
|
+
- place the following in your `application.js`:
|
36
|
+
|
37
|
+
```js
|
38
|
+
//= require react
|
39
|
+
//= require react_ujs
|
40
|
+
//= require components
|
41
|
+
```
|
23
42
|
|
24
43
|
## Usage
|
25
44
|
|
26
|
-
###
|
45
|
+
### React.js builds
|
27
46
|
|
28
|
-
|
47
|
+
You can pick which React.js build (development, production, with or without [add-ons]((http://facebook.github.io/react/docs/addons.html))) to serve in each environment by adding a config. Here are the defaults:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# config/environments/development.rb
|
51
|
+
MyApp::Application.configure do
|
52
|
+
config.react.variant = :development
|
53
|
+
end
|
29
54
|
|
30
|
-
|
55
|
+
# config/environments/production.rb
|
56
|
+
MyApp::Application.configure do
|
57
|
+
config.react.variant = :production
|
58
|
+
end
|
59
|
+
```
|
31
60
|
|
32
|
-
|
33
|
-
// app/assets/application.js
|
61
|
+
To include add-ons, use this config:
|
34
62
|
|
35
|
-
|
63
|
+
```ruby
|
64
|
+
MyApp::Application.configure do
|
65
|
+
config.react.addons = true # defaults to false
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
After restarting your Rails server, `//= require react` will provide the build of React.js which was specified by the configurations.
|
70
|
+
|
71
|
+
`react-rails` offers a few other options for versions & builds of React.js. See [VERSIONS.md](https://github.com/reactjs/react-rails/blob/master/VERSIONS.md) for more info about using the `react-source` gem or dropping in your own copies of React.js.
|
72
|
+
|
73
|
+
### JSX
|
74
|
+
|
75
|
+
After installing `react-rails`, restart your server. Now, `.js.jsx` files will be transformed in the asset pipeline.
|
76
|
+
|
77
|
+
You can use JSX `--harmony` or `--strip-types` options by adding a configuration:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
config.react.jsx_transform_options = {
|
81
|
+
harmony: true,
|
82
|
+
strip_types: true, # for removing Flow type annotations
|
83
|
+
}
|
84
|
+
```
|
85
|
+
|
86
|
+
To use CoffeeScript, create `.js.jsx.coffee` files and embed JSX inside backticks, for example:
|
87
|
+
|
88
|
+
```coffee
|
89
|
+
Component = React.createClass
|
90
|
+
render: ->
|
91
|
+
`<ExampleComponent videos={this.props.videos} />`
|
36
92
|
```
|
37
93
|
|
38
|
-
|
94
|
+
### Rendering & mounting
|
95
|
+
|
96
|
+
`react-rails` includes a view helper (`react_component`) and an unobtrusive JavaScript driver (`react_ujs`) which work together to put React components on the page. You should require the UJS driver in your manifest after `react` (and after `turbolinks` if you use [Turbolinks](https://github.com/rails/turbolinks))
|
97
|
+
|
98
|
+
The __view helper__ puts a `div` on the page with the requested component class & props. For example:
|
39
99
|
|
40
100
|
```erb
|
41
|
-
|
101
|
+
<%= react_component('HelloMessage', name: 'John') %>
|
102
|
+
<!-- becomes: -->
|
103
|
+
<div data-react-class="HelloMessage" data-react-props="{"name":"John"}"></div>
|
104
|
+
```
|
105
|
+
|
106
|
+
On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class` and `data-react-props`. Before page unload, it will unmount components (if you want to disable this behavior, remove `data-react-class` attribute in `componentDidMount`).
|
107
|
+
|
108
|
+
`react_ujs` uses Turbolinks events if they're available, otherwise, it uses native events. __Turbolinks >= 2.4.0__ is recommended because it exposes better events.
|
42
109
|
|
43
|
-
|
110
|
+
The view helper's signature is
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
react_component(component_class_name, props={}, html_options={})
|
44
114
|
```
|
45
115
|
|
116
|
+
- `component_class_name` is a string which names a globally-accessible component class. It may have dots (eg, `"MyApp.Header.MenuItem"`).
|
117
|
+
- `props` is either an object that responds to `#to_json` or an already-stringified JSON object (eg, made with Jbuilder, see note below)
|
118
|
+
- `html_options` may include:
|
119
|
+
- `tag:` to use an element other than a `div` to embed `data-react-class` and `-props`.
|
120
|
+
- `prerender: true` to render the component on the server.
|
121
|
+
- `**other` Any other arguments (eg `class:`, `id:`) are passed through to [`content_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag).
|
46
122
|
|
47
|
-
### JSX
|
48
123
|
|
49
|
-
|
124
|
+
### Server rendering
|
125
|
+
|
126
|
+
To render components on the server, pass `prerender: true` to `react_component`:
|
50
127
|
|
128
|
+
```erb
|
129
|
+
<%= react_component('HelloMessage', {name: 'John'}, {prerender: true}) %>
|
130
|
+
<!-- becomes: -->
|
131
|
+
<div data-react-class="HelloMessage" data-react-props="{"name":"John"}">
|
132
|
+
<h1>Hello, John!</h1>
|
133
|
+
</div>
|
134
|
+
```
|
135
|
+
|
136
|
+
_(It will be also be mounted by the UJS on page load.)_
|
51
137
|
|
52
|
-
|
138
|
+
There are some requirements for this to work:
|
53
139
|
|
54
|
-
|
140
|
+
- `react-rails` must load your code. By convention, it uses `components.js`, which was created by the install task. This file must include your components _and_ their dependencies (eg, Underscore.js).
|
141
|
+
- Your components must be accessible in the global scope. If you are using `.js.jsx.coffee` files then the wrapper function needs to be taken into account:
|
55
142
|
|
56
|
-
|
143
|
+
```coffee
|
144
|
+
# @ is `window`:
|
145
|
+
@Component = React.createClass
|
146
|
+
render: ->
|
147
|
+
`<ExampleComponent videos={this.props.videos} />`
|
148
|
+
```
|
149
|
+
- Your code can't reference `document`. Prerender processes don't have access to `document`, so jQuery and some other libs won't work in this environment :(
|
150
|
+
|
151
|
+
You can configure your pool of JS virtual machines and specify where it should load code:
|
57
152
|
|
58
153
|
```ruby
|
59
|
-
# config/environments/
|
154
|
+
# config/environments/application.rb
|
155
|
+
# These are the defaults if you dont specify any yourself
|
60
156
|
MyApp::Application.configure do
|
61
|
-
|
157
|
+
# renderer pool size:
|
158
|
+
config.react.max_renderers = 10
|
159
|
+
# prerender timeout, in seconds:
|
160
|
+
config.react.timeout = 20
|
161
|
+
# where to get React.js source:
|
162
|
+
config.react.react_js = lambda { File.read(::Rails.application.assets.resolve('react.js')) }
|
163
|
+
# array of filenames that will be requested from the asset pipeline
|
164
|
+
# and concatenated:
|
165
|
+
config.react.component_filenames = ['components.js']
|
166
|
+
# server-side console.log, console.warn, and console.error messages will be replayed on the client
|
167
|
+
# (you can set this to `true` in config/enviroments/development.rb to replay in development only)
|
168
|
+
config.react.replay_console = false
|
62
169
|
end
|
170
|
+
```
|
63
171
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
172
|
+
### Component generator
|
173
|
+
|
174
|
+
react-rails ships with a Rails generator to help you get started with a simple component scaffold. You can run it using `rails generate react:component ComponentName`. The generator takes an optional list of arguments for default propTypes, which follow the conventions set in the [Reusable Components](http://facebook.github.io/react/docs/reusable-components.html) section of the React documentation.
|
175
|
+
|
176
|
+
For example:
|
177
|
+
|
178
|
+
```shell
|
179
|
+
rails generate react:component Post title:string body:string published:bool published_by:instanceOf{Person}
|
68
180
|
```
|
69
181
|
|
70
|
-
|
182
|
+
would generate the following in `app/assets/javascripts/components/post.js.jsx`:
|
183
|
+
|
184
|
+
```jsx
|
185
|
+
var Post = React.createClass({
|
186
|
+
propTypes: {
|
187
|
+
title: React.PropTypes.string,
|
188
|
+
body: React.PropTypes.string,
|
189
|
+
published: React.PropTypes.bool,
|
190
|
+
publishedBy: React.PropTypes.instanceOf(Person)
|
191
|
+
},
|
192
|
+
|
193
|
+
render: function() {
|
194
|
+
return (
|
195
|
+
<div>
|
196
|
+
<div>Title: {this.props.title}</div>
|
197
|
+
<div>Body: {this.props.body}</div>
|
198
|
+
<div>Published: {this.props.published}</div>
|
199
|
+
<div>Published By: {this.props.published_by}</div>
|
200
|
+
</div>
|
201
|
+
);
|
202
|
+
}
|
203
|
+
});
|
204
|
+
```
|
205
|
+
|
206
|
+
The generator can use the following arguments to create basic propTypes:
|
207
|
+
|
208
|
+
* any
|
209
|
+
* array
|
210
|
+
* bool
|
211
|
+
* element
|
212
|
+
* func
|
213
|
+
* number
|
214
|
+
* object
|
215
|
+
* node
|
216
|
+
* shape
|
217
|
+
* string
|
218
|
+
|
219
|
+
The following additional arguments have special behavior:
|
71
220
|
|
72
|
-
|
221
|
+
* `instanceOf` takes an optional class name in the form of {className}
|
222
|
+
* `oneOf` behaves like an enum, and takes an optional list of strings in the form of `'name:oneOf{one,two,three}'`.
|
223
|
+
* `oneOfType` takes an optional list of react and custom types in the form of `'model:oneOfType{string,number,OtherType}'`
|
224
|
+
|
225
|
+
Note that the arguments for `oneOf` and `oneOfType` must be enclosed in single quotes to prevent your terminal from expanding them into an argument list.
|
226
|
+
|
227
|
+
### Jbuilder & react-rails
|
228
|
+
|
229
|
+
If you use Jbuilder to pass JSON string to `react_component`, make sure your JSON is a stringified hash, not an array. This is not the Rails default -- you should add the root node yourself. For example:
|
73
230
|
|
74
231
|
```ruby
|
75
|
-
|
76
|
-
|
232
|
+
# BAD: returns a stringified array
|
233
|
+
json.array!(@messages) do |message|
|
234
|
+
json.extract! message, :id, :name
|
235
|
+
json.url message_url(message, format: :json)
|
77
236
|
end
|
78
|
-
```
|
79
237
|
|
238
|
+
# GOOD: returns a stringified hash
|
239
|
+
json.messages(@messages) do |message|
|
240
|
+
json.extract! message, :id, :name
|
241
|
+
json.url message_url(message, format: :json)
|
242
|
+
end
|
243
|
+
```
|
80
244
|
|
81
245
|
## CoffeeScript
|
82
246
|
|
83
|
-
It is possible to use JSX with CoffeeScript. We need to embed JSX inside backticks so CoffeeScript ignores the syntax it doesn't understand. Here's an example:
|
247
|
+
It is possible to use JSX with CoffeeScript. The caveat is that you will still need to include the docblock. Since CoffeeScript doesn't allow `/* */` style comments, we need to do something a little different. We also need to embed JSX inside backticks so CoffeeScript ignores the syntax it doesn't understand. Here's an example:
|
84
248
|
|
85
249
|
```coffee
|
86
250
|
Component = React.createClass
|
87
251
|
render: ->
|
88
252
|
`<ExampleComponent videos={this.props.videos} />`
|
89
253
|
```
|
90
|
-
|
@@ -0,0 +1,104 @@
|
|
1
|
+
/*globals React, Turbolinks*/
|
2
|
+
|
3
|
+
// Unobtrusive scripting adapter for React
|
4
|
+
;(function(document, window) {
|
5
|
+
// jQuery is optional. Use it to support legacy browsers.
|
6
|
+
var $ = (typeof window.jQuery !== 'undefined') && window.jQuery;
|
7
|
+
|
8
|
+
// create the namespace
|
9
|
+
window.ReactRailsUJS = {
|
10
|
+
CLASS_NAME_ATTR: 'data-react-class',
|
11
|
+
PROPS_ATTR: 'data-react-props',
|
12
|
+
RAILS_ENV_DEVELOPMENT: <%= Rails.env == "development" %>,
|
13
|
+
// helper method for the mount and unmount methods to find the
|
14
|
+
// `data-react-class` DOM elements
|
15
|
+
findDOMNodes: function(searchSelector) {
|
16
|
+
// we will use fully qualified paths as we do not bind the callbacks
|
17
|
+
var selector;
|
18
|
+
if (typeof searchSelector === 'undefined') {
|
19
|
+
var selector = '[' + window.ReactRailsUJS.CLASS_NAME_ATTR + ']';
|
20
|
+
} else {
|
21
|
+
var selector = searchSelector + ' [' + window.ReactRailsUJS.CLASS_NAME_ATTR + ']';
|
22
|
+
}
|
23
|
+
|
24
|
+
if ($) {
|
25
|
+
return $(selector);
|
26
|
+
} else {
|
27
|
+
return document.querySelectorAll(selector);
|
28
|
+
}
|
29
|
+
},
|
30
|
+
|
31
|
+
mountComponents: function(searchSelector) {
|
32
|
+
var nodes = window.ReactRailsUJS.findDOMNodes(searchSelector);
|
33
|
+
|
34
|
+
for (var i = 0; i < nodes.length; ++i) {
|
35
|
+
var node = nodes[i];
|
36
|
+
var className = node.getAttribute(window.ReactRailsUJS.CLASS_NAME_ATTR);
|
37
|
+
|
38
|
+
// Assume className is simple and can be found at top-level (window).
|
39
|
+
// Fallback to eval to handle cases like 'My.React.ComponentName'.
|
40
|
+
var constructor = window[className] || eval.call(window, className);
|
41
|
+
var propsJson = node.getAttribute(window.ReactRailsUJS.PROPS_ATTR);
|
42
|
+
var props = propsJson && JSON.parse(propsJson);
|
43
|
+
|
44
|
+
React.render(React.createElement(constructor, props), node);
|
45
|
+
}
|
46
|
+
},
|
47
|
+
|
48
|
+
unmountComponents: function(searchSelector) {
|
49
|
+
var nodes = window.ReactRailsUJS.findDOMNodes(searchSelector);
|
50
|
+
|
51
|
+
for (var i = 0; i < nodes.length; ++i) {
|
52
|
+
var node = nodes[i];
|
53
|
+
|
54
|
+
React.unmountComponentAtNode(node);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
};
|
58
|
+
|
59
|
+
// functions not exposed publicly
|
60
|
+
function handleTurbolinksEvents () {
|
61
|
+
var handleEvent;
|
62
|
+
var unmountEvent;
|
63
|
+
|
64
|
+
if ($) {
|
65
|
+
handleEvent = function(eventName, callback) {
|
66
|
+
$(document).on(eventName, callback);
|
67
|
+
};
|
68
|
+
|
69
|
+
} else {
|
70
|
+
handleEvent = function(eventName, callback) {
|
71
|
+
document.addEventListener(eventName, callback);
|
72
|
+
};
|
73
|
+
}
|
74
|
+
|
75
|
+
if (Turbolinks.EVENTS) {
|
76
|
+
unmountEvent = Turbolinks.EVENTS.BEFORE_UNLOAD;
|
77
|
+
} else {
|
78
|
+
unmountEvent = 'page:receive';
|
79
|
+
Turbolinks.pagesCached(0);
|
80
|
+
|
81
|
+
if (window.ReactRailsUJS.RAILS_ENV_DEVELOPMENT) {
|
82
|
+
console.warn('The Turbolinks cache has been disabled (Turbolinks >= 2.4.0 is recommended). See https://github.com/reactjs/react-rails/issues/87 for more information.');
|
83
|
+
}
|
84
|
+
}
|
85
|
+
handleEvent('page:change', function() {window.ReactRailsUJS.mountComponents()});
|
86
|
+
handleEvent(unmountEvent, function() {window.ReactRailsUJS.unmountComponents()});
|
87
|
+
}
|
88
|
+
|
89
|
+
function handleNativeEvents() {
|
90
|
+
if ($) {
|
91
|
+
$(function() {window.ReactRailsUJS.mountComponents()});
|
92
|
+
$(window).unload(function() {window.ReactRailsUJS.unmountComponents()});
|
93
|
+
} else {
|
94
|
+
document.addEventListener('DOMContentLoaded', function() {window.ReactRailsUJS.mountComponents()});
|
95
|
+
window.addEventListener('unload', function() {window.ReactRailsUJS.unmountComponents()});
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
|
100
|
+
handleTurbolinksEvents();
|
101
|
+
} else {
|
102
|
+
handleNativeEvents();
|
103
|
+
}
|
104
|
+
})(document, window);
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module React
|
2
|
+
module Generators
|
3
|
+
class ComponentGenerator < ::Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path '../../templates', __FILE__
|
5
|
+
desc <<-DESC.strip_heredoc
|
6
|
+
Description:
|
7
|
+
Scaffold a react component into app/assets/javascripts/components.
|
8
|
+
The generated component will include a basic render function and a PropTypes
|
9
|
+
hash to help with development.
|
10
|
+
|
11
|
+
Available field types:
|
12
|
+
|
13
|
+
Basic prop types do not take any additional arguments. If you do not specify
|
14
|
+
a prop type, the generic node will be used. The basic types available are:
|
15
|
+
|
16
|
+
any
|
17
|
+
array
|
18
|
+
bool
|
19
|
+
element
|
20
|
+
func
|
21
|
+
number
|
22
|
+
object
|
23
|
+
node
|
24
|
+
shape
|
25
|
+
string
|
26
|
+
|
27
|
+
Special PropTypes take additional arguments in {}, and must be enclosed in
|
28
|
+
single quotes to keep bash from expanding the arguments in {}.
|
29
|
+
|
30
|
+
instanceOf
|
31
|
+
takes an optional class name in the form of {className}
|
32
|
+
|
33
|
+
oneOf
|
34
|
+
behaves like an enum, and takes an optional list of strings that will
|
35
|
+
be allowed in the form of 'name:oneOf{one,two,three}'.
|
36
|
+
|
37
|
+
oneOfType.
|
38
|
+
oneOfType takes an optional list of react and custom types in the form of
|
39
|
+
'model:oneOfType{string,number,OtherType}'
|
40
|
+
|
41
|
+
Examples:
|
42
|
+
rails g react:component person name
|
43
|
+
rails g react:component restaurant name:string rating:number owner:instanceOf{Person}
|
44
|
+
rails g react:component food 'kind:oneOf{meat,cheese,vegetable}'
|
45
|
+
rails g react:component events 'location:oneOfType{string,Restaurant}'
|
46
|
+
DESC
|
47
|
+
|
48
|
+
argument :attributes,
|
49
|
+
:type => :array,
|
50
|
+
:default => [],
|
51
|
+
:banner => "field[:type] field[:type] ..."
|
52
|
+
|
53
|
+
REACT_PROP_TYPES = {
|
54
|
+
"node" => 'React.PropTypes.node',
|
55
|
+
"bool" => 'React.PropTypes.bool',
|
56
|
+
"boolean" => 'React.PropTypes.bool',
|
57
|
+
"string" => 'React.PropTypes.string',
|
58
|
+
"number" => 'React.PropTypes.number',
|
59
|
+
"object" => 'React.PropTypes.object',
|
60
|
+
"array" => 'React.PropTypes.array',
|
61
|
+
"shape" => 'React.PropTypes.shape({})',
|
62
|
+
"element" => 'React.PropTypes.element',
|
63
|
+
"func" => 'React.PropTypes.func',
|
64
|
+
"function" => 'React.PropTypes.func',
|
65
|
+
"any" => 'React.PropTypes.any',
|
66
|
+
|
67
|
+
"instanceOf" => ->(type) {
|
68
|
+
'React.PropTypes.instanceOf(%s)' % type.to_s.camelize
|
69
|
+
},
|
70
|
+
|
71
|
+
"oneOf" => ->(*options) {
|
72
|
+
enums = options.map{|k| "'#{k.to_s}'"}.join(',')
|
73
|
+
'React.PropTypes.oneOf([%s])' % enums
|
74
|
+
},
|
75
|
+
|
76
|
+
"oneOfType" => ->(*options) {
|
77
|
+
types = options.map{|k| "#{lookup(k.to_s, k.to_s)}" }.join(',')
|
78
|
+
'React.PropTypes.oneOfType([%s])' % types
|
79
|
+
},
|
80
|
+
}
|
81
|
+
|
82
|
+
def create_component_file
|
83
|
+
extension = "js.jsx"
|
84
|
+
file_path = File.join('app/assets/javascripts/components', "#{file_name}.#{extension}")
|
85
|
+
template("component.#{extension}", file_path)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def parse_attributes!
|
91
|
+
self.attributes = (attributes || []).map do |attr|
|
92
|
+
name, type, options = "", "", ""
|
93
|
+
options_regex = /(?<options>{.*})/
|
94
|
+
|
95
|
+
name, type = attr.split(':')
|
96
|
+
|
97
|
+
if matchdata = options_regex.match(type)
|
98
|
+
options = matchdata[:options]
|
99
|
+
type = type.gsub(options_regex, '')
|
100
|
+
end
|
101
|
+
|
102
|
+
{ :name => name, :type => lookup(type, options) }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.lookup(type = "node", options = "")
|
107
|
+
react_prop_type = REACT_PROP_TYPES[type]
|
108
|
+
if react_prop_type.blank?
|
109
|
+
if type =~ /^[[:upper:]]/
|
110
|
+
react_prop_type = REACT_PROP_TYPES['instanceOf']
|
111
|
+
else
|
112
|
+
react_prop_type = REACT_PROP_TYPES['node']
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
options = options.to_s.gsub(/[{}]/, '').split(',')
|
117
|
+
|
118
|
+
react_prop_type = react_prop_type.call(*options) if react_prop_type.respond_to? :call
|
119
|
+
react_prop_type
|
120
|
+
end
|
121
|
+
|
122
|
+
def lookup(type = "node", options = "")
|
123
|
+
self.class.lookup(type, options)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module React
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < ::Rails::Generators::Base
|
4
|
+
source_root File.expand_path '../../templates', __FILE__
|
5
|
+
|
6
|
+
desc 'Create default react.js folder layout and prep application.js'
|
7
|
+
|
8
|
+
class_option :skip_git,
|
9
|
+
type: :boolean,
|
10
|
+
aliases: '-g',
|
11
|
+
default: false,
|
12
|
+
desc: 'Skip Git keeps'
|
13
|
+
|
14
|
+
def create_directory
|
15
|
+
empty_directory 'app/assets/javascripts/components'
|
16
|
+
create_file 'app/assets/javascripts/components/.gitkeep' unless options[:skip_git]
|
17
|
+
end
|
18
|
+
|
19
|
+
def inject_react
|
20
|
+
require_react = "//= require react\n"
|
21
|
+
|
22
|
+
if manifest.exist?
|
23
|
+
manifest_contents = File.read(manifest)
|
24
|
+
|
25
|
+
if manifest_contents.include? 'require turbolinks'
|
26
|
+
inject_into_file manifest, require_react, {after: "//= require turbolinks\n"}
|
27
|
+
elsif manifest_contents.include? 'require_tree'
|
28
|
+
inject_into_file manifest, require_react, {before: '//= require_tree'}
|
29
|
+
else
|
30
|
+
append_file manifest, require_react
|
31
|
+
end
|
32
|
+
else
|
33
|
+
create_file manifest, require_react
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def inject_components
|
38
|
+
inject_into_file manifest, "//= require components\n", {after: "//= require react\n"}
|
39
|
+
end
|
40
|
+
|
41
|
+
def inject_react_ujs
|
42
|
+
inject_into_file manifest, "//= require react_ujs\n", {after: "//= require react\n"}
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_components
|
46
|
+
components_js = "//= require_tree ./components\n"
|
47
|
+
components_file = File.join(*%w(app assets javascripts components.js))
|
48
|
+
create_file components_file, components_js
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def manifest
|
54
|
+
Pathname.new(destination_root).join('app/assets/javascripts', 'application.js')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
var <%= file_name.camelize %> = React.createClass({
|
2
|
+
<% if attributes.size > 0 -%>
|
3
|
+
propTypes: {
|
4
|
+
<% attributes.each_with_index do |attribute, idx| -%>
|
5
|
+
<%= attribute[:name].camelize(:lower) %>: <%= attribute[:type] %><% if (idx < attributes.length-1) %>,<% end %>
|
6
|
+
<% end -%>
|
7
|
+
},
|
8
|
+
<% end -%>
|
9
|
+
|
10
|
+
render: function() {
|
11
|
+
<% if attributes.size > 0 -%>
|
12
|
+
return (
|
13
|
+
<div>
|
14
|
+
<% attributes.each do |attribute| -%>
|
15
|
+
<div><%= attribute[:name].titleize %>: {this.props.<%= attribute[:name] %>}</div>
|
16
|
+
<% end -%>
|
17
|
+
</div>
|
18
|
+
);
|
19
|
+
<% else -%>
|
20
|
+
return <div />;
|
21
|
+
<% end -%>
|
22
|
+
}
|
23
|
+
});
|
data/lib/react-rails.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
module React
|
2
|
+
class Console
|
3
|
+
def self.polyfill_js
|
4
|
+
# Overwrite global `console` object with something that can capture messages
|
5
|
+
# to return to client later for debugging
|
6
|
+
<<-JS
|
7
|
+
var console = { history: [] };
|
8
|
+
['error', 'log', 'info', 'warn'].forEach(function (fn) {
|
9
|
+
console[fn] = function () {
|
10
|
+
console.history.push({level: fn, arguments: Array.prototype.slice.call(arguments)});
|
11
|
+
};
|
12
|
+
});
|
13
|
+
JS
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.replay_as_script_js
|
17
|
+
<<-JS
|
18
|
+
(function (history) {
|
19
|
+
if (history && history.length > 0) {
|
20
|
+
result += '\\n<scr'+'ipt>';
|
21
|
+
history.forEach(function (msg) {
|
22
|
+
result += '\\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
|
23
|
+
});
|
24
|
+
result += '\\n</scr'+'ipt>';
|
25
|
+
}
|
26
|
+
})(console.history);
|
27
|
+
JS
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/react/jsx.rb
CHANGED
@@ -1,21 +1,36 @@
|
|
1
1
|
require 'execjs'
|
2
2
|
require 'react/source'
|
3
3
|
require 'react/jsx/template'
|
4
|
+
require 'rails'
|
4
5
|
|
5
6
|
module React
|
6
7
|
module JSX
|
8
|
+
mattr_accessor :transform_options
|
9
|
+
|
7
10
|
def self.context
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# lazily loaded during first request and reloaded every time when in dev or test
|
12
|
+
unless @context && ::Rails.env.production?
|
13
|
+
contents =
|
14
|
+
# If execjs uses therubyracer, there is no 'global'. Make sure
|
15
|
+
# we have it so JSX script can work properly.
|
16
|
+
'var global = global || this;' +
|
17
|
+
|
18
|
+
# search for transformer file using sprockets - allows user to override
|
19
|
+
# this file in his own application
|
20
|
+
File.read(::Rails.application.assets.resolve('JSXTransformer.js'))
|
21
|
+
|
22
|
+
@context = ExecJS.compile(contents)
|
23
|
+
end
|
24
|
+
|
25
|
+
@context
|
15
26
|
end
|
16
27
|
|
17
|
-
def self.transform(code)
|
18
|
-
|
28
|
+
def self.transform(code, options={})
|
29
|
+
js_options = {
|
30
|
+
stripTypes: options[:strip_types],
|
31
|
+
harmony: options[:harmony],
|
32
|
+
}
|
33
|
+
result = context.call('JSXTransformer.transform', code, js_options)
|
19
34
|
return result['code']
|
20
35
|
end
|
21
36
|
end
|
data/lib/react/jsx/template.rb
CHANGED
data/lib/react/rails.rb
CHANGED
data/lib/react/rails/engine.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module React
|
2
2
|
module Rails
|
3
3
|
class Engine < ::Rails::Engine
|
4
|
-
initializer "react_rails.setup_engine", :
|
4
|
+
initializer "react_rails.setup_engine", :group => :all do |app|
|
5
5
|
app.assets.register_engine '.jsx', React::JSX::Template
|
6
6
|
end
|
7
7
|
end
|
data/lib/react/rails/railtie.rb
CHANGED
@@ -5,32 +5,83 @@ module React
|
|
5
5
|
class Railtie < ::Rails::Railtie
|
6
6
|
config.react = ActiveSupport::OrderedOptions.new
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
# Sensible defaults. Can be overridden in application.rb
|
9
|
+
config.react.variant = (::Rails.env.production? ? :production : :development)
|
10
|
+
config.react.addons = false
|
11
|
+
config.react.jsx_transform_options = {}
|
12
|
+
# Server-side rendering
|
13
|
+
config.react.max_renderers = 10
|
14
|
+
config.react.timeout = 20 #seconds
|
15
|
+
config.react.react_js = lambda {File.read(::Rails.application.assets.resolve('react.js'))}
|
16
|
+
config.react.component_filenames = ['components.js']
|
10
17
|
|
18
|
+
# Watch .jsx files for changes in dev, so we can reload the JS VMs with the new JS code.
|
19
|
+
initializer "react_rails.add_watchable_files", group: :all do |app|
|
20
|
+
app.config.watchable_files.concat Dir["#{app.root}/app/assets/javascripts/**/*.jsx*"]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Include the react-rails view helper lazily
|
24
|
+
initializer "react_rails.setup_view_helpers", group: :all do |app|
|
25
|
+
React::JSX.transform_options = app.config.react.jsx_transform_options
|
26
|
+
ActiveSupport.on_load(:action_view) do
|
27
|
+
include ::React::Rails::ViewHelper
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
initializer "react_rails.setup_vendor", group: :all do |app|
|
11
32
|
# Mimic behavior of ember-rails...
|
12
33
|
# We want to include different files in dev/prod. The unminified builds
|
13
34
|
# contain console logging for invariants and logging to help catch
|
14
35
|
# common mistakes. These are all stripped out in the minified build.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
36
|
+
|
37
|
+
# Copy over the variant into a path that sprockets will pick up.
|
38
|
+
# We'll always copy to 'react.js' so that no includes need to change.
|
39
|
+
# We'll also always copy of JSXTransformer.js
|
40
|
+
tmp_path = app.root.join('tmp/react-rails')
|
41
|
+
filename = 'react' +
|
42
|
+
(app.config.react.addons ? '-with-addons' : '') +
|
43
|
+
(app.config.react.variant == :production ? '.min.js' : '.js')
|
44
|
+
FileUtils.mkdir_p(tmp_path)
|
45
|
+
FileUtils.cp(::React::Source.bundled_path_for(filename),
|
46
|
+
tmp_path.join('react.js'))
|
47
|
+
FileUtils.cp(::React::Source.bundled_path_for('JSXTransformer.js'),
|
48
|
+
tmp_path.join('JSXTransformer.js'))
|
49
|
+
app.assets.prepend_path tmp_path
|
50
|
+
|
51
|
+
# Allow overriding react files that are not based on environment
|
52
|
+
# e.g. /vendor/assets/react/JSXTransformer.js
|
53
|
+
dropin_path = app.root.join("vendor/assets/react")
|
54
|
+
app.assets.prepend_path dropin_path if dropin_path.exist?
|
55
|
+
|
56
|
+
# Allow overriding react files that are based on environment
|
57
|
+
# e.g. /vendor/assets/react/development/react.js
|
58
|
+
dropin_path_env = app.root.join("vendor/assets/react/#{app.config.react.variant}")
|
59
|
+
app.assets.prepend_path dropin_path_env if dropin_path_env.exist?
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
config.after_initialize do |app|
|
64
|
+
# Server Rendering
|
65
|
+
# Concat component_filenames together for server rendering
|
66
|
+
app.config.react.components_js = lambda {
|
67
|
+
app.config.react.component_filenames.map do |filename|
|
68
|
+
app.assets[filename].to_s
|
69
|
+
end.join(";")
|
70
|
+
}
|
71
|
+
|
72
|
+
do_setup = lambda do
|
73
|
+
cfg = app.config.react
|
74
|
+
React::Renderer.setup!( cfg.react_js, cfg.components_js, cfg.replay_console,
|
75
|
+
{:size => cfg.max_renderers, :timeout => cfg.timeout})
|
32
76
|
end
|
77
|
+
|
78
|
+
do_setup.call
|
79
|
+
|
80
|
+
# Reload the JS VMs in dev when files change
|
81
|
+
ActionDispatch::Reloader.to_prepare(&do_setup)
|
33
82
|
end
|
83
|
+
|
84
|
+
|
34
85
|
end
|
35
86
|
end
|
36
87
|
end
|
data/lib/react/rails/version.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module React
|
2
2
|
module Rails
|
3
|
-
#
|
4
|
-
|
5
|
-
VERSION = '0.13.0.0'
|
3
|
+
# If you change this, make sure to update VERSIONS.md
|
4
|
+
VERSION = '1.0.0'
|
6
5
|
end
|
7
6
|
end
|
8
|
-
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module React
|
2
|
+
module Rails
|
3
|
+
module ViewHelper
|
4
|
+
|
5
|
+
# Render a UJS-type HTML tag annotated with data attributes, which
|
6
|
+
# are used by react_ujs to actually instantiate the React component
|
7
|
+
# on the client.
|
8
|
+
#
|
9
|
+
def react_component(name, args = {}, options = {}, &block)
|
10
|
+
options = {:tag => options} if options.is_a?(Symbol)
|
11
|
+
block = Proc.new{concat React::Renderer.render(name, args)} if options[:prerender]
|
12
|
+
|
13
|
+
html_options = options.reverse_merge(:data => {})
|
14
|
+
html_options[:data].tap do |data|
|
15
|
+
data[:react_class] = name
|
16
|
+
data[:react_props] = React::Renderer.react_props(args) unless args.empty?
|
17
|
+
end
|
18
|
+
html_tag = html_options[:tag] || :div
|
19
|
+
|
20
|
+
# remove internally used properties so they aren't rendered to DOM
|
21
|
+
html_options.except!(:tag, :prerender)
|
22
|
+
|
23
|
+
content_tag(html_tag, '', html_options, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'connection_pool'
|
2
|
+
|
3
|
+
module React
|
4
|
+
class Renderer
|
5
|
+
|
6
|
+
class PrerenderError < RuntimeError
|
7
|
+
def initialize(component_name, props, js_message)
|
8
|
+
message = ["Encountered error \"#{js_message}\" when prerendering #{component_name} with #{props}",
|
9
|
+
js_message.backtrace.join("\n")].join("\n")
|
10
|
+
super(message)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
cattr_accessor :pool
|
15
|
+
|
16
|
+
def self.setup!(react_js, components_js, replay_console, args={})
|
17
|
+
args.assert_valid_keys(:size, :timeout)
|
18
|
+
@@react_js = react_js
|
19
|
+
@@components_js = components_js
|
20
|
+
@@replay_console = replay_console
|
21
|
+
@@pool.shutdown{} if @@pool
|
22
|
+
reset_combined_js!
|
23
|
+
default_pool_options = {:size =>10, :timeout => 20}
|
24
|
+
@@pool = ConnectionPool.new(default_pool_options.merge(args)) { self.new }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.render(component, args={})
|
28
|
+
@@pool.with do |renderer|
|
29
|
+
renderer.render(component, args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.react_props(args={})
|
34
|
+
if args.is_a? String
|
35
|
+
args
|
36
|
+
else
|
37
|
+
args.to_json
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def context
|
42
|
+
@context ||= ExecJS.compile(self.class.combined_js)
|
43
|
+
end
|
44
|
+
|
45
|
+
def render(component, args={})
|
46
|
+
react_props = React::Renderer.react_props(args)
|
47
|
+
jscode = <<-JS
|
48
|
+
(function () {
|
49
|
+
var result = React.renderToString(React.createElement(#{component}, #{react_props}));
|
50
|
+
#{@@replay_console ? React::Console.replay_as_script_js : ''}
|
51
|
+
return result;
|
52
|
+
})()
|
53
|
+
JS
|
54
|
+
context.eval(jscode).html_safe
|
55
|
+
rescue ExecJS::ProgramError => e
|
56
|
+
raise PrerenderError.new(component, react_props, e)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def self.setup_combined_js
|
63
|
+
<<-JS
|
64
|
+
var global = global || this;
|
65
|
+
var self = self || this;
|
66
|
+
var window = window || this;
|
67
|
+
#{React::Console.polyfill_js}
|
68
|
+
#{@@react_js.call};
|
69
|
+
React = global.React;
|
70
|
+
#{@@components_js.call};
|
71
|
+
JS
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.reset_combined_js!
|
75
|
+
@@combined_js = setup_combined_js
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.combined_js
|
79
|
+
@@combined_js
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: react-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul O’Shannessy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: appraisal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -25,7 +39,7 @@ dependencies:
|
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: 1.2.2
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: coffee-rails
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - ">="
|
@@ -39,14 +53,98 @@ dependencies:
|
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: es5-shim-rails
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.0.5
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.0.5
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: jbuilder
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
79
|
requirements:
|
45
80
|
- - ">="
|
46
81
|
- !ruby/object:Gem::Version
|
47
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: poltergeist
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.3.3
|
48
90
|
type: :development
|
49
91
|
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.3.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: test-unit
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.5'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.5'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: turbolinks
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.0.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.0.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: coffee-script-source
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.8'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.8'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: connection_pool
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
50
148
|
version_requirements: !ruby/object:Gem::Requirement
|
51
149
|
requirements:
|
52
150
|
- - ">="
|
@@ -84,16 +182,16 @@ dependencies:
|
|
84
182
|
name: react-source
|
85
183
|
requirement: !ruby/object:Gem::Requirement
|
86
184
|
requirements:
|
87
|
-
- -
|
185
|
+
- - "~>"
|
88
186
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.13
|
187
|
+
version: '0.13'
|
90
188
|
type: :runtime
|
91
189
|
prerelease: false
|
92
190
|
version_requirements: !ruby/object:Gem::Requirement
|
93
191
|
requirements:
|
94
|
-
- -
|
192
|
+
- - "~>"
|
95
193
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.13
|
194
|
+
version: '0.13'
|
97
195
|
description: Compile your JSX on demand or precompile for production.
|
98
196
|
email:
|
99
197
|
- paul@oshannessy.com
|
@@ -103,14 +201,21 @@ extra_rdoc_files: []
|
|
103
201
|
files:
|
104
202
|
- LICENSE
|
105
203
|
- README.md
|
204
|
+
- lib/assets/javascripts/react_ujs.js.erb
|
205
|
+
- lib/generators/react/component_generator.rb
|
206
|
+
- lib/generators/react/install_generator.rb
|
207
|
+
- lib/generators/templates/component.js.jsx
|
106
208
|
- lib/react-rails.rb
|
209
|
+
- lib/react/console.rb
|
107
210
|
- lib/react/jsx.rb
|
108
211
|
- lib/react/jsx/template.rb
|
109
212
|
- lib/react/rails.rb
|
110
213
|
- lib/react/rails/engine.rb
|
111
214
|
- lib/react/rails/railtie.rb
|
112
215
|
- lib/react/rails/version.rb
|
113
|
-
|
216
|
+
- lib/react/rails/view_helper.rb
|
217
|
+
- lib/react/renderer.rb
|
218
|
+
homepage: https://github.com/reactjs/react-rails
|
114
219
|
licenses:
|
115
220
|
- APL 2.0
|
116
221
|
metadata: {}
|
@@ -130,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
235
|
version: '0'
|
131
236
|
requirements: []
|
132
237
|
rubyforge_project:
|
133
|
-
rubygems_version: 2.
|
238
|
+
rubygems_version: 2.4.5
|
134
239
|
signing_key:
|
135
240
|
specification_version: 4
|
136
241
|
summary: React/JSX adapter for the Ruby on Rails asset pipeline.
|