diff --git a/CHANGELOG.md b/CHANGELOG.md
index bb2a9476..f10bb900 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
#### Bug Fixes
+- Use controller lifecycle hooks for view helper (tests don't run middlewares)
+
## 1.3.0 (September 15, 2015)
#### Breaking Changes
diff --git a/README.md b/README.md
index 4320d2af..6e05eeca 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ rails g react:install
```
This will:
-- create a `components.js` manifest file and a `app/assets/javascripts/components/` directory,
+- create a `components.js` manifest file and a `app/assets/javascripts/components/` directory,
where you will put your components
- place the following in your `application.js`:
@@ -48,7 +48,7 @@ where you will put your components
### React.js builds
-You can pick which React.js build (development, production, with or without [add-ons]((http://facebook.github.io/react/docs/addons.html)))
+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:
```ruby
@@ -71,10 +71,10 @@ MyApp::Application.configure do
end
```
-After restarting your Rails server, `//= require react` will provide the build of React.js which
+After restarting your Rails server, `//= require react` will provide the build of React.js which
was specified by the configurations.
-`react-rails` offers a few other options for versions & builds of React.js.
+`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.
@@ -121,7 +121,7 @@ config.react.jsx_transform_options = {
### Rendering & mounting
-`react-rails` includes a view helper (`react_component`) and an unobtrusive JavaScript driver (`react_ujs`)
+`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)).
@@ -133,7 +133,7 @@ The __view helper__ puts a `div` on the page with the requested component class
```
-On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class`
+On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class`
and `data-react-props`.
If Turbolinks is present components are mounted on the `page:change` event, and unmounted on `page:before-unload`.
@@ -169,9 +169,9 @@ _(It will be also be mounted by the UJS on page load.)_
There are some requirements for this to work:
-- `react-rails` must load your code. By convention it uses `components.js`, which was created
+- `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).
-- Your components must be accessible in the global scope.
+- 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:
```coffee
@@ -180,7 +180,7 @@ If you are using `.js.jsx.coffee` files then the wrapper function needs to be ta
render: ->
``
```
-- Your code can't reference `document`. Prerender processes don't have access to `document`,
+- 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 :(
You can configure your pool of JS virtual machines and specify where it should load code:
@@ -222,10 +222,10 @@ By default, your current layout will be used and the component, rather than a vi
### Component generator
-`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 (--es6)`.
-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)
+`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 (--es6)`.
+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.
For example:
@@ -294,7 +294,7 @@ Note that the arguments for `oneOf` and `oneOfType` must be enclosed in single q
### Jbuilder & react-rails
-If you use Jbuilder to pass a JSON string to `react_component`, make sure your JSON is a stringified hash,
+If you use Jbuilder to pass a 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:
```ruby
@@ -313,7 +313,7 @@ end
## CoffeeScript
-It is possible to use JSX with CoffeeScript. To use CoffeeScript, create files with an extension `.js.jsx.coffee`.
+It is possible to use JSX with CoffeeScript. To use CoffeeScript, create files with an extension `.js.jsx.coffee`.
We also need to embed JSX code inside backticks so that CoffeeScript ignores the syntax it doesn't understand.
Here's an example:
@@ -348,8 +348,8 @@ Any subclass of `ExecJSRenderer` may use those hooks (for example, `SprocketsRen
`react-rails` uses a "helper implementation" class to generate the output of the `react_component` helper. The helper is initialized once per request and used for each `react_component` call during that request. You can provide a custom helper class to `config.react.view_helper_implementation`. The class must implement:
- `#react_component(name, props = {}, options = {}, &block)` to return a string to inject into the Rails view
-- `#setup(rack_env)`, called when the helper is initialized at the start of the request
-- `#teardown(rack_env)`, called at the end of the request
+- `#setup(controller_instance)`, called when the helper is initialized at the start of the request
+- `#teardown(controller_instance)`, called at the end of the request
`react-rails` provides one implementation, `React::Rails::ComponentMount`.
diff --git a/Rakefile b/Rakefile
index 117ee384..2b33d706 100644
--- a/Rakefile
+++ b/Rakefile
@@ -34,7 +34,6 @@ Rake::TestTask.new(:test) do |t|
t.libs << 'test'
t.pattern = ENV['TEST_PATTERN'] || 'test/**/*_test.rb'
t.verbose = ENV['TEST_VERBOSE'] == '1'
- t.warning = true
end
task default: :test
diff --git a/lib/react/rails.rb b/lib/react/rails.rb
index 788b610c..a1a98507 100644
--- a/lib/react/rails.rb
+++ b/lib/react/rails.rb
@@ -1,7 +1,7 @@
require 'react/rails/asset_variant'
require 'react/rails/engine'
require 'react/rails/railtie'
-require 'react/rails/render_middleware'
+require 'react/rails/controller_lifecycle'
require 'react/rails/version'
require 'react/rails/component_mount'
require 'react/rails/view_helper'
diff --git a/lib/react/rails/component_mount.rb b/lib/react/rails/component_mount.rb
index 506d3391..9b8f1db4 100644
--- a/lib/react/rails/component_mount.rb
+++ b/lib/react/rails/component_mount.rb
@@ -10,7 +10,7 @@ class ComponentMount
include ActionView::Helpers::TextHelper
attr_accessor :output_buffer
- # RenderMiddleware calls these hooks
+ # ControllerLifecycle calls these hooks
# You can use them in custom helper implementations
def setup(env)
end
diff --git a/lib/react/rails/controller_lifecycle.rb b/lib/react/rails/controller_lifecycle.rb
new file mode 100644
index 00000000..d8f370a6
--- /dev/null
+++ b/lib/react/rails/controller_lifecycle.rb
@@ -0,0 +1,24 @@
+module React
+ module Rails
+ module ControllerLifecycle
+ extend ActiveSupport::Concern
+
+ included do
+ # use old names to support Rails 3
+ before_filter :setup_react_component_helper
+ after_filter :teardown_react_component_helper
+ attr_reader :__react_component_helper
+ end
+
+ def setup_react_component_helper
+ new_helper = React::Rails::ViewHelper.helper_implementation_class.new
+ new_helper.setup(self)
+ @__react_component_helper = new_helper
+ end
+
+ def teardown_react_component_helper
+ @__react_component_helper.teardown(self)
+ end
+ end
+ end
+end
diff --git a/lib/react/rails/controller_renderer.rb b/lib/react/rails/controller_renderer.rb
index b78a87e5..aaaa2cbc 100644
--- a/lib/react/rails/controller_renderer.rb
+++ b/lib/react/rails/controller_renderer.rb
@@ -5,9 +5,9 @@ class React::Rails::ControllerRenderer
attr_accessor :output_buffer
- attr_reader :request
def initialize(options={})
- @request = options[:request]
+ controller = options[:controller]
+ @__react_component_helper = controller.__react_component_helper
end
def call(name, options, &block)
diff --git a/lib/react/rails/railtie.rb b/lib/react/rails/railtie.rb
index 0605fe1a..c90d1667 100644
--- a/lib/react/rails/railtie.rb
+++ b/lib/react/rails/railtie.rb
@@ -24,13 +24,18 @@ class Railtie < ::Rails::Railtie
# Include the react-rails view helper lazily
initializer "react_rails.setup_view_helpers", group: :all do |app|
- app.config.middleware.use(::React::Rails::RenderMiddleware)
+
app.config.react.jsx_transformer_class ||= React::JSX::DEFAULT_TRANSFORMER
React::JSX.transformer_class = app.config.react.jsx_transformer_class
React::JSX.transform_options = app.config.react.jsx_transform_options
app.config.react.view_helper_implementation ||= React::Rails::ComponentMount
React::Rails::ViewHelper.helper_implementation_class = app.config.react.view_helper_implementation
+
+ ActiveSupport.on_load(:action_controller) do
+ include ::React::Rails::ControllerLifecycle
+ end
+
ActiveSupport.on_load(:action_view) do
include ::React::Rails::ViewHelper
end
@@ -38,7 +43,7 @@ class Railtie < ::Rails::Railtie
initializer "react_rails.add_component_renderer", group: :all do |app|
ActionController::Renderers.add :component do |component_name, options|
- renderer = ::React::Rails::ControllerRenderer.new(request: request)
+ renderer = ::React::Rails::ControllerRenderer.new(controller: self)
html = renderer.call(component_name, options)
render_options = options.merge(inline: html)
render(render_options)
diff --git a/lib/react/rails/render_middleware.rb b/lib/react/rails/render_middleware.rb
deleted file mode 100644
index e038cc3b..00000000
--- a/lib/react/rails/render_middleware.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module React
- module Rails
- class RenderMiddleware
- HELPER_IMPLEMENTATION_KEY = "react_rails.view_helper_implementation"
- def initialize(app)
- @app = app
- end
-
- def call(env)
- new_helper = React::Rails::ViewHelper.helper_implementation_class.new
- new_helper.setup(env)
- env[HELPER_IMPLEMENTATION_KEY] = new_helper
- response = @app.call(env)
- new_helper.teardown(env)
- response
- end
- end
- end
-end
diff --git a/lib/react/rails/view_helper.rb b/lib/react/rails/view_helper.rb
index eeca5dc4..d3fc7dc4 100644
--- a/lib/react/rails/view_helper.rb
+++ b/lib/react/rails/view_helper.rb
@@ -3,15 +3,14 @@ module Rails
module ViewHelper
# This class will be used for inserting tags into HTML.
# It should implement:
- # - #setup(env)
- # - #teardown(env)
+ # - #setup(controller_instance)
+ # - #teardown(controller_instance)
# - #react_component(name, props, options &block)
# The default is {React::Rails::ComponentMount}
mattr_accessor :helper_implementation_class
def react_component(*args, &block)
- impl_key = React::Rails::RenderMiddleware::HELPER_IMPLEMENTATION_KEY
- helper_obj = request.env[impl_key]
+ helper_obj = @__react_component_helper
helper_obj.react_component(*args, &block)
end
end
diff --git a/test/react/rails/render_middleware_test.rb b/test/react/rails/controller_lifecycle_test.rb
similarity index 71%
rename from test/react/rails/render_middleware_test.rb
rename to test/react/rails/controller_lifecycle_test.rb
index 35848c6f..2b5c236f 100644
--- a/test/react/rails/render_middleware_test.rb
+++ b/test/react/rails/controller_lifecycle_test.rb
@@ -4,12 +4,13 @@
# calls to `react_component`
class DummyHelperImplementation
attr_reader :events
+
def initialize
@events = []
end
- def setup(env)
- @events << :setup
+ def setup(controller)
+ @events << (controller.params["param_test"] || :setup)
end
def teardown(env)
@@ -21,9 +22,7 @@ def react_component(*args)
end
end
-class RenderMiddlewareTest < ActionDispatch::IntegrationTest
- impl_key = React::Rails::RenderMiddleware::HELPER_IMPLEMENTATION_KEY
-
+class ControllerLifecycleTest < ActionDispatch::IntegrationTest
def setup
@previous_helper_implementation = React::Rails::ViewHelper.helper_implementation_class
React::Rails::ViewHelper.helper_implementation_class = DummyHelperImplementation
@@ -35,22 +34,22 @@ def teardown
test "it creates a helper object and puts it in the request env" do
get '/pages/1'
- helper_obj = request.env[impl_key]
+ helper_obj = controller.__react_component_helper
assert(helper_obj.is_a?(DummyHelperImplementation), "It uses the view helper implementation class")
end
test "it calls setup and teardown methods" do
- get '/pages/1'
- helper_obj = request.env[impl_key]
- lifecycle_steps = [:setup, :react_component, :teardown]
+ get '/pages/1?param_test=123'
+ helper_obj = controller.__react_component_helper
+ lifecycle_steps = ["123", :react_component, :teardown]
assert_equal(lifecycle_steps, helper_obj.events)
end
test "there's a new helper object for every request" do
get '/pages/1'
- first_helper = request.env[impl_key]
+ first_helper = controller.__react_component_helper
get '/pages/1'
- second_helper = request.env[impl_key]
+ second_helper = controller.__react_component_helper
assert(first_helper != second_helper, "The helper for the second request is brand new")
end
end
diff --git a/test/react/rails/pages_controller_test.rb b/test/react/rails/pages_controller_test.rb
new file mode 100644
index 00000000..89badd4a
--- /dev/null
+++ b/test/react/rails/pages_controller_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class PagesControllerTest < ActionController::TestCase
+ test 'renders successfully' do
+ get :show, id: 1
+ assert_equal(200, response.status)
+ end
+end