Skip to content

Commit 2f8d693

Browse files
committed
Merge pull request #253 from shakacode/rob/improve-ensure-assets-compiled
Improve EnsureAssetsCompiled logic to wait for webpack process to finish
2 parents 0146069 + fd85038 commit 2f8d693

24 files changed

+472
-212
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ Contributors: please follow the recommendations outlined at [keepachangelog.com]
66
## [Unreleased]
77
##### Added
88
- Added forman to gemspec in case new dev does not have it globally installed. [#248](https://github.com/shakacode/react_on_rails/pull/248)
9+
910
##### Changed
1011
- Changed the EnsureAssetsCompiled feature that ensures RSpec tests are run with the latest webpack bundles. Previously, webpack bundles would be rebuilt every test run, whether or not they actually needed to be. While one could around this by running webpack in the background to rebuild the static assets, doing so required users to name their webpack build script exactly correct as the technique relied on a `pgrep`.
1112

1213
Instead, the new functionality checks the mdate on each generated webpack bundle/asset against the mdate of every file inside of the `client` directory, which is all encapsulated by the standard library method `FileUtils.uptodate?` and is very performant. This eliminates the need to check for an existing webpack process because the webpack bundles will be up to date anyway if the latter is running.
1314

1415
Users need not make any changes to their code to benefit from the enhancement, assuming all of their generated files are output to `javascripts/generated`, `stylesheets/generated`, `fonts/generated`, or `images/generated`. [#251](https://github.com/shakacode/react_on_rails/pull/251)
16+
17+
##### Breaking Change
18+
Renamed `ReactOnRails.configure_rspec_to_compile_assets` to `ReactOnRails::TestHelper.configure_rspec_to_compile_assets`
1519

1620
## [2.3.0] - 2016-02-01
1721
##### Added

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ We're definitely not doing that. With react_on_rails, webpack is mainly generati
122122
1. Add the following to your Gemfile and bundle install:
123123
124124
```ruby
125-
gem "react_on_rails", "~> 2.0.0"
125+
gem "react_on_rails", "~> 3"
126126
```
127127
128128
2. See help for the generator:

docs/additional_reading/rspec_configuration.md

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
# RSpec Configuration
22
Because you will probably want to run RSpec tests that rely on compiled webpack assets (typically, your integration/feature specs where `js: true`), you will want to ensure you don't accidentally run tests on missing or stale webpack assets. If you did use stale Webpack assets, you will get invalid test results as your tests do not use the very latest JavaScript code.
33

4-
ReactOnRails provides a helper method called `configure_rspec_to_compile_assets`. Call this method from inside of the `RSpec.configure` block in your `spec/rails_helper.rb` file, passing the config as an argument.
4+
ReactOnRails provides a helper method called `ReactOnRails::TestHelper.configure_rspec_to_compile_assets`. Call this method from inside of the `RSpec.configure` block in your `spec/rails_helper.rb` file, passing the config as an argument. See file [lib/react_on_rails/test_helper.rb](../../lib/react_on_rails/test_helper.rb) for more details. You can customize this to your particular needs by replacing any of the default components used by `ReactOnRails::TestHelper.configure_rspec_to_compile_assets`.
55

66
```ruby
77
RSpec.configure do |config|
8-
# Next line will ensure that assets are built if webpack -w is not running
9-
ReactOnRails.configure_rspec_to_compile_assets(config)
8+
# Next line will ensure that assets are built if webpack -w is not running to build the bundles
9+
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
1010
```
1111

12-
You can pass an RSpec metatag as an optional second parameter to this helper method if you want this helper to run on examples other than where `js: true` (default). The helper will compile webpack files at most once per test run.
12+
You can pass an RSpec metatag as an optional second parameter to this helper method if you want this helper to run on examples other than where `js: true` (default). The helper will compile webpack files at most once per test run. The helper will not compile the webpack files unless they are out of date (stale).
1313

14-
If you do not want to be slowed down by re-compiling webpack assets from scratch every test run, you can call `npm run build:client` (and `npm run build:server` if doing server rendering) to have webpack recompile these files in the background, which will be much faster. The helper looks for these processes and will abort recompiling if it finds them to be running.
14+
Please take note of the following:
15+
- This utility assumes your build tasks for the static generated files are `npm run build:client` and `npm run build:server` and do not have the `--watch` option enabled.
16+
- By default, the webpack processes look for the `app/assets/javascripts/generated` and `app/assets/stylesheets/generated` folders. If these folders are missing, are empty, or contain files with `mtime`s older than any of the files in your `client` folder, the helper will recompile your assets. You can override this inside of `config/initializers/react_on_rails.rb` by passing an array of filepaths (relative to the root of the app) to the `generated_assets_dirs` configuration option.
1517

16-
If you want to use a testing framework other than RSpec, we suggest you implement similar logic.
18+
If you want to speed up the re-compiling process, you can call `npm run build:dev:client` (and `npm run build:dev:server` if doing server rendering) to have webpack run in "watch" mode and recompile these files in the background, which will be much faster when making incremental changes than compiling from scratch.
19+
20+
If you want to use a testing framework other than RSpec, please submit let us know on the changes you need to do and we'll update the docs.
1721

1822
![2016-01-27_02-36-43](https://cloud.githubusercontent.com/assets/1118459/12611951/7c56d070-c4a4-11e5-8a80-9615f99960d9.png)
1923

lib/react_on_rails.rb

+6-8
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66
require "react_on_rails/server_rendering_pool"
77
require "react_on_rails/engine"
88
require "react_on_rails/version_syntax_converter"
9-
require "react_on_rails/ensure_assets_compiled"
10-
require "react_on_rails/webpack_assets_status_checker"
9+
require "react_on_rails/test_helper"
1110
require "react_on_rails/git_utils"
1211
require "react_on_rails/utils"
13-
14-
module ReactOnRails
15-
def self.configure_rspec_to_compile_assets(config, metatag = :js)
16-
config.before(:example, metatag) { EnsureAssetsCompiled.build.call }
17-
end
18-
end
12+
require "react_on_rails/test_helper"
13+
require "react_on_rails/test_helper/webpack_assets_compiler"
14+
require "react_on_rails/test_helper/webpack_assets_status_checker"
15+
require "react_on_rails/test_helper/webpack_process_checker"
16+
require "react_on_rails/test_helper/ensure_assets_compiled"

lib/react_on_rails/configuration.rb

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
21
module ReactOnRails
32
def self.configure
43
yield(configuration)
54
end
65

6+
DEFAULT_GENERATED_ASSETS_DIRS = [
7+
%w(app assets javascripts generated),
8+
%w(app assets javascripts stylesheets generated)
9+
].map { |dirs| File.join(*dirs) }.freeze
10+
711
def self.configuration
812
@configuration ||= Configuration.new(
9-
server_bundle_js_file: "app/assets/javascripts/generated/server.js",
13+
generated_assets_dirs: DEFAULT_GENERATED_ASSETS_DIRS,
14+
server_bundle_js_file: File.join(*%w(app assets javascripts generated server.js)),
1015
prerender: false,
1116
replay_console: true,
1217
logging_on_server: true,
@@ -23,17 +28,18 @@ class Configuration
2328
:trace, :development_mode,
2429
:logging_on_server, :server_renderer_pool_size,
2530
:server_renderer_timeout, :raise_on_prerender_error,
26-
:skip_display_none
31+
:skip_display_none, :generated_assets_dirs
2732

2833
def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil,
2934
trace: nil, development_mode: nil,
3035
logging_on_server: nil, server_renderer_pool_size: nil,
3136
server_renderer_timeout: nil, raise_on_prerender_error: nil,
32-
skip_display_none: nil)
37+
skip_display_none: nil, generated_assets_dirs: DEFAULT_GENERATED_ASSETS_DIRS)
3338
self.server_bundle_js_file = if File.exist?(server_bundle_js_file)
3439
server_bundle_js_file
3540
end
3641

42+
self.generated_assets_dirs = generated_assets_dirs
3743
self.prerender = prerender
3844
self.replay_console = replay_console
3945
self.logging_on_server = logging_on_server

lib/react_on_rails/ensure_assets_compiled.rb

-47
This file was deleted.

lib/react_on_rails/test_helper.rb

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
module ReactOnRails
2+
module TestHelper
3+
# Because you will probably want to run RSpec tests that rely on compiled webpack assets
4+
# (typically, your integration/feature specs where `js: true`), you will want to ensure you
5+
# don't accidentally run tests on missing or stale webpack assets. If you did use stale
6+
# Webpack assets, you will get invalid test results as your tests do not use the very latest
7+
# JavaScript code.
8+
#
9+
# Call this method from inside of the `RSpec.configure` block in your `spec/rails_helper.rb`
10+
# file, passing the config as an argument. You can customize this to your particular needs by
11+
# replacing any of the default components.
12+
#
13+
# RSpec.configure do |config|
14+
# ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
15+
#
16+
# You can pass an RSpec metatag as an optional second parameter to this helper method
17+
# if you want this helper to run on examples other than where `js: true` (default). The helper
18+
# will compile webpack files at most once per test run.
19+
#
20+
# If you do not want to be slowed down by re-compiling webpack assets from scratch every test
21+
# run, you can call `npm run build:client` (and `npm run build:server` if doing server
22+
# rendering) to have webpack recompile these files in the background, which will be *much*
23+
# faster. The helper looks for these processes and will abort recompiling if it finds them
24+
# to be running.
25+
#
26+
# See docs/additional_reading/rspec_configuration.md for more info
27+
def self.configure_rspec_to_compile_assets(config, metatag = :js)
28+
config.before(:example, metatag) { ReactOnRails::TestHelper.ensure_assets_compiled }
29+
end
30+
31+
# Main entry point to ensuring assets are compiled. See `configure_rspec_to_compile_assets` for
32+
# an example of usage.
33+
#
34+
# Typical usage passes all params as nil defaults.
35+
# webpack_assets_status_checker: provide: `up_to_date?`, `whats_not_up_to_date`, `client_dir`
36+
# defaults to ReactOnRails::TestHelper::WebpackAssetsStatusChecker
37+
# webpack_process_checker: provide one method: `def running?`
38+
# defaults to ReactOnRails::TestHelper::WebpackProcessChecker
39+
# webpack_assets_compiler: provide one method: `def compile`
40+
# defaults to ReactOnRails::TestHelper::WebpackAssetsCompiler
41+
# client_dir and compiled_dirs are passed into the default webpack_assets_status_checker if you
42+
# don't provide one.
43+
def self.ensure_assets_compiled(webpack_assets_status_checker: nil,
44+
webpack_assets_compiler: nil,
45+
webpack_process_checker: nil,
46+
client_dir: nil,
47+
compiled_dirs: nil)
48+
49+
if webpack_assets_status_checker.nil?
50+
client_dir ||= Rails.root.join("client")
51+
compiled_dirs ||= ReactOnRails.configuration.generated_assets_dirs
52+
webpack_assets_status_checker ||=
53+
WebpackAssetsStatusChecker.new(client_dir: client_dir,
54+
compiled_dirs: compiled_dirs)
55+
end
56+
57+
webpack_assets_compiler ||= WebpackAssetsCompiler.new
58+
webpack_process_checker ||= WebpackProcessChecker.new
59+
60+
ReactOnRails::TestHelper::EnsureAssetsCompiled.new(
61+
webpack_assets_status_checker: webpack_assets_status_checker,
62+
webpack_assets_compiler: webpack_assets_compiler,
63+
webpack_process_checker: webpack_process_checker
64+
).call
65+
end
66+
end
67+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
module ReactOnRails
2+
module TestHelper
3+
class EnsureAssetsCompiled
4+
class << self
5+
attr_accessor :has_been_run
6+
@has_been_run = false
7+
end
8+
9+
attr_reader :webpack_assets_status_checker,
10+
:webpack_assets_compiler,
11+
:webpack_process_checker
12+
13+
MAX_TIME_TO_WAIT = 5
14+
15+
def initialize(webpack_assets_status_checker: nil,
16+
webpack_assets_compiler: nil,
17+
webpack_process_checker: nil)
18+
@webpack_assets_status_checker = webpack_assets_status_checker
19+
@webpack_assets_compiler = webpack_assets_compiler
20+
@webpack_process_checker = webpack_process_checker
21+
end
22+
23+
def call
24+
return if self.class.has_been_run
25+
26+
loop_count = 0
27+
loop do
28+
break if webpack_assets_status_checker.up_to_date?
29+
30+
puts_first_iteration_message(loop_count)
31+
32+
if webpack_process_checker.running? && loop_count < MAX_TIME_TO_WAIT
33+
loop_count += 1
34+
sleep 1
35+
else
36+
puts_max_iterations_message(loop_count)
37+
38+
webpack_assets_compiler.compile
39+
puts
40+
break
41+
end
42+
end
43+
44+
self.class.has_been_run = true
45+
end
46+
47+
def puts_first_iteration_message(loop_count)
48+
return unless loop_count == 0
49+
50+
puts "\n\nReact on Rails is ensuring your JavaScript generated files are up to date!"
51+
end
52+
53+
def puts_max_iterations_message(loop_count)
54+
if loop_count == MAX_TIME_TO_WAIT
55+
stale_files = webpack_assets_status_checker.whats_not_up_to_date.join("\n")
56+
57+
puts <<-MSG
58+
59+
Even though we detected the webpack watch processes are running, we found files modified that are
60+
not causing a rebuild of your generated files:
61+
62+
#{stale_files}
63+
64+
One possibility is that you modified a file in your directory that is not a dependency of
65+
your webpack files: #{webpack_assets_status_checker.client_dir}
66+
67+
To be sure, we will now rebuild your generated files.
68+
MSG
69+
end
70+
end
71+
end
72+
end
73+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# You can replace this implementation with your own for use by the
2+
# ReactOnRails::TestHelper.ensure_assets_compiled helper
3+
module ReactOnRails
4+
module TestHelper
5+
class WebpackAssetsCompiler
6+
def compile
7+
compile_type(:client)
8+
compile_type(:server) if Utils.server_rendering_is_enabled?
9+
end
10+
11+
private
12+
13+
def compile_type(type)
14+
unless @printed_msg
15+
puts <<-MSG
16+
If you are frequently running tests, you can run webpack in watch mode to speed up this process.
17+
See the official documentation:
18+
https://github.com/shakacode/react_on_rails/blob/master/docs/additional_reading/rspec_configuration.md
19+
MSG
20+
@printed_msg = true
21+
end
22+
23+
puts "\nBuilding Webpack #{type}-rendering assets..."
24+
25+
build_output = `cd client && npm run build:#{type}`
26+
27+
fail "Error in building assets!\n#{build_output}" unless Utils.last_process_completed_successfully?
28+
29+
puts "Completed building Webpack #{type}-rendering assets."
30+
end
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)