Skip to content

Commit ece10ec

Browse files
committed
closes #185 #186
1 parent 5b92f28 commit ece10ec

File tree

7 files changed

+151
-46
lines changed

7 files changed

+151
-46
lines changed

docs/installation/installation.md

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ These are the steps to manually install Hyperstack in an existing Rails app:
7474

7575
There are quite a few steps, but each has a specific, and understandable purpose.
7676

77-
### Insure `yarn` is loaded
77+
### 1. Insure `yarn` is loaded
7878

7979
Yarn is used to load and manage NPM (Javascript) assets.
8080

8181
See https://yarnpkg.com/en/docs/install for details
8282

83-
### Add the gems
83+
### 2. Add the gems
8484

8585
Add
8686
```ruby
@@ -95,21 +95,26 @@ Then `bundle install`.
9595

9696
And if you just added webpacker make sure to run `bundle exec rails webpacker:install`
9797

98-
### Update the `application.js` file
98+
>Note: if you want to use the unreleased edge branch your rails-hyperstack gem specification will be:
99+
```ruby
100+
gem 'rails-hyperstack',
101+
git: 'git://github.com/hyperstack-org/hyperstack.git',
102+
branch: 'edge',
103+
glob: 'ruby/*/*.gemspec'
104+
```
99105

100-
The `hyperstack-loader` is a dynamically generated asset manifest that will load all your client side Ruby code. Make sure it is the last require in `app/assets/javascripts/application.js` file. That is it should be just before the final `require_tree` directive
106+
### 3. Update the `application.js` file
101107

102-
`jQuery` is very nicely integrated with Hyperstack, and provides a well
103-
documented uniform interface to the DOM. To use it require it and its Rails
104-
counter part in `application.js` before the `hyperstack-loader`
108+
The `hyperstack-loader` is a dynamically generated asset manifest that will load all your client side Ruby code. Make sure it is the last require in `app/assets/javascripts/application.js` file. That is it should be just *before* the final `require_tree` directive
105109

106110
```javascript
111+
// assets/javascripts/application.js
112+
...
107113
//= require hyperstack-loader
108114
//= require_tree .
109115
```
110-
> Note check to make sure jquery is not already being required.
111116

112-
### Add the `hyperstack` directories
117+
### 4. Add the `hyperstack` directories
113118

114119
Hyperstack will load code out of the `app/hyperstack` directory. Within this directory there are typically the following subdirectories:
115120

@@ -124,11 +129,11 @@ These directories are all optional. The `models`, `operations`, and `shared` su
124129

125130
Any other subdirectories will be treated as client only. The names listed above such as `components`, `stores` and `lib` are just conventions. For example you may prefer `client_lib`.
126131

127-
> Note that you will still have a `app/models` directory, which can be used to keep server-only models. This is useful for models that will never be accessed from the client to reduce payload size. You can also add an `app/operations` directory if you wish to have Operations that only run on the server.
132+
> Note that you will still have the standard Rails `app/models` directory, which can be used to keep server-only models. This is useful for models that will never be accessed from the client to reduce payload size. You can also add an `app/operations` directory if you wish to have Operations that only run on the server.
128133
>
129-
> For security see the policy setup below.
134+
> This does **not** effect security. See the section 7 for how Policies are setup.
130135
131-
### Add the HyperComponent base class
136+
### 5. Add the HyperComponent base class
132137

133138
The Hyperstack convention is for each application to define a `HyperComponent` base class from which all of your other components will inherit. This follows the modern Rails convention used with Models and Controllers.
134139

@@ -139,8 +144,8 @@ The typical `HyperComponent` class definition looks like this:
139144
class HyperComponent
140145
# All component classes must include Hyperstack::Component
141146
include Hyperstack::Component
142-
# The Observer module adds state handling
143-
include Hyperstack::State::Observer
147+
# The Observable module adds state handling
148+
include Hyperstack::State::Observable
144149
# The following turns on the new style param accessor
145150
# i.e. param :foo is accessed by the foo method
146151
param_accessor_style :accessors
@@ -151,9 +156,9 @@ end
151156
base class `ApplicationComponent` more closely following the
152157
rails convention.
153158

154-
### Replicate the `application_record.rb` file
159+
### 6. Replicate the `application_record.rb` file
155160

156-
Model files typically inherit from the `ApplicationModel` class, so you must move the
161+
Rails models files normally inherit from the `ApplicationModel` class, so you must move the
157162
`app/models/application_record.rb` to `app/hyperstack/models/application_record.rb` so
158163
that it is accessible both on the client and server.
159164

@@ -171,9 +176,9 @@ require 'models/application_record.rb'
171176
> Note that the above is *not* a typo. Rails paths begin with
172177
the *subdirectory* name. So `'models/application_record.rb'` means to search all directories for a file name `application_record.rb` in the `models` *subdirectory*
173178

174-
### Add a basic `application_policy.rb` file
179+
### 7. Add a basic `application_policy.rb` file
175180

176-
Your server side model data is protected by *Policies* defined in Policy classes stored in the `app/policy` directory. The following file creates basic a *"wide open"* set of
181+
Your server side model data is protected by *Policies* defined in Policy classes stored in the `app/policy` directory. The following file creates a basic *"wide open"* set of
177182
policies for development mode. You will then need to add specific Policies to protect
178183
your data in production mode.
179184

@@ -205,15 +210,16 @@ end unless Rails.env.production?
205210
> Note that regardless of whether models are public (i.e stored in the hyperstack/models
206211
directory) or private, they are ultimately protected by the Policy system.
207212

208-
### Add the `hyperstack.rb` initializer file
213+
### 8. Add the `hyperstack.rb` initializer file
209214

210215
Add the following file to the `config/initializers/` directory:
211216

212217
```ruby
213218
# config/initializers/hyperstack.rb
214219
Hyperstack.configuration do |config|
215-
# If you are not using ActionCable,
220+
# If you do not want to use ActionCable,
216221
# see http://hyperstack.orgs/docs/models/configuring-transport/
222+
# for setting up other options.
217223
config.transport = :action_cable # or :pusher or :simple_poller
218224

219225
# typically you will want to develop with prerendering off, and
@@ -252,7 +258,63 @@ you will may want to direct the output to a dedicated log file for example.
252258

253259
### Integrate with webpacker
254260

255-
@barriehadfield - HELP!
261+
The Rails webpacker gem will bundle up all your javascript assets including those used by Hyperstack such as React, and React-Router.
262+
263+
You can also easily add other NPM (node package manager) assets to the webpacker *pack files*.
264+
265+
Hyperstack will look for two webpacker pack files: one for packages that *only* run on the client side, and packages that can run on *both* the client, and during server prerendering.
266+
> Prerendering builds the initial page view server side, and then delivers it to the client as a normal static HTML page. Attached to the HTML are flags that React will use update the page as components are re-rendered after the initial page load.
267+
>
268+
> This means that page load time is comparable to any other Rails view.
269+
>
270+
> But to make this work packages that rely on the `browser` object, cannot be used during prerendering. Well structured packages that depend on the `browser` object will have a way to run in the prerendering environment.
271+
272+
You will also need to fetch the packages, plus any of their dependencies and bring them into your build environment.
273+
274+
> Coming from Ruby this can be confusing as we are used to simply adding a dependency into the `Gemfile`, and then using bundler to both fetch dependencies, and produce the `Gemfile.lock` file.
275+
>
276+
> In the JS world its a two part process. Yarn fetches packages and their dependents, and maintains a `yarn.lock` file. The pack files specify how to package up the JS code, making it ready for delivery to the client.
277+
278+
#### Add the `client_and_server.js` pack file
279+
280+
* Note that this goes in a new directory: `app/javascripts/packs` *
281+
282+
```javascript
283+
//app/javascript/packs/client_and_server.js
284+
// these packages will be loaded both during prerendering and on the client
285+
React = require('react'); // react-js library
286+
History = require('history'); // react-router history library
287+
ReactRouter = require('react-router'); // react-router js library
288+
ReactRouterDOM = require('react-router-dom'); // react-router DOM interface
289+
ReactRailsUJS = require('react_ujs'); // interface to react-rails
290+
// to add additional NPM packages run add yarn package-name@version
291+
// then add the require here.
292+
```
293+
#### Add the `client_only.js` pack file
294+
295+
```javascript
296+
//app/javascript/packs/client_only.js
297+
// add any requires for packages that will run client side only
298+
ReactDOM = require('react-dom'); // react-js client side code
299+
jQuery = require('jquery');
300+
// to add additional NPM packages call run add yarn package-name@version
301+
// then add the require here.
302+
```
303+
304+
#### Add the packages using `yarn`
305+
306+
First you need make sure you have `yarn` installed:
307+
[Yarn install for any system](https://yarnpkg.com/en/docs/install)
308+
309+
Once yarn is installed you need to add the following packages which Hyperstack depends on:
310+
311+
```text
312+
yarn add react@16 react-dom@16 \
313+
react-router@^5.0.0 react-router-dom@^5.0.0 \
314+
react_ujs@^2.5.0 jquery@^3.4.1
315+
```
316+
317+
And now you are good to go. In the future if you want to add a new
256318

257319
### Add the Hyperstack engine to the routes file
258320

@@ -270,15 +332,12 @@ end
270332

271333
You can mount the engine under any name you please. All of internal Hyperstack requests will be prefixed with that mount point.
272334

273-
> Note: You can also directly ask Hyperstack to mount your top level components
274-
via the routes file. For example
275-
335+
>Note: You can also directly ask Hyperstack to mount your top level components
336+
via the routes file. For example
276337
```ruby
277338
Rails.application.routes.draw do
278-
# this route should be first in the routes file so it always matches
279-
mount Hyperstack::Engine => '/hyperstack'
339+
mount Hyperstack::Engine => '/hyperstack' # this must be the first route
280340
get '/(*other)', to: 'hyperstack#app'
281-
...
282341
```
283342
> will pass all requests (i.e. `/(*other)`) to the hyperstack engine, and find
284343
and mount a component named `App`. Whatever ever you name the engine

install/rails-webpacker.rb

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,14 @@ class Hyperstack::ApplicationPolicy
114114

115115
# ----------------------------------- Add NPM modules
116116

117-
run 'yarn add react'
118-
run 'yarn add react-dom'
119-
run 'yarn add react-router'
117+
run 'yarn add react@16'
118+
run 'yarn add react-dom@16'
119+
run 'yarn add react-router@^5.0.0'
120120
if WITH_PRERENDERING
121-
run 'yarn add react-router-dom'
122-
run 'yarn add history'
123-
run 'yarn add react_ujs'
124-
run 'yarn add jquery'
121+
run 'yarn add react-router-dom@^5.0.0'
122+
# run 'yarn add history' # this will be brought in by react-router
123+
run 'yarn add react_ujs@^2.5.0'
124+
run 'yarn add jquery@^3.4.1'
125125
end
126126

127127
if !WITH_PRERENDERING
@@ -166,7 +166,7 @@ class Hyperstack::ApplicationPolicy
166166
ReactRouter = require('react-router'); // react-router js library
167167
ReactRouterDOM = require('react-router-dom'); // react-router DOM interface
168168
ReactRailsUJS = require('react_ujs'); // interface to react-rails
169-
// to add additional NPM packages call run yarn package-name@version
169+
// to add additional NPM packages call run add yarn package-name@version
170170
// then add the require here.
171171
CODE
172172

@@ -177,7 +177,7 @@ class Hyperstack::ApplicationPolicy
177177
// add any requires for packages that will run client side only
178178
ReactDOM = require('react-dom'); // react-js client side code
179179
jQuery = require('jquery');
180-
// to add additional NPM packages call run yarn package-name@version
180+
// to add additional NPM packages call run add yarn package-name@version
181181
// then add the require here.
182182
CODE
183183

@@ -189,11 +189,12 @@ class Hyperstack::ApplicationPolicy
189189
Rails.application.config.assets.paths << Rails.root.join('public', 'packs', 'js').to_s
190190
RUBY
191191
end
192-
append_file 'config/environments/test.rb' do
192+
inject_into_file 'config/environments/test.rb', before: /^end/ do
193193
<<-RUBY
194+
195+
# added by hyperstack installer
194196
config.assets.paths << Rails.root.join('public', 'packs-test', 'js').to_s
195197
RUBY
196-
end
197198

198199

199200
# ----------------------------------- application.js

ruby/hyper-component/lib/hyperstack/component/element.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ def merge_built_in_event_prop!(prop_name, &block)
121121
prop_name => %x{
122122
function(){
123123
var react_event = arguments[0];
124+
if (arguments.length == 0 || react_event.constructor.name != 'SyntheticEvent') {
125+
return #{yield(*Array(`arguments`))}
126+
}
124127
var all_args;
125128
var other_args;
126129
if (arguments.length > 1) {

ruby/hyper-component/lib/hyperstack/internal/component/react_wrapper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def self.import_native_component(opal_class, native_class)
3030
def self.eval_native_react_component(name)
3131
component = `eval(name)`
3232
raise "#{name} is not defined" if `#{component} === undefined`
33+
component = `component.default` if `component.__esModule`
3334
is_component_class = `#{component}.prototype !== undefined` &&
3435
(`!!#{component}.prototype.isReactComponent` ||
3536
`!!#{component}.prototype.render`)

ruby/hyper-component/spec/client_features/misc_fixes_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,24 @@ class Wham < HyperComponent
7676
expect(page).to have_content('found me!')
7777
end
7878

79+
it "allows application components to raise built in events" do
80+
mount 'RaiseMeAnEvent' do
81+
class RaiseMeAnEvent < HyperComponent
82+
render(DIV) do
83+
SPAN { @value ||= "not set yet!" }
84+
EventRaiser()
85+
.on(:change) { |new_value| mutate @value = new_value }
86+
end
87+
end
88+
class EventRaiser < HyperComponent
89+
fires :change
90+
before_mount { after(0) { change!('you have been set') }}
91+
render(SPAN) { '' }
92+
end
93+
end
94+
expect(page).to have_content('you have been set')
95+
end
96+
7997
# it "and it can still use the deprecated mutate syntax" do
8098
# mount "TestComp" do
8199
# class TestComp < Hyperloop::Component

ruby/hyper-component/spec/client_features/native_library_spec.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,29 @@ class Foo < Hyperloop::Component
3838
end
3939
end
4040

41+
describe "component defined in an es6 module" do
42+
it "is detected as native React.js component by `native_react_component?`" do
43+
expect_evaluate_ruby do
44+
Hyperstack::Internal::Component::ReactWrapper.native_react_component?(
45+
JS.call(:eval,
46+
"dummy = { default: function () { return null; }, __esModule: true }"
47+
)
48+
)
49+
end.to be_truthy
50+
end
51+
52+
it "is imported" do
53+
mount 'Foo', name: "There" do
54+
JS.call(:eval, 'window.NativeModule = { __esModule: true, default: function HelloMessage(props){
55+
return React.createElement("div", null, "Hello ", props.name); }}')
56+
class Foo < Hyperloop::Component
57+
imports "NativeModule"
58+
end
59+
end
60+
expect(page.body[-60..-19]).to include('<div>Hello There</div>')
61+
end
62+
end
63+
4164
it "can use native_react_component? to detect a native React.js component" do
4265
evaluate_ruby do
4366
"this makes sure React is loaded for this test, before js is run"

ruby/rails-hyperstack/lib/generators/hyperstack/install_generator.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def create_hyperstack_files_and_directories
3030
create_file 'app/hyperstack/components/hyper_component.rb', <<-RUBY
3131
class HyperComponent
3232
include Hyperstack::Component
33-
include Hyperstack::State::Observer
33+
include Hyperstack::State::Observable
3434
param_accessor_style :accessors
3535
end
3636
RUBY
@@ -112,15 +112,15 @@ def add_webpacker_manifests
112112
ReactRouter = require('react-router'); // react-router js library
113113
ReactRouterDOM = require('react-router-dom'); // react-router DOM interface
114114
ReactRailsUJS = require('react_ujs'); // interface to react-rails
115-
// to add additional NPM packages call run yarn package-name@version
115+
// to add additional NPM packages call run yarn add package-name@version
116116
// then add the require here.
117117
JAVASCRIPT
118118
create_file 'app/javascript/packs/client_only.js', <<-JAVASCRIPT
119119
//app/javascript/packs/client_only.js
120120
// add any requires for packages that will run client side only
121121
ReactDOM = require('react-dom'); // react-js client side code
122122
jQuery = require('jquery');
123-
// to add additional NPM packages call run yarn package-name@version
123+
// to add additional NPM packages call run yarn add package-name@version
124124
// then add the require here.
125125
JAVASCRIPT
126126
append_file 'config/initializers/assets.rb' do
@@ -142,11 +142,11 @@ def add_webpacks
142142
return if skip_webpack?
143143
yarn 'react', '16'
144144
yarn 'react-dom', '16'
145-
yarn 'react-router'#, '4.2'
146-
yarn 'react-router-dom'#, '4.2'
147-
yarn 'history'#, '4.2'
148-
yarn 'react_ujs'
149-
yarn 'jquery'
145+
yarn 'react-router', '^5.0.0'
146+
yarn 'react-router-dom', '^5.0.0'
147+
# yarn 'history'#, '4.2' this will be brought in by react-router
148+
yarn 'react_ujs', '^2.5.0'
149+
yarn 'jquery', '^3.4.1'
150150
end
151151

152152
def add_webpacker_gem

0 commit comments

Comments
 (0)