tilt-react 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: bbcaf6f0207e3e10892e7770f8717513f490095f
4
- data.tar.gz: 329c3140e578f25104e710ab89d62a66e71e9337
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZjM5Mjk2ZjUxMWI0NDAzYjMzZTkwNzllYTY1NWI5OTJiY2I0MDZhYQ==
5
+ data.tar.gz: !binary |-
6
+ MjkxZmRhYjRhMDVjNzQxMmNlZjhiZDBjMzlkYjEyZmY0NzQ3ZGVmZA==
5
7
  SHA512:
6
- metadata.gz: c4f15fca0cc8a5f690b95229488c96ca181d3c9e7922aff80f701a8b1108f098a624d30ea6280b238716c4887fea1a3cd17b5280e967c32b6113101a035deb97
7
- data.tar.gz: 81b158738e68e6b75862dbf1e507894e10117454c2f5793821dd4703843fd3ff31633d114a1fbd830cbf1517d4b089f28de2ae89739280bd0ee175fa23e13360
8
+ metadata.gz: !binary |-
9
+ MWU5MGY4ZWMzNjFiNTg0ZWU2MDhiOWU4NTBmNzhjMDI1OWQ5NGI0ZGUzNjQ5
10
+ YzViZWJmYmM2YTgwZTg5ZTg3M2EwN2VkYzFjMjZiYzAzMTY2ZWU2MDgzZjZk
11
+ M2E2YmIwZDQxMDg1ZDZmM2YyYTEyMDgzYTI5ZDUwY2JlOTQyNTM=
12
+ data.tar.gz: !binary |-
13
+ Njk5OTRjZDg1ODIwZTQyMWY3NmZmNGFiZTA4YWMxMGZiMGE3NzJhN2RkMDMw
14
+ ZmI3Y2Q3MDI0Mjg1Mzc4MTA4MTliMjJhMDdjM2RhOTZkNWZkMzBiMDkwNDk2
15
+ NDZiMzkyNTljNzMwY2EyYzkyZDEzNzEyMzJhYzkwZDllYjNjNGY=
@@ -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: "NLJ1KBW8QcBQLY2R8qYgr+M4MgaGr1NZFBBffPvsQf/fqHo+n26B+z42SoMQIUaGm40Y9fDB3FgloumG5GGCNsdQ+6mIec2948Fjh9BsBy8ZDdpOypcsX5cAtBwHAhOa1tUv/nOU1udMEExT9dFQ7FCae+Z/ivw2EvpgaZuOd5bDwZWvnPiDAybVwlwusTWOPxQhTR3OEd1y0WnXfoVtdT0ACdp6mLxzy7ERs8uEuoOBHq/Q4Ai807beseNtZs9H+2K4L48fi3u65/p7c9WpyzCUVeEpUXPBjLl43NzWXqC8Zj6sOL5gRzj4gV3y7a+NTBzDyOHEAqNvnNe83AHS38AMLidvbmnLWSf2GvKBHr9oSdiQzyBxiHVdZXgP/1FjrVu2JC7MAYVKz5va9Pri4Ey38qjdT1JrR+9meXCq9nTMyPVWxdiO6AysoXBG+7YaNqZjvvxpT60UfKc9pzYf+YxHNEgmYvK0AbjiY2A+wX7f5+mT/BgwKIr6LHSy136Y9IuduUKfuhsdeKn1LBD68ox6pmB24oz2XNJPJ2cLPgIARGMj55ZYg/3zrccaqa/sHBp/XvQ7r+MsR6gCZ7Kedfhevq7JimSsbN6FkNrzIPlzAMxOIQCuSLtwc52FVMMrznmihbN2TmWssdBvAyua5dkqlwqgOUYKDP69m3gI2Bc="
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
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in tilt-react.gemspec
4
4
  gemspec
5
+
6
+ gem "codeclimate-test-reporter", group: :test, require: nil
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
@@ -1 +1,7 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :default => :test
5
+ task :test => :spec
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
@@ -1,2 +1,4 @@
1
1
  /node_modules/
2
- /public/js/components.js
2
+ /public/js/*
3
+ package.json
4
+ webpack.config.js
@@ -2,18 +2,13 @@ PATH
2
2
  remote: ../..
3
3
  specs:
4
4
  tilt-react (0.1.0)
5
- babel-transpiler (~> 0.7)
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
@@ -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 React in order to get this example to work. [Install NPM](https://docs.npmjs.com/getting-started/installing-node), install the npm dependencies, then run the app:
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 install
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.
@@ -0,0 +1,2 @@
1
+ $:<<'../../lib'
2
+ require 'tilt/react/tasks'
@@ -5,6 +5,10 @@ class ExampleApp < Sinatra::Base
5
5
  register Sinatra::React
6
6
 
7
7
  get '/' do
8
- react :home_page, locals: { name: params['name'] || 'you' }
8
+ react :home_page, locals: { name: params['name'] }
9
+ end
10
+
11
+ get '/other' do
12
+ react :other_page
9
13
  end
10
14
  end
@@ -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,5 @@
1
+ import HomePage from './HomePage';
2
+ import OtherPage from './OtherPage';
3
+
4
+ window.TiltReact.addComponent(HomePage);
5
+ window.TiltReact.addComponent(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>
@@ -8,6 +8,8 @@
8
8
  </head>
9
9
  <body>
10
10
  <%= yield %>
11
- <script src="js/components.js"></script>
11
+ <script src="js/tilt_react_client_bundle.js"></script>
12
+ <script src="js/home_bundle.js"></script>
13
+ <script>TiltReact.bind()</script>
12
14
  </body>
13
15
  </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
+ }
@@ -0,0 +1,6 @@
1
+ import ReactDOMServer from 'react-dom/server';
2
+
3
+ window.TiltReact.renderToString = function(componentName, props) {
4
+ const element = window.TiltReact.elementForComponent(componentName, props);
5
+ return ReactDOMServer.renderToString(element);
6
+ };
@@ -1,12 +1,120 @@
1
- React = require('react');
2
- DOMServer = require('react-dom/server');
3
-
4
- var tiltReact = {
5
- render: function(componentName, props) {
6
- var component = React.createFactory(tiltReact.components[componentName]);
7
- return DOMServer.renderToString(component(props));
8
- },
9
- components: {}
10
- };
11
-
12
- module.exports = tiltReact;
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
+ };
@@ -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(*args)
8
- render :jsx, *args
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 = settings.components
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 :components, Proc.new { root && File.join(root, 'components') }
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
- # FIXME: I don't like this following line. Apparently this block gets called
28
- # multiple times, once without the settings above returning nil, and a second
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
@@ -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 = @@tilt_react_js.render(@component_class, props)
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.context
26
- @@context
21
+ def self.prepare_context
22
+ @@context = V8::Context.new
23
+ @@context.eval('window = {};')
27
24
  end
28
25
 
29
- def self.js_libs=(js_dirs)
30
- # Expand the js_libs to be the root and all folders in it, because CommonJS.rb doesn't do this
31
- @js_libs = [*js_dirs].map { |dir|
32
- [
33
- dir,
34
- Dir.glob("#{dir}/*").select { |f| File.directory? f }
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
- [*files].map do |file|
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
@@ -1,5 +1,5 @@
1
1
  module Tilt
2
2
  module React
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -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 'babel-transpiler', '~> 0.7'
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.1.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-04-15 00:00:00.000000000 Z
11
+ date: 2016-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: babel-transpiler
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.7'
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.7'
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
- - ".codeclimate.yml"
133
- - ".eslintignore"
134
- - ".eslintrc"
135
- - ".gitignore"
136
- - ".rspec"
137
- - ".rubocop.yml"
138
- - ".travis.yml"
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/home_page.jsx
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/package.json
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/tilt-react-client.js
162
+ - ext/default.package.json
163
+ - ext/tilt-react-server.js
160
164
  - ext/tilt-react.js
161
- - lib/commonjs/require_string.rb
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.1
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:
@@ -1,13 +0,0 @@
1
- import React, { PropTypes } from 'react';
2
-
3
- class HomePage extends React.Component {
4
- render() {
5
- return (
6
- <h1>Hello {this.props.name || 'you'}</h1>
7
- );
8
- }
9
- }
10
-
11
- HomePage.propTypes = { name: PropTypes.string };
12
-
13
- export default HomePage;
@@ -1,7 +0,0 @@
1
- {
2
- "dependencies": {
3
- "fbjs": "^0.7",
4
- "react": "^0.14",
5
- "react-dom": "^0.14"
6
- }
7
- }
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