tilt-react 0.1.0 → 0.2.0
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 +13 -5
- data/.travis.yml +2 -1
- data/Gemfile +2 -0
- data/README.md +2 -1
- data/Rakefile +7 -1
- data/examples/sinatra/.gitignore +3 -1
- data/examples/sinatra/Gemfile.lock +6 -8
- data/examples/sinatra/README.md +5 -2
- data/examples/sinatra/Rakefile +2 -0
- data/examples/sinatra/app.rb +5 -1
- data/examples/sinatra/components/HomePage/HomePage.jsx +16 -0
- data/examples/sinatra/components/OtherPage/OtherPage.jsx +14 -0
- data/examples/sinatra/components/home.js +5 -0
- data/examples/sinatra/public/index.html +14 -0
- data/examples/sinatra/views/layout.erb +3 -1
- data/ext/default.package.json +13 -0
- data/ext/tilt-react-server.js +6 -0
- data/ext/tilt-react.js +120 -12
- data/ext/webpack.config.js +47 -0
- data/lib/sinatra/react.rb +24 -21
- data/lib/tilt/react.rb +17 -64
- data/lib/tilt/react/tasks.rb +59 -0
- data/lib/tilt/react/version.rb +1 -1
- data/tilt-react.gemspec +2 -2
- metadata +52 -48
- data/examples/sinatra/components/home_page.jsx +0 -13
- data/examples/sinatra/package.json +0 -7
- data/ext/tilt-react-client.js +0 -0
- data/lib/commonjs/require_string.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjM5Mjk2ZjUxMWI0NDAzYjMzZTkwNzllYTY1NWI5OTJiY2I0MDZhYQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MjkxZmRhYjRhMDVjNzQxMmNlZjhiZDBjMzlkYjEyZmY0NzQ3ZGVmZA==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MWU5MGY4ZWMzNjFiNTg0ZWU2MDhiOWU4NTBmNzhjMDI1OWQ5NGI0ZGUzNjQ5
|
10
|
+
YzViZWJmYmM2YTgwZTg5ZTg3M2EwN2VkYzFjMjZiYzAzMTY2ZWU2MDgzZjZk
|
11
|
+
M2E2YmIwZDQxMDg1ZDZmM2YyYTEyMDgzYTI5ZDUwY2JlOTQyNTM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Njk5OTRjZDg1ODIwZTQyMWY3NmZmNGFiZTA4YWMxMGZiMGE3NzJhN2RkMDMw
|
14
|
+
ZmI3Y2Q3MDI0Mjg1Mzc4MTA4MTliMjJhMDdjM2RhOTZkNWZkMzBiMDkwNDk2
|
15
|
+
NDZiMzkyNTljNzMwY2EyYzkyZDEzNzEyMzJhYzkwZDllYjNjNGY=
|
data/.travis.yml
CHANGED
@@ -4,10 +4,11 @@ rvm:
|
|
4
4
|
deploy:
|
5
5
|
provider: rubygems
|
6
6
|
api_key:
|
7
|
+
secure: pVtlk2B2iWhYtZmUApIe6H8RHDjiaZWUTjCHnyPKQmkIeB4wC+Mu/Cr1ykqXUwko6rOMz0BllNn5J/DIX4hNBzYZnI7JWRM7rpV11Ni76rWXU+RxcQ07XtnZeBwb6PSZLsnt2x0T5CqJjidcJJo3IiQVz3DrRGtX6bOpMV9nf5ySjOwBq/PyHzaOi4vCdH+t3KDco4R1ppCz+AXuKry3FNm+HWxsRQOsswerEi9wWGJClq07W8/IRPhL5AGbML5hS1VbKtR0WCQyz4uax1hvty01VsEwijTiwui6ed0wtXjU/rnKZ/cTYDV+U0TyiGchclvlCd5IBlsNmck+9LQjsJcvxMbCtG0dMuOhwwmxvVG+MqBGIEFmR+eKQPa+PNuWb1ApRTT7/Jh8u5YcVBhiKqzZ3MJxCg8Gl+NVlN/SPIfWOgj4KWqJI9w73lGiw7mSbh9Vp1n0eV3LmVeZgza+Hgk+ATztZupyGPyiKZ9WUxEppVJvAp5+/YQPompklaSkL84JccH+4zkD+62nX7oltjS8f+mSj8JeubZ0X+bfYD6hVYc7rkNVnYVzj4Moa1OO68zM5OYAAM8e9J8gvnjhuBElhWb/feVh0VOlWhP8qxjO/dYdVVAZ6LJAHg0zEK9YPiabaNMZ6oxhRZVBWSd07yW0NzmYm3jG2emDZdDxfxM=
|
7
8
|
gem: tilt-react
|
8
9
|
on:
|
9
10
|
tags: true
|
10
11
|
repo: jphastings/tilt-react
|
11
12
|
addons:
|
12
13
|
code_climate:
|
13
|
-
secure:
|
14
|
+
secure: NLJ1KBW8QcBQLY2R8qYgr+M4MgaGr1NZFBBffPvsQf/fqHo+n26B+z42SoMQIUaGm40Y9fDB3FgloumG5GGCNsdQ+6mIec2948Fjh9BsBy8ZDdpOypcsX5cAtBwHAhOa1tUv/nOU1udMEExT9dFQ7FCae+Z/ivw2EvpgaZuOd5bDwZWvnPiDAybVwlwusTWOPxQhTR3OEd1y0WnXfoVtdT0ACdp6mLxzy7ERs8uEuoOBHq/Q4Ai807beseNtZs9H+2K4L48fi3u65/p7c9WpyzCUVeEpUXPBjLl43NzWXqC8Zj6sOL5gRzj4gV3y7a+NTBzDyOHEAqNvnNe83AHS38AMLidvbmnLWSf2GvKBHr9oSdiQzyBxiHVdZXgP/1FjrVu2JC7MAYVKz5va9Pri4Ey38qjdT1JrR+9meXCq9nTMyPVWxdiO6AysoXBG+7YaNqZjvvxpT60UfKc9pzYf+YxHNEgmYvK0AbjiY2A+wX7f5+mT/BgwKIr6LHSy136Y9IuduUKfuhsdeKn1LBD68ox6pmB24oz2XNJPJ2cLPgIARGMj55ZYg/3zrccaqa/sHBp/XvQ7r+MsR6gCZ7Kedfhevq7JimSsbN6FkNrzIPlzAMxOIQCuSLtwc52FVMMrznmihbN2TmWssdBvAyua5dkqlwqgOUYKDP69m3gI2Bc=
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
Use react components as view templates in Sinatra and other frameworks using Tilt.
|
4
4
|
|
5
|
+
This new imroved version uses webpack; which means both server and client side render is supported! There are helper functions which will allow single-page-like AJAX re-rendering of the page with History API integration.
|
6
|
+
|
5
7
|
Bonus feature: RSpec::React, a helpful framework for writing unit tests for the server generated HTML your components produce.
|
6
8
|
|
7
9
|
This is proof-of-concept code, there are some limitations:
|
8
10
|
|
9
|
-
* Only server-side rendering is supported at the moment
|
10
11
|
* There's some hacky code around the place (search for FIXME)
|
11
12
|
* There aren't any tests for the actual library…
|
12
13
|
|
data/Rakefile
CHANGED
data/examples/sinatra/.gitignore
CHANGED
@@ -2,18 +2,13 @@ PATH
|
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
4
|
tilt-react (0.1.0)
|
5
|
-
|
6
|
-
commonjs (~> 0.2)
|
5
|
+
rack-accept (~> 0.4)
|
7
6
|
therubyracer (~> 0.12)
|
8
7
|
|
9
8
|
GEM
|
10
9
|
remote: https://rubygems.org/
|
11
10
|
specs:
|
12
11
|
addressable (2.4.0)
|
13
|
-
babel-source (5.8.35)
|
14
|
-
babel-transpiler (0.7.0)
|
15
|
-
babel-source (>= 4.0, < 6)
|
16
|
-
execjs (~> 2.0)
|
17
12
|
capybara (2.6.2)
|
18
13
|
addressable
|
19
14
|
mime-types (>= 1.16)
|
@@ -21,9 +16,7 @@ GEM
|
|
21
16
|
rack (>= 1.0.0)
|
22
17
|
rack-test (>= 0.5.4)
|
23
18
|
xpath (~> 2.0)
|
24
|
-
commonjs (0.2.7)
|
25
19
|
diff-lcs (1.2.5)
|
26
|
-
execjs (2.6.0)
|
27
20
|
libv8 (3.16.14.13)
|
28
21
|
mime-types (3.0)
|
29
22
|
mime-types-data (~> 3.2015)
|
@@ -32,6 +25,8 @@ GEM
|
|
32
25
|
nokogiri (1.6.7.2)
|
33
26
|
mini_portile2 (~> 2.0.0.rc2)
|
34
27
|
rack (1.6.4)
|
28
|
+
rack-accept (0.4.5)
|
29
|
+
rack (>= 0.4)
|
35
30
|
rack-protection (1.5.3)
|
36
31
|
rack
|
37
32
|
rack-test (0.6.3)
|
@@ -76,3 +71,6 @@ DEPENDENCIES
|
|
76
71
|
rspec-its (~> 1.2)
|
77
72
|
sinatra (~> 1.4)
|
78
73
|
tilt-react!
|
74
|
+
|
75
|
+
BUNDLED WITH
|
76
|
+
1.11.2
|
data/examples/sinatra/README.md
CHANGED
@@ -4,16 +4,19 @@ This example app demonstrates how to use React Components as view templates in y
|
|
4
4
|
|
5
5
|
## Go go gadget example app
|
6
6
|
|
7
|
-
You'll need to download
|
7
|
+
You'll need to download NPM in order to get this example to work. [Install NPM](https://docs.npmjs.com/getting-started/installing-node), run the setup tasks, then run the app:
|
8
8
|
|
9
9
|
```bash
|
10
|
-
npm
|
10
|
+
rake react:setup:npm
|
11
|
+
rake react:compile
|
11
12
|
rackup
|
12
13
|
```
|
13
14
|
You should be set! Try visiting http://127.0.0.1:9292/?name=world
|
14
15
|
|
15
16
|
## Writing unit tests for your components
|
16
17
|
|
18
|
+
_Tests aren't working right now - webpack support is a WIP!_
|
19
|
+
|
17
20
|
There are also simple tests demonstrating how to write unit tests for the HTML your components produce.
|
18
21
|
|
19
22
|
If you declare `type: :component` on the root `describe` block of your test suite, then that component is loaded, rendered with the `props` given, and the subject of the test becomes a Nokogiri HTML object, which allows easy assertion with `rspec-its` etc.
|
data/examples/sinatra/app.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
import React, { PropTypes } from 'react';
|
2
|
+
|
3
|
+
class HomePage extends React.Component {
|
4
|
+
render() {
|
5
|
+
return (
|
6
|
+
<section>
|
7
|
+
<h1>Hello {this.props.name || 'you'}</h1>
|
8
|
+
<p>This is being rendered with React. Head to <a href="/other" onClick={window.TiltReact.ajaxLoad}>another page</a>.</p>
|
9
|
+
</section>
|
10
|
+
);
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
HomePage.propTypes = { name: PropTypes.string };
|
15
|
+
|
16
|
+
export default HomePage;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import React, { PropTypes } from 'react';
|
2
|
+
|
3
|
+
class OtherPage extends React.Component {
|
4
|
+
render() {
|
5
|
+
return (
|
6
|
+
<section>
|
7
|
+
<h1>Another page</h1>
|
8
|
+
<p>Go back <a href="/" onClick={window.TiltReact.ajaxLoad}>Home</a>.</p>
|
9
|
+
</section>
|
10
|
+
);
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
export default OtherPage;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
|
+
<title>Title Page</title>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<div id="container"></div>
|
11
|
+
<script src="js/tilt_react_client_bundle.js"></script>
|
12
|
+
<script src="js/home_bundle.js"></script>
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"dependencies": {
|
3
|
+
"babel-core": "^6.7.7",
|
4
|
+
"babel-loader": "^6.2.4",
|
5
|
+
"babel-preset-es2015": "^6.6.0",
|
6
|
+
"babel-preset-react": "^6.5.0",
|
7
|
+
"fbjs": "0.8",
|
8
|
+
"glob": "^7.0.3",
|
9
|
+
"react": "^15.0",
|
10
|
+
"react-dom": "^15.0",
|
11
|
+
"webpack": "^1.13.0"
|
12
|
+
}
|
13
|
+
}
|
data/ext/tilt-react.js
CHANGED
@@ -1,12 +1,120 @@
|
|
1
|
-
React
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
import React from 'react';
|
2
|
+
import ReactDOM from 'react-dom';
|
3
|
+
|
4
|
+
class TiltReactClass {
|
5
|
+
constructor() {
|
6
|
+
this.components = {};
|
7
|
+
}
|
8
|
+
|
9
|
+
bind() {
|
10
|
+
this.reactContainers().forEach(container => {
|
11
|
+
const componentName = container.dataset.reactClass;
|
12
|
+
const propsContainer = container.nextElementSibling;
|
13
|
+
const props = JSON.parse(propsContainer.innerText);
|
14
|
+
this.render(container, componentName, props);
|
15
|
+
container.parentElement.removeChild(propsContainer);
|
16
|
+
});
|
17
|
+
|
18
|
+
window.addEventListener('popstate', event => {
|
19
|
+
const containers = this.reactContainers();
|
20
|
+
try {
|
21
|
+
event.state.forEach((data, i) => {
|
22
|
+
this.render(
|
23
|
+
containers[i],
|
24
|
+
data[0],
|
25
|
+
JSON.parse(data[1]) || {}
|
26
|
+
);
|
27
|
+
});
|
28
|
+
} catch(e) {
|
29
|
+
// State wasn't in the correct format
|
30
|
+
}
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
render(container, componentName, props, pathChange) {
|
35
|
+
const element = this.elementForComponent(componentName, props);
|
36
|
+
ReactDOM.render(element, container);
|
37
|
+
container.dataset.reactClass = componentName;
|
38
|
+
container.dataset.props = JSON.stringify(props);
|
39
|
+
|
40
|
+
if (typeof pathChange === 'undefined') {
|
41
|
+
window.history.replaceState(
|
42
|
+
this.pageState(),
|
43
|
+
document.title,
|
44
|
+
window.location.href
|
45
|
+
);
|
46
|
+
} else {
|
47
|
+
window.history.pushState(
|
48
|
+
this.pageState(),
|
49
|
+
document.title,
|
50
|
+
pathChange
|
51
|
+
);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
elementForComponent(componentName, props) {
|
56
|
+
const component = this.components[componentName];
|
57
|
+
const element = React.createElement(component, props);
|
58
|
+
return element;
|
59
|
+
}
|
60
|
+
|
61
|
+
addComponent(component) {
|
62
|
+
this.components[component.name] = component;
|
63
|
+
}
|
64
|
+
|
65
|
+
componentNames() {
|
66
|
+
return Object.keys(this.components);
|
67
|
+
}
|
68
|
+
|
69
|
+
reactContainers() {
|
70
|
+
return Array.prototype.slice.call(document.querySelectorAll('div[data-react-class]'));
|
71
|
+
}
|
72
|
+
|
73
|
+
reactContainerFor(element) {
|
74
|
+
while(typeof element !== 'null') {
|
75
|
+
if (element.hasAttribute('data-react-class')) {
|
76
|
+
return element;
|
77
|
+
}
|
78
|
+
element = element.parentElement;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
pageState() {
|
83
|
+
return this.reactContainers().map(container => {
|
84
|
+
return [
|
85
|
+
container.dataset.reactClass,
|
86
|
+
container.dataset.props
|
87
|
+
];
|
88
|
+
});
|
89
|
+
}
|
90
|
+
|
91
|
+
fetch(path, options, targetContainer) {
|
92
|
+
targetContainer = targetContainer || this.reactContainers()[0];
|
93
|
+
options = options || {};
|
94
|
+
options.headers = options.headers || {};
|
95
|
+
options.headers.Accept = 'application/json';
|
96
|
+
|
97
|
+
fetch(path, options).then(response => {
|
98
|
+
return response.json();
|
99
|
+
}).then(json => {
|
100
|
+
this.render(targetContainer, json[0], json[1], path);
|
101
|
+
});
|
102
|
+
}
|
103
|
+
|
104
|
+
ajaxLoad(event) {
|
105
|
+
event.preventDefault();
|
106
|
+
switch(event.type) {
|
107
|
+
case 'click':
|
108
|
+
TiltReact.fetch(
|
109
|
+
event.target.href,
|
110
|
+
{ method: 'GET' },
|
111
|
+
TiltReact.reactContainerFor(event.target)
|
112
|
+
);
|
113
|
+
return;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
window.TiltReact = new TiltReactClass();
|
119
|
+
|
120
|
+
module.exports = window.TiltReact;
|
@@ -0,0 +1,47 @@
|
|
1
|
+
var path = require('path');
|
2
|
+
var glob = require('glob')
|
3
|
+
var webpack = require('webpack');
|
4
|
+
|
5
|
+
function entryPoints(dir) {
|
6
|
+
var entryPoints = {};
|
7
|
+
var opts = {
|
8
|
+
nosort: true,
|
9
|
+
realpath: true,
|
10
|
+
};
|
11
|
+
glob.sync(path.join(dir, '*.js'), opts).forEach(function (file) {
|
12
|
+
var parts = file.match(/\/([^\/]+).js$/);
|
13
|
+
if (parts[1] !== null) {
|
14
|
+
entryPoints[parts[1]] = file;
|
15
|
+
}
|
16
|
+
});
|
17
|
+
return entryPoints;
|
18
|
+
}
|
19
|
+
|
20
|
+
module.exports = {
|
21
|
+
entry: entryPoints('./components'),
|
22
|
+
output: {
|
23
|
+
path: path.join(__dirname, 'public/js'),
|
24
|
+
filename: '[name]_bundle.js'
|
25
|
+
},
|
26
|
+
module: {
|
27
|
+
loaders: [
|
28
|
+
{
|
29
|
+
test: /.jsx?/,
|
30
|
+
loader: 'babel-loader',
|
31
|
+
exclude: /node_modules/,
|
32
|
+
query: {
|
33
|
+
presets: ['es2015', 'react'],
|
34
|
+
},
|
35
|
+
}
|
36
|
+
]
|
37
|
+
},
|
38
|
+
plugins: [
|
39
|
+
// Avoid publishing files when compilation fails
|
40
|
+
new webpack.NoErrorsPlugin(),
|
41
|
+
// new webpack.optimize.UglifyJsPlugin({minimize: true}),
|
42
|
+
],
|
43
|
+
stats: {
|
44
|
+
colors: true
|
45
|
+
},
|
46
|
+
devtool: 'source-map',
|
47
|
+
};
|
data/lib/sinatra/react.rb
CHANGED
@@ -1,44 +1,47 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'tilt/react'
|
3
|
+
require 'rack/accept'
|
3
4
|
|
4
5
|
module Sinatra
|
5
6
|
module React
|
6
7
|
module Helpers
|
7
|
-
def react(
|
8
|
-
|
8
|
+
def react(component, opts = {})
|
9
|
+
req = Rack::Accept::MediaType.new(request.env['HTTP_ACCEPT'])
|
10
|
+
if req.accept?('text/html')
|
11
|
+
return render :jsx, component, opts
|
12
|
+
elsif req.accept?('application/json')
|
13
|
+
content_type :json
|
14
|
+
return json_for_props(component, opts[:locals])
|
15
|
+
else
|
16
|
+
halt 406
|
17
|
+
end
|
9
18
|
end
|
10
19
|
|
11
20
|
def find_template(views, name, engine, &block)
|
12
21
|
if engine == Tilt::ReactTemplate
|
13
|
-
views =
|
22
|
+
views = Tilt::ReactTemplate.components
|
14
23
|
end
|
15
24
|
super(views, name, engine, &block)
|
16
25
|
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def json_for_props(component, props)
|
30
|
+
[
|
31
|
+
Tilt::ReactTemplate.file_to_class_name(component.to_s),
|
32
|
+
props
|
33
|
+
].to_json
|
34
|
+
end
|
17
35
|
end
|
18
36
|
|
19
37
|
def self.registered(app)
|
20
38
|
app.helpers(Helpers)
|
21
|
-
app.set :
|
22
|
-
app.set :js_libs, Proc.new { root && File.join(root, 'node_modules') }
|
23
|
-
app.set :component_bundles, { '*.jsx' => 'js/components.js' }
|
39
|
+
app.set :bundles_glob, 'public/js/*_bundle.js'
|
24
40
|
app.set :jsx, layout_engine: :erb
|
25
41
|
|
26
42
|
app.configure do |config|
|
27
|
-
|
28
|
-
|
29
|
-
# time with them being what they should be. Because this block configures the
|
30
|
-
# JS context calling it without js_libs means no libraries are loaded, so we
|
31
|
-
# must wait until js_libs has content.
|
32
|
-
next unless config.settings.js_libs
|
33
|
-
|
34
|
-
Tilt::ReactTemplate.js_libs = config.settings.js_libs
|
35
|
-
Tilt::ReactTemplate.load_context
|
36
|
-
Tilt::ReactTemplate.compile_bundles(config.settings.component_bundles.map { |glob, bundle|
|
37
|
-
[
|
38
|
-
File.expand_path(glob, config.settings.components),
|
39
|
-
File.expand_path(bundle, config.settings.public_folder)
|
40
|
-
]
|
41
|
-
})
|
43
|
+
Tilt::ReactTemplate.prepare_context
|
44
|
+
Tilt::ReactTemplate.load_directory(config.settings.bundles_glob)
|
42
45
|
end
|
43
46
|
end
|
44
47
|
end
|
data/lib/tilt/react.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'tilt/react/version'
|
2
2
|
require 'tilt'
|
3
3
|
require 'fileutils'
|
4
|
-
require 'commonjs'
|
5
|
-
require 'commonjs/require_string'
|
6
4
|
require 'json'
|
7
5
|
require 'v8'
|
8
6
|
|
@@ -10,87 +8,42 @@ module Tilt
|
|
10
8
|
class ReactTemplate < Template
|
11
9
|
def initialize(file=nil, line=1, options={}, &block)
|
12
10
|
file = File.expand_path(file)
|
13
|
-
@bundle = @@bundle_lookup[file]
|
14
|
-
raise "The requested component has not be loaded, or does not exist: #{file}" unless @bundle
|
15
11
|
@component_class = File.basename(file, '.jsx').split('_').map(&:capitalize).join
|
16
12
|
end
|
17
13
|
|
18
14
|
def evaluate(scope, props, &block)
|
19
15
|
@output ||= begin
|
20
|
-
component = @@
|
16
|
+
component = @@renderer.renderToString(@component_class, props)
|
21
17
|
%{<div data-react-class="#{@component_class}">#{component}</div><script data-react-class="#{@component_class}" type="application/json">#{props.to_json}</script>}
|
22
18
|
end
|
23
19
|
end
|
24
20
|
|
25
|
-
def self.
|
26
|
-
@@context
|
21
|
+
def self.prepare_context
|
22
|
+
@@context = V8::Context.new
|
23
|
+
@@context.eval('window = {};')
|
27
24
|
end
|
28
25
|
|
29
|
-
def self.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
]
|
36
|
-
}.flatten.compact
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.load_context
|
40
|
-
# Include the tilt-react bootstrap JS
|
41
|
-
all_libs = @js_libs + [File.expand_path('../../ext/', __dir__)]
|
42
|
-
|
43
|
-
@@context = CommonJS::Environment.new(
|
44
|
-
V8::Context.new,
|
45
|
-
path: all_libs
|
46
|
-
).tap { |env|
|
47
|
-
env.runtime[:process] = { 'env' => ENV }
|
48
|
-
@@tilt_react_js = env.require('tilt-react')
|
49
|
-
}
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.compile_bundles(bundles)
|
53
|
-
bundles.each do |selector, target|
|
54
|
-
files = case selector
|
55
|
-
when Enumerable then selector
|
56
|
-
when String then Dir.glob(selector)
|
57
|
-
else raise "Cannot coerce into list of files: #{selector}"
|
58
|
-
end
|
59
|
-
|
60
|
-
if files.any?
|
61
|
-
FileUtils.mkdir_p(File.dirname(target))
|
62
|
-
|
63
|
-
compile(files).each do |file, es5|
|
64
|
-
@@bundle_lookup[file] = target
|
65
|
-
end
|
66
|
-
|
67
|
-
# FIXME: Browserify the es5
|
68
|
-
File.open(target, 'w') {}
|
26
|
+
def self.load_directory(glob)
|
27
|
+
Dir.glob(glob).to_a.sort_by { |bundle|
|
28
|
+
case bundle
|
29
|
+
when %r{/tilt_react_client_bundle.js$} then 0
|
30
|
+
when %r{/tilt_react_server_bundle.js$} then 1
|
31
|
+
else 2
|
69
32
|
end
|
33
|
+
}.each do |bundle|
|
34
|
+
@@context.load(bundle)
|
70
35
|
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.compile(files)
|
74
|
-
@@bundle_lookup ||= {}
|
75
36
|
|
76
|
-
|
77
|
-
file = File.expand_path(file)
|
78
|
-
next if @@bundle_lookup[file]
|
79
|
-
|
80
|
-
export_as = file_to_class_name(file)
|
81
|
-
template = Tilt::BabelTemplate.new(file)
|
82
|
-
es5 = template.render
|
83
|
-
@@tilt_react_js.components[export_as] = context.require_string(export_as, es5)
|
84
|
-
@@bundle_lookup[file] = true
|
85
|
-
[file, es5]
|
86
|
-
end
|
37
|
+
@@renderer = @@context.eval('window.TiltReact')
|
87
38
|
end
|
88
39
|
|
89
|
-
private
|
90
|
-
|
91
40
|
def self.file_to_class_name(file)
|
92
41
|
File.basename(file, '.jsx').split('_').map(&:capitalize).join
|
93
42
|
end
|
43
|
+
|
44
|
+
def self.components
|
45
|
+
@@context.eval('window.TiltReact.componentNames()').to_a
|
46
|
+
end
|
94
47
|
end
|
95
48
|
end
|
96
49
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
namespace :react do
|
4
|
+
desc "Compiles components according to the webpack config."
|
5
|
+
task compile: :'setup:webpack' do
|
6
|
+
puts "Running webpack…"
|
7
|
+
|
8
|
+
libs = {
|
9
|
+
tilt_react_client: File.expand_path('../../../ext/tilt-react.js', __dir__),
|
10
|
+
tilt_react_server: File.expand_path('../../../ext/tilt-react-server.js', __dir__),
|
11
|
+
}
|
12
|
+
|
13
|
+
libs.each do |file, source|
|
14
|
+
FileUtils.cp(source, "components/#{file}.js")
|
15
|
+
end
|
16
|
+
|
17
|
+
%x[webpack]
|
18
|
+
|
19
|
+
libs.each do |file, _|
|
20
|
+
FileUtils.rm("components/#{file}.js")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Compiles components according to the webpack config."
|
25
|
+
task watch: :'setup:webpack' do
|
26
|
+
puts "Watching for changes in components…"
|
27
|
+
%x[webpack --watch]
|
28
|
+
end
|
29
|
+
|
30
|
+
namespace :setup do
|
31
|
+
desc "Installs all required npm packages."
|
32
|
+
task npm: :package do
|
33
|
+
puts "Ensuring all NPM packages are installed…"
|
34
|
+
%x[npm install]
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Copies the default package.json to the current directory."
|
38
|
+
task :package do
|
39
|
+
default_package = File.expand_path('../../../ext/default.package.json', __dir__)
|
40
|
+
local_package = File.expand_path('./package.json')
|
41
|
+
|
42
|
+
if !File.exist?(local_package)
|
43
|
+
FileUtils.cp(default_package, local_package)
|
44
|
+
puts "Default package.json copied to local directory"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Copies the default webpack.config.json to the current directory."
|
49
|
+
task :webpack do
|
50
|
+
default_config = File.expand_path('../../../ext/webpack.config.js', __dir__)
|
51
|
+
local_config = File.expand_path('./webpack.config.js')
|
52
|
+
|
53
|
+
if !File.exist?(local_config)
|
54
|
+
FileUtils.cp(default_config, local_config)
|
55
|
+
puts "Default webpack.config.js copied to local directory"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/tilt/react/version.rb
CHANGED
data/tilt-react.gemspec
CHANGED
@@ -19,12 +19,12 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency '
|
23
|
-
spec.add_dependency 'commonjs', '~> 0.2'
|
22
|
+
spec.add_dependency 'rack-accept', '~> 0.4'
|
24
23
|
spec.add_dependency 'therubyracer', '~> 0.12'
|
25
24
|
|
26
25
|
spec.add_development_dependency 'bundler', '~> 1.9'
|
27
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.4'
|
28
28
|
spec.add_development_dependency 'nokogiri', '~> 1.6'
|
29
29
|
spec.add_development_dependency 'capybara', '~> 2.6'
|
30
30
|
spec.add_development_dependency 'rspec-as_fixture', '~> 0.1'
|
metadata
CHANGED
@@ -1,125 +1,125 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tilt-react
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- JP Hastings-Spital
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rack-accept
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.4'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: commonjs
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.2'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.2'
|
26
|
+
version: '0.4'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: therubyracer
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
|
-
- -
|
31
|
+
- - ~>
|
46
32
|
- !ruby/object:Gem::Version
|
47
33
|
version: '0.12'
|
48
34
|
type: :runtime
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
|
-
- -
|
38
|
+
- - ~>
|
53
39
|
- !ruby/object:Gem::Version
|
54
40
|
version: '0.12'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: bundler
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
|
-
- -
|
45
|
+
- - ~>
|
60
46
|
- !ruby/object:Gem::Version
|
61
47
|
version: '1.9'
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
|
-
- -
|
52
|
+
- - ~>
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '1.9'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: rake
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
|
-
- -
|
59
|
+
- - ~>
|
74
60
|
- !ruby/object:Gem::Version
|
75
61
|
version: '10.0'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
|
-
- -
|
66
|
+
- - ~>
|
81
67
|
- !ruby/object:Gem::Version
|
82
68
|
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.4'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: nokogiri
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ~>
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '1.6'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ~>
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '1.6'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: capybara
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - ~>
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '2.6'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - ~>
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '2.6'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: rspec-as_fixture
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ~>
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0.1'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- -
|
122
|
+
- - ~>
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0.1'
|
125
125
|
description: Render React.js JSX files with the Tilt templating system.
|
@@ -129,13 +129,13 @@ executables: []
|
|
129
129
|
extensions: []
|
130
130
|
extra_rdoc_files: []
|
131
131
|
files:
|
132
|
-
-
|
133
|
-
-
|
134
|
-
-
|
135
|
-
-
|
136
|
-
-
|
137
|
-
-
|
138
|
-
-
|
132
|
+
- .codeclimate.yml
|
133
|
+
- .eslintignore
|
134
|
+
- .eslintrc
|
135
|
+
- .gitignore
|
136
|
+
- .rspec
|
137
|
+
- .rubocop.yml
|
138
|
+
- .travis.yml
|
139
139
|
- CODE_OF_CONDUCT.md
|
140
140
|
- Gemfile
|
141
141
|
- LICENSE.txt
|
@@ -148,21 +148,26 @@ files:
|
|
148
148
|
- examples/sinatra/Gemfile
|
149
149
|
- examples/sinatra/Gemfile.lock
|
150
150
|
- examples/sinatra/README.md
|
151
|
+
- examples/sinatra/Rakefile
|
151
152
|
- examples/sinatra/app.rb
|
152
|
-
- examples/sinatra/components/
|
153
|
+
- examples/sinatra/components/HomePage/HomePage.jsx
|
154
|
+
- examples/sinatra/components/OtherPage/OtherPage.jsx
|
155
|
+
- examples/sinatra/components/home.js
|
153
156
|
- examples/sinatra/config.ru
|
154
|
-
- examples/sinatra/
|
157
|
+
- examples/sinatra/public/index.html
|
155
158
|
- examples/sinatra/spec/fixtures/home_page.yml
|
156
159
|
- examples/sinatra/spec/home_page_spec.rb
|
157
160
|
- examples/sinatra/spec/spec_helper.rb
|
158
161
|
- examples/sinatra/views/layout.erb
|
159
|
-
- ext/
|
162
|
+
- ext/default.package.json
|
163
|
+
- ext/tilt-react-server.js
|
160
164
|
- ext/tilt-react.js
|
161
|
-
-
|
165
|
+
- ext/webpack.config.js
|
162
166
|
- lib/rspec/react.rb
|
163
167
|
- lib/rspec/react/component_helpers.rb
|
164
168
|
- lib/sinatra/react.rb
|
165
169
|
- lib/tilt/react.rb
|
170
|
+
- lib/tilt/react/tasks.rb
|
166
171
|
- lib/tilt/react/version.rb
|
167
172
|
- tilt-react.gemspec
|
168
173
|
homepage: https://github.com/jphastings/tilt-react
|
@@ -175,20 +180,19 @@ require_paths:
|
|
175
180
|
- lib
|
176
181
|
required_ruby_version: !ruby/object:Gem::Requirement
|
177
182
|
requirements:
|
178
|
-
- -
|
183
|
+
- - ! '>='
|
179
184
|
- !ruby/object:Gem::Version
|
180
185
|
version: '0'
|
181
186
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
187
|
requirements:
|
183
|
-
- -
|
188
|
+
- - ! '>='
|
184
189
|
- !ruby/object:Gem::Version
|
185
190
|
version: '0'
|
186
191
|
requirements: []
|
187
192
|
rubyforge_project:
|
188
|
-
rubygems_version: 2.4.5
|
193
|
+
rubygems_version: 2.4.5
|
189
194
|
signing_key:
|
190
195
|
specification_version: 4
|
191
196
|
summary: Use React.js JSX files as view templates in Sinatra and other Tilt powered
|
192
197
|
frameworks.
|
193
198
|
test_files: []
|
194
|
-
has_rdoc:
|
data/ext/tilt-react-client.js
DELETED
File without changes
|
@@ -1,12 +0,0 @@
|
|
1
|
-
module CommonJS
|
2
|
-
class Environment
|
3
|
-
def require_string(module_id, js)
|
4
|
-
raise "#{module_id} has already been loaded" if @modules[module_id]
|
5
|
-
load_js = "( function(module, require, exports) {\n#{js}\n} )"
|
6
|
-
load = @runtime.eval(load_js)
|
7
|
-
@modules[module_id] = mod = Module.new(module_id, self)
|
8
|
-
load.call(mod, mod.require_function, mod.exports)
|
9
|
-
mod.exports
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|