Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added 2 helpers to get hashed file names #1018

Merged
merged 1 commit into from
Jan 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Changes since last non-beta release.

*Please add entries here for your pull requests that are not yet released.*

#### Added
- Added 2 cache helpers: ReactOnRails::Utils.bundle_file_name(bundle_name) and ReactOnRails::Utils.server_bundle_file_name
for easy access to the hashed filenames for use in cache keys by [justin808](https://github.com/justin808).

#### Fixed
- Fixed `Utils.bundle_js_file_path` generating the incorrect path for `manifest.json` in webpacker projects: [Issue #1011](https://github.com/shakacode/react_on_rails/issues/1011) by [elstgav](https://github.com/elstgav)

Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ A key decision in your use React on Rails is whether you go with the rails/webpa

Until version 9, all React on Rails apps used the `/client` directory for configuring React on Rails in terms of the configuration of Webpack and location of your JavaScript and Webpack files, including the node_modules directory. Version 9 changed the default to `/` for the `node_modules` location using this value in `config/initializers/react_on_rails.rb`: `config.node_modules_location`.

The [ShakaCode Team](http://www.shakacode.com) recommends this approach projects beyond the simplest cases as it provides the greatest transparency in your webpack and overall client-side setup. The *big advantage* to this is that almost everything within the `/client` directory will apply if you wish to convert your client-side code to a pure Single Page Application that runs without Rails. This allows you to google for how to do something with Webpack configuration and what applies to a non-Rails app will apply just as well to a React on Rails app.
The [ShakaCode Team](http://www.shakacode.com) _recommends_ this approach for projects beyond the simplest cases as it provides the greatest transparency in your webpack and overall client-side setup. The *big advantage* to this is that almost everything within the `/client` directory will apply if you wish to convert your client-side code to a pure Single Page Application that runs without Rails. This allows you to google for how to do something with Webpack configuration and what applies to a non-Rails app will apply just as well to a React on Rails app.

The two best examples of this patten are the [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial) and the integration test example in [spec/dummy](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy).

Expand Down Expand Up @@ -679,8 +679,11 @@ If you are using [jquery-ujs](https://github.com/rails/jquery-ujs) for AJAX call
1. Examples in [spec/dummy/app/views/react_router](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy/app/views/react_router) and follow to the JavaScript code in the [spec/dummy/client/app/startup/ServerRouterApp.jsx](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy/client/app/startup/ServerRouterApp.jsx).
1. [Code Splitting docs](./docs/additional-reading/code-splitting.md) for information about how to set up code splitting for server rendered routes.

## Caching and Performance
Consider fragment and http caching of pages that contain React on Rails components. See [Caching and Performance](./docs/additional-reading/caching-and-performance.md) for more details.

## Deployment
* Version 6.0 puts the necessary precompile steps automatically in the rake precompile step. You can, however, disable this by setting certain values to nil in the [config/initializers/react_on_rails.rb](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy/config/initializers/react_on_rails.rb).
* React on Rails puts the necessary precompile steps automatically in the rake precompile step. You can, however, disable this by setting certain values to nil in the [config/initializers/react_on_rails.rb](https://github.com/shakacode/react_on_rails/tree/master/spec/dummy/config/initializers/react_on_rails.rb).
* `config.symlink_non_digested_assets_regex`: Set to nil to turn off the setup of non-js assets.
* `build_production_command`: Set to nil to turn off the precompilation of the js assets.
* See the [Heroku Deployment](./docs/additional-reading/heroku-deployment.md) doc for specifics regarding Heroku. The information here should apply to other deployments.
Expand Down Expand Up @@ -724,6 +727,7 @@ If you want to use a node server for server rendering, [get in touch](mailto:jus
+ [Hot Reloading of Assets For Rails Development](./docs/additional-reading/hot-reloading-rails-development.md)
+ [Heroku Deployment](./docs/additional-reading/heroku-deployment.md)
+ [Updating Dependencies](./docs/additional-reading/updating-dependencies.md)
+ [Caching and Performance](./docs/additional-reading/caching-and-performance.md)

+ **API**
+ [JavaScript API](./docs/api/javascript-api.md)
Expand Down
31 changes: 31 additions & 0 deletions docs/additional-reading/caching-and-performance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Caching and Performance


## Caching

### Fragment Caching

If you wish to do fragment caching that includes React on Rails rendered components, be sure to
include the bundle name of your server rendering bundle in your cache key. This is analogous to
how Rails puts an MD5 hash of your views in the cache key so that if the views change, then your
cache is busted. In the case of React code, if your React code changes, then your bundle name will
change due to the typical inclusion of a hash in the name.

Call this method to get the server bundle file name:

```ruby
# Returns the hashed file name of the server bundle when using webpacker.
# Nececessary fragment-caching keys.
ReactOnRails::Utils.server_bundle_file_name
```

### HTTP Caching

When creating a HTTP cache, you want the cache key to include your client bundle files.

Call this method to get the client bundle file name. Note, you have to pass which bundle name.

```ruby
# Returns the hashed file name when using webpacker. Useful for creating cache keys.
ReactOnRails::Utils..bundle_file_name(bundle_name)
```
9 changes: 7 additions & 2 deletions docs/api/ruby-api.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
PENDING:
# View Helpers
See the [app/helpers/react_on_rails_helper.rb](../../app/helpers/react_on_rails_helper.rb) source.

Should we document the view helpers here concisely?
# Controller Helpers
See the [lib/react_on_rails/controller.rb](../../lib/react_on_rails/controller.rb) source.

# Utility Methods
See the [lib/react_on_rails/utils.rb](../../lib/react_on_rails/utils.rb) source.
27 changes: 27 additions & 0 deletions lib/react_on_rails/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,33 @@

module ReactOnRails
module Utils
###########################################################
# PUBLIC API
###########################################################

# Returns the hashed file name when using webpacker. Useful for creating cache keys.
def self.bundle_file_name(bundle_name)
raise "Only call bundle_file_name if using webpacker" unless using_webpacker?
full_path = bundle_js_file_path_from_webpacker(bundle_name)
pathname = Pathname.new(full_path)
pathname.basename.to_s
end

# Returns the hashed file name of the server bundle when using webpacker.
# Nececessary fragment-caching keys.
def self.server_bundle_file_name
return @server_bundle_hash if @server_bundle_hash && !Rails.env.development?

@server_bundle_hash = begin
server_bundle_name = ReactOnRails.configuration.server_bundle_js_file
bundle_file_name(server_bundle_name)
end
end

###########################################################
# PRIVATE API -- Subject to change
###########################################################

# https://forum.shakacode.com/t/yak-of-the-week-ruby-2-4-pathname-empty-changed-to-look-at-file-size/901
# return object if truthy, else return nil
def self.truthy_presence(obj)
Expand Down
44 changes: 44 additions & 0 deletions spec/react_on_rails/utils_spec.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,50 @@
# frozen_string_literal: true

# rubocop:disable Metrics/ModuleLength
# rubocop:disable Metrics/BlockLength

require_relative "spec_helper"

module ReactOnRails
RSpec.describe Utils do
describe "cache helpers .server_bundle_file_name and .bundle_file_name" do
context "and file in manifest", :webpacker do
before do
allow(Rails).to receive(:root).and_return(Pathname.new("."))
allow(ReactOnRails).to receive_message_chain("configuration.generated_assets_dir")
.and_return("public/webpack/production")
allow(Webpacker).to receive_message_chain("config.public_output_path")
.and_return("public/webpack/production")
allow(Utils).to receive(:using_webpacker?).and_return(true)
end
describe ".bundle_file_name" do
before do
allow(Webpacker).to receive_message_chain("manifest.lookup")
.with("client-bundle.js")
.and_return("/webpack/production/client-bundle-0123456789abcdef.js")
end
subject do
Utils.bundle_file_name("client-bundle.js")
end
it { expect(subject).to eq("client-bundle-0123456789abcdef.js") }
end

describe ".server_bundle_file_name" do
before do
allow(ReactOnRails).to receive_message_chain("configuration.server_bundle_js_file")
.and_return("server-bundle.js")
allow(Webpacker).to receive_message_chain("manifest.lookup")
.with("server-bundle.js")
.and_return("/webpack/production/server-bundle-0123456789abcdef.js")
end
subject do
Utils.server_bundle_file_name
end
it { expect(subject).to eq("server-bundle-0123456789abcdef.js") }
end
end
end

describe ".bundle_js_file_path" do
before do
allow(ReactOnRails).to receive_message_chain(:configuration, :generated_assets_dir)
Expand Down Expand Up @@ -231,3 +272,6 @@ module ReactOnRails
end
end
end

# rubocop:enable Metrics/ModuleLength
# rubocop:enable Metrics/BlockLength