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

Add node as an option to the generator #469

Merged
merged 4 commits into from
Aug 21, 2016
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. Items under
Contributors: please follow the recommendations outlined at [keepachangelog.com](http://keepachangelog.com/). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare to the latest release version.

## [Unreleased]

- Node option for installer added as alternative for server rendering [#469](https://github.com/shakacode/react_on_rails/pull/469) by [jbhatab](https://github.com/jbhatab).
- React on Rails server rendering now supports contexts outside of browser rendering, such as ActionMailer templates [#486](https://github.com/shakacode/react_on_rails/pull/486) by [eacaps](https://github.com/eacaps).
- React on Rails now correctly parses single-digit version strings from package.json [#491](https://github.com/shakacode/react_on_rails/pull/491) by [samphilipd ](https://github.com/samphilipd ).
- Fixed assets symlinking to correctly use filenames with spaces. Begining in [#510](https://github.com/shakacode/react_on_rails/pull/510), ending in [#513](https://github.com/shakacode/react_on_rails/pull/513) by [dzirtusss](https://github.com/dzirtusss)
Expand Down
2 changes: 2 additions & 0 deletions docs/additional-reading/node-server-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ If you're serious about this comparing Node.js versus execJS/mini_racer, then [g
## Setup of React on Rails with Node.js Server Rendering
**Warning: this is an experimental feature.**

* Every time the webpack bundle changes, you have to restart the server yourself.

To do this you need to add a few files and then configure react_on_rails to use NodeJS. Here are the relevant files to add.

Node server rendering allows you to use separate NodeJS process as a renderer. The process loads your configured server_bundle_js file and then executes javascript to render the component inside its environment. The communication between rails and node occurs
Expand Down
1 change: 1 addition & 0 deletions docs/additional-reading/server-rendering-tips.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Be sure to use mini_racer. See [issues/428](https://github.com/shakacode/react_o
component. Since the passed in props Hash from the view helper applies to client and server side code, the best way to
do this is to use a generator function.
- If you're serious about server rendering, it's worth the effort to have different entry points for client and server rendering. It's worth the extra complexity.
- You can enable node server rendering if you want. See more information here: https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/node-server-rendering.md

The point is that you have separate files for top level client or server side, and you pass some extra option indicating that rendering is happening server side.

Expand Down
4 changes: 4 additions & 0 deletions lib/generators/USAGE
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ can pass the redux option if you'd like to have redux setup for you automaticall
to integrate the Redux state container framework. The necessary node modules
will be automatically included for you.

* Node

Passing the --node generator option sets up the necessary files for node to render the react_components.

*******************************************************************************

After running the generator, you will want to:
Expand Down
8 changes: 8 additions & 0 deletions lib/generators/react_on_rails/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ class InstallGenerator < Rails::Generators::Base
desc: "Install Redux gems and Redux version of Hello World Example. Default: false",
aliases: "-R"

# --redux
class_option :node,
type: :boolean,
default: false,
desc: "Sets up node as a server rendering option. Default: false",
aliases: "-N"

# --ignore-warnings
class_option :ignore_warnings,
type: :boolean,
Expand Down Expand Up @@ -46,6 +53,7 @@ def invoke_generators
invoke "react_on_rails:base"
invoke "react_on_rails:react_no_redux" unless options.redux?
invoke "react_on_rails:react_with_redux" if options.redux?
invoke "react_on_rails:node" if options.node?
end

# NOTE: other requirements for existing files such as .gitignore or application.
Expand Down
22 changes: 22 additions & 0 deletions lib/generators/react_on_rails/node_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require "rails/generators"

module ReactOnRails
module Generators
class NodeGenerator < Rails::Generators::Base
Rails::Generators.hide_namespace(namespace)
source_root(File.expand_path("../templates", __FILE__))

def create_node_directory
empty_directory("client/node")
end

def copy_base_redux_files
base_path = "node/base/"
%w(client/node/server.js
client/node/package.json).each do |file|
copy_file(base_path + file, file)
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ ReactOnRails.configure do |config|
config.skip_display_none = false

# The server render method - either ExecJS or NodeJS
<%- if options.node? -%>
config.server_render_method = "NodeJS"
<%- else -%>
config.server_render_method = "ExecJS"
<%- end -%>

# Client js uses assets not digested by rails.
# For any asset matching this regex, non-digested symlink will be created (what webpack's css wants)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "react_on_rails_node",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./server.js -s webpack-bundle.js"
},
"dependencies": {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
var net = require('net');
var fs = require('fs');

var bundlePath = '../../app/assets/webpack/';
var bundleFileName = 'webpack-bundle.js';

var currentArg;

function Handler() {
this.queue = [];
this.initialized = false;
}

Handler.prototype.handle = function (connection) {
var callback = function () {
connection.setEncoding('utf8');
connection.on('data', (data)=> {
console.log('Processing request: ' + data);
var result = eval(data);
connection.write(result);
});
};

if (this.initialized) {
callback();
} else {
this.queue.push(callback);
}
};

Handler.prototype.initialize = function () {
console.log('Processing ' + this.queue.length + ' pending requests');
var callback;
while (callback = this.queue.pop()) {
callback();
}

this.initialized = true;
};

var handler = new Handler();

process.argv.forEach((val) => {
if (val[0] == '-') {
currentArg = val.slice(1);
return;
}

if (currentArg == 's') {
bundleFileName = val;
}
});

try {
fs.mkdirSync(bundlePath);
} catch (e) {
if (e.code != 'EEXIST') throw e;
}

fs.watchFile(bundlePath + bundleFileName, (curr) => {
if (curr && curr.blocks && curr.blocks > 0) {
if (handler.initialized) {
console.log('Reloading server bundle must be implemented by restarting the node process!');
return;
}

require(bundlePath + bundleFileName);
console.log('Loaded server bundle: ' + bundlePath + bundleFileName);
handler.initialize();
}
});

var unixServer = net.createServer(function (connection) {
handler.handle(connection);
});

unixServer.listen('node.sock');

process.on('SIGINT', () => {
unixServer.close();
process.exit();
});
12 changes: 12 additions & 0 deletions spec/react_on_rails/generators/install_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
include_examples "react_with_redux_generator"
end

context "--node" do
before(:all) { run_generator_test_with_args(%w(--node)) }
include_examples "base_generator", application_js: true
include_examples "node_generator"
end

context "-N" do
before(:all) { run_generator_test_with_args(%w(-N)) }
include_examples "base_generator", application_js: true
include_examples "node_generator"
end

context "without existing application.js or application.js.coffee file" do
before(:all) { run_generator_test_with_args([], application_js: false) }
include_examples "base_generator", application_js: false
Expand Down
6 changes: 6 additions & 0 deletions spec/react_on_rails/support/shared_examples/node_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
shared_examples "node_generator" do
it "copies base redux files" do
%w(client/node/server.js
client/node/package.json).each { |file| assert_file(file) }
end
end