Skip to content

Commit cd9867f

Browse files
committed
Merge pull request #12 from jakubmal/feature/react-dropin
Allow users to override react.js and transformer.js for assets and engine
2 parents 3cbfddd + 1f0d8ce commit cd9867f

File tree

13 files changed

+123
-21
lines changed

13 files changed

+123
-21
lines changed

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@ As with all gem dependencies, we strongly recommend adding `react-rails` to your
1717
```ruby
1818
# Gemfile
1919

20-
gem 'react-rails', '~> 0.8.0.0'
20+
gem 'react-rails', '~> 1.0.0'
2121
```
2222

23-
2423
## Usage
2524

2625
### react.js
2726

28-
In order to use React client-side in your application, you must make sure the browser requests it. One way to do that is to drop `react.js` into `app/assets/javascript/` and by default your application manifest will pick it up. There are downsides to this approach, so we made it even easier. Once you have `react-rails` installed, you can just add a line into your config file (see Configuring) and require react directly in your manifest:
27+
In order to use React client-side in your application, you must make sure the browser requests it. One way to do that is to drop `react.js` into `vendor/assets/javascript/` and by default your application manifest will pick it up. There are downsides to this approach, so we made it even easier. Once you have `react-rails` installed, you can just add a line into your config file (see Configuring) and require react directly in your manifest:
2928

3029
You can `require` it in your manifest:
3130

@@ -129,3 +128,20 @@ Component = React.createClass
129128
`<ExampleComponent videos={this.props.videos} />`
130129
```
131130

131+
### Changing react.js and JSXTransformer.js versions
132+
133+
In some cases you may want to have your `react.js` and `JSXTransformer.js` files come from a different release than the one, that is specified in the `react-rails.gemspec`. To achieve that, you have to manually replace them in your app.
134+
135+
#### Instructions
136+
137+
Just put another version of `react.js` or `JSXTransformer.js` under `/vendor/assets/react` directory.
138+
If you need different versions of `react.js` for production and development, then use a subdirectory named
139+
after `config.react.variant`, e.g. you set `config.react.variant = :development` so for this environment
140+
`react.js` is expected to be in `/vendor/assets/react/development`
141+
142+
#### Things to remember
143+
144+
If you replace `JSXTransformer.js` in production environment, you have to restart your rails instance,
145+
because the jsx compiler context is cached.
146+
147+
Name of the `JSXTransformer.js` file *is case-sensitive*.

lib/react/jsx.rb

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
require 'execjs'
22
require 'react/source'
33
require 'react/jsx/template'
4+
require 'rails'
45

56
module React
67
module JSX
78
def self.context
8-
# TODO: create React::Source::contents_for
9-
contents =
10-
# If execjs uses therubyracer, there is no 'global'. Make sure
11-
# we have it so JSX script can work properly.
12-
'var global = global || this;' +
13-
File.read(React::Source.bundled_path_for('JSXTransformer.js'))
14-
@context ||= ExecJS.compile(contents)
9+
# lazily loaded during first request and reloaded every time when in dev or test
10+
unless @context && ::Rails.env.production?
11+
contents =
12+
# If execjs uses therubyracer, there is no 'global'. Make sure
13+
# we have it so JSX script can work properly.
14+
'var global = global || this;' +
15+
16+
# search for transformer file using sprockets - allows user to override
17+
# this file in his own application
18+
File.read(::Rails.application.assets.resolve('JSXTransformer.js'))
19+
20+
@context = ExecJS.compile(contents)
21+
end
22+
23+
@context
1524
end
1625

1726
def self.transform(code)

lib/react/jsx/template.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Template < Tilt::Template
99
def prepare
1010
end
1111

12-
def evaluate(scopre, locals, &block)
12+
def evaluate(scope, locals, &block)
1313
@output ||= JSX::transform(data)
1414
end
1515
end

lib/react/rails/engine.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module React
22
module Rails
33
class Engine < ::Rails::Engine
4-
initializer "react_rails.setup_engine", :after => "sprockets.environment", :group => :all do |app|
4+
initializer "react_rails.setup_engine", :group => :all do |app|
55
app.assets.register_engine '.jsx', React::JSX::Template
66
end
77
end

lib/react/rails/railtie.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ module Rails
55
class Railtie < ::Rails::Railtie
66
config.react = ActiveSupport::OrderedOptions.new
77

8-
initializer "react_rails.setup_vendor", :after => "sprockets.environment" do |app|
9-
variant = app.config.react.variant
10-
8+
# run after all initializers to allow sprockets to pick up react.js and
9+
# jsxtransformer.js from end-user to override ours if needed
10+
config.after_initialize do |app|
1111
# Mimic behavior of ember-rails...
1212
# We want to include different files in dev/prod. The unminified builds
1313
# contain console logging for invariants and logging to help catch
@@ -26,9 +26,17 @@ class Railtie < ::Rails::Railtie
2626
tmp_path.join('react.js'))
2727
FileUtils.cp(::React::Source.bundled_path_for('JSXTransformer.js'),
2828
tmp_path.join('JSXTransformer.js'))
29+
app.assets.prepend_path tmp_path
30+
31+
# Allow overriding react files that are not based on environment
32+
# e.g. /vendor/assets/react/JSXTransformer.js
33+
dropin_path = app.root.join("vendor/assets/react")
34+
app.assets.prepend_path dropin_path if dropin_path.exist?
2935

30-
# Make sure it can be found
31-
app.assets.append_path(tmp_path)
36+
# Allow overriding react files that are based on environment
37+
# e.g. /vendor/assets/react/react.js
38+
dropin_path_env = app.root.join("vendor/assets/react/#{variant}")
39+
app.assets.prepend_path dropin_path_env if dropin_path_env.exist?
3240
end
3341
end
3442
end

lib/react/rails/version.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module React
22
module Rails
33
# Version numbers will track react-source, but we'll add another level so
44
# that we can increment, but have some amount of stability.
5-
VERSION = '0.8.0.0'
5+
VERSION = '1.0.0.beta1'
66
end
77
end
8-
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/** @jsx React.DOM */
2+
<div/>;

test/dummy/config/environments/test.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
# test suite. You never need to work with it otherwise. Remember that
66
# your test database is "scratch space" for the test suite and is wiped
77
# and recreated between test runs. Don't rely on the data there!
8-
config.cache_classes = true
8+
9+
# we need this to reload the jsx transformer when different version is dropped in
10+
config.cache_classes = false
11+
config.reload_plugins = true
12+
config.assets.cache_store = :null_store
913

1014
# Do not eager load code on boot. This avoids loading your whole application
1115
# just for the purpose of running a single test. If you are using a tool that
@@ -33,4 +37,6 @@
3337

3438
# Print deprecation notices to the stderr.
3539
config.active_support.deprecation = :stderr
40+
41+
config.react.variant = :test
3642
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var JSXTransformer = {
2+
transform: function () {
3+
return {
4+
code: 'test_confirmation_token_jsx_transformed;'
5+
};
6+
}
7+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
'test_confirmation_token_react_content';

test/jsxtransform_test.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'test_helper'
2+
require 'fileutils'
23

3-
# The transformer is inserting a newline after the docblock for some reason...
4+
# Sprockets is inserting a newline after the docblock for some reason...
45
EXPECTED_JS = <<eos
56
/** @jsx React.DOM */
67
@@ -26,6 +27,7 @@ class JSXTransformTest < ActionDispatch::IntegrationTest
2627

2728
test 'asset pipeline should transform JSX' do
2829
get 'assets/example.js'
30+
FileUtils.rm_r CACHE_PATH if CACHE_PATH.exist?
2931
assert_response :success
3032
assert_equal EXPECTED_JS, @response.body
3133
end
@@ -42,4 +44,17 @@ class JSXTransformTest < ActionDispatch::IntegrationTest
4244
assert_equal EXPECTED_JS_2.gsub(/\s/, ''), @response.body.gsub(/\s/, '')
4345
end
4446

47+
test 'can use dropped in version of JSX transformer' do
48+
hidden_path = File.expand_path("../dummy/vendor/assets/react/JSXTransformer__.js", __FILE__)
49+
replacing_path = File.expand_path("../dummy/vendor/assets/react/JSXTransformer.js", __FILE__)
50+
51+
FileUtils.mv hidden_path, replacing_path
52+
get 'assets/example3.js'
53+
54+
FileUtils.mv replacing_path, hidden_path
55+
FileUtils.rm_r CACHE_PATH if CACHE_PATH.exist?
56+
57+
assert_response :success
58+
assert_equal 'test_confirmation_token_jsx_transformed;', @response.body
59+
end
4560
end

test/react_test.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
require 'test_helper'
2+
require 'fileutils'
3+
4+
class ReactTest < ActionDispatch::IntegrationTest
5+
6+
test 'asset pipeline should deliver react file in a non-production variant' do
7+
actual_react_file_path = File.expand_path("../dummy/tmp/react-rails/react.js", __FILE__)
8+
actual_react_file_content = File.read actual_react_file_path
9+
10+
react_file_token = "'test_confirmation_token_react_content_non_production';\n";
11+
File.open(actual_react_file_path, 'w') {|f| f.write react_file_token}
12+
13+
get 'assets/react.js'
14+
15+
File.open(actual_react_file_path, 'w') {|f| f.write actual_react_file_content}
16+
FileUtils.rm_r CACHE_PATH if CACHE_PATH.exist?
17+
18+
assert_response :success
19+
assert_equal react_file_token, @response.body
20+
end
21+
22+
test 'asset pipeline should deliver drop-in react file replacement' do
23+
hidden_path = File.expand_path("../dummy/vendor/assets/react/test/react__.js", __FILE__)
24+
replacing_path = File.expand_path("../dummy/vendor/assets/react/test/react.js", __FILE__)
25+
26+
FileUtils.mv hidden_path, replacing_path
27+
get 'assets/react.js'
28+
29+
FileUtils.mv replacing_path, hidden_path
30+
FileUtils.rm_r CACHE_PATH if CACHE_PATH.exist?
31+
32+
assert_response :success
33+
assert_equal "'test_confirmation_token_react_content';\n", @response.body
34+
end
35+
36+
end

test/test_helper.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
require File.expand_path("../dummy/config/environment.rb", __FILE__)
55
require "rails/test_help"
6+
require "pathname"
7+
8+
CACHE_PATH = Pathname.new File.expand_path("../dummy/tmp/cache", __FILE__)
69

710
Rails.backtrace_cleaner.remove_silencers!
811

0 commit comments

Comments
 (0)