Hyperstack adds the following files and directories to your Rails application:
In addition there are configuration settings in existing Rails files that are explained in the next section. Below we cover the purpose of each these files, and their contents.
Here lives all your Hyperstack code that will run on the client. Some of the subdirectories are isomorphic meaning the code is shared between the client and the server, other directories are client only.
hyperstackdirectory there can be the following sub-directories:
components(client-only) is where your components live.Following Rails conventions a component with a class of
Bar::None::FooManchushould be in a file named
models(isomorphic) is where ActiveRecord models are shared with the client. More on this below.
operations(isomorphic) is where Hyperstack Operations will live.
shared(isomorphic) is where you can put shared code that are not models or operations.
- Any other subdirectory (such as
client-ops) will be considered client-only.
Files in the
/operationsdirectories are loaded on the client and the server. So when you place a model's class definition in the
hyperstack/modelsdirectory the class is available on the client.
class Todo < ApplicationRecord
Todo.count # will return the same value on the client and the server
See the Policy section below for how access to the actual data is controlled. Remember a Model describes some data, but the actual data is stored in the database, and protected by Policies.
Likewise Operations placed in the
/operationsdirectory can be run on the client or the server, or in the case of a
ServerOpthe operation can be invoked on the client, but will run on the server.
Hyperstack sets things up so that Rails will first look in the
/operationsdirectories, and then in the server only
app/operationsdirectories. So if you don't want some model shared you can just leave it in the normal
There are cases where you would like split a class definition into its shared and server-only aspects. For example there may be code in a model that cannot be sensibly run on the client. Hyperstack augments the Rails dependency lookup mechanism so that when a file is found in a
hyperstackdirectory we will also load any matching file in the normal
This works because Ruby classes are open, so that you can define a class (or module) in multiple places.
Operations are Hyperstack's way of providing Service Objects: classes that perform some operation not strictly belonging to a single model, and often involving other services such as remote APIs. The idea of Operations comes from the Trailblazer Framework.
As such Operations can be useful strictly on the server side, and so can be added to the
Server side operations can also be remotely run from the client. Such operations are defined as subclasses of
The right way to define a
ServerOpis to place its basic definition including its parameter signature in the
hyperstack/operationsdirectory, and then placing the rest of the operation's definition in the
Hyperstack uses Policies to define access rights to your models. Policies are placed in the
app/policiesdirectory. For example the policies for the
Todomodel would defined by the
TodoPolicyclass located at
app/policies/todo_policy.rbDetails on policies can be found Policy section of this document..
│ └── user.rb # private section of User model
│ └── email_the_owner.rb # server code
│ ├── components/
│ │ ├── app.rb
│ │ ├── edit_todo.rb
│ │ ├── footer.rb
│ │ ├── header.rb
│ │ ├── show_todo.rb
│ │ └── todo_index.rb
│ ├── models/
│ │ ├── application_record.rb # usually no need to split this
│ │ ├── todo.rb # note all of Todo definition is public
│ │ └── user.rb # user has a public and private section
│ └── operations/
│ └── email_the_owner.rb # serverop interface only
These directories are where most of your work will be done during Hyperstack development.
What about Controllers and Views?Hyperstack works alongside Rails controllers and views. In a clean-sheet Hyperstack app you never need to create a controller or a view. On the other hand if you have existing code or aspects of your project that you feel would work better using a traditional MVC approach everything will work fine. You can also merge the two worlds: Hyperstack includes two helpers that allow you to mount components either from a controller or from within a view.
The Hyperstack configuration can be controlled via the
config/initializers/hyperstack.rbinitializer file. Using the installer will set up a reasonable set of of options, which you can tweak as needed.
Here is a summary of the various configuration settings:
# server_side_auto_require will patch the ActiveSupport Dependencies module
# so that you can define classes and modules with files in both the
# app/hyperstack/xxx and app/xxx directories.
# By default the generators will generate new components as subclasses of
# HyperComponent. You can change this using the component_base_class setting.
Hyperstack.component_base_class = 'HyperComponent' # i.e. 'ApplicationComponent'
# prerendering is default :off, you should wait until your
# application is relatively well debugged before turning on.
Hyperstack.prerendering = :off # or :on
# The transport setting controls how push (websocket) communications are
# implemented. The default is :none, but will be set to :action_cable if you
# install hyper-model.
# Other possibilities are :action_cable, :pusher (see www.pusher.com)
# or :simple_poller which is sometimes handy during system debug.
Hyperstack.transport = :action_cable # :pusher, :simple_poller or :none
# hotloader settings:
# sets the port hotloader will listen on. Note this must match the value used
# to start the hotloader typically in the foreman Procfile.
Hyperstack.hotloader_port = 25222
# seconds between pings over the hotloader websocket. Normally not needed.
Hyperstack.hotloader_ping = nil
# hotloader will automatically reload callbacks when effected classes are
# reloaded. Not recommended to change this.
Hyperstack.hotloader_ignore_callback_mapping = false
# Transport settings
# seconds before timeout when sending messages between the rails console and
# the server.
Hyperstack.send_to_server_timeout = 10
# Transport specific options
# pusher specific options
app_id: 'your pusher app id',
key: 'your pusher key',
secret: 'your pusher secret',
cluster: 'mt1', # pusher cluster defaults to mt1
encrypted: true, # encrypt pusher comms, defaults to true
refresh_channels_every: 2.minutes, # how often to check which channels are alive
# simple poller specific options
expire_polled_connection_in: 5.minutes, # when to kill simple poller connections
seconds_between_poll: 5.seconds, # how fast to poll when using simple poller
expire_new_connection_in: 10.seconds, # how long to keep initial sessions alive
# Namespace used to keep hyperstack communication separate from other websockets
Hyperstack.channel_prefix = 'synchromesh'
# If there a JS console available should websocket comms be logged?
Hyperstack.client_logging = true
# Automatically create a (possibly temporary) websocket connection as each
# browser session starts. Usually this is needed for further authentication and
# should be left as true
Hyperstack.connect_session = true
# Where to store the connection tables. Default is :active_record but you
# can also specify redis. If specifying redis the redis url defaults to
Hyperstack.connection = [adapter: :active_record] # or
# [adapter: :redis, redis_url: 'redis://127.0.0.1:6379]
# The import directive loads optional portions of the various hyperstack gems.
# Here are the common imports typically included:
Hyperstack.import 'hyperstack/hotloader', client_only: true if Rails.env.development?
# and these are typically not imported:
# React source is normally brought in through webpacker
# Hyperstack.import 'react/react-source-browser'
# add this line if you need jQuery AND ARE NOT USING WEBPACK
# Hyperstack.import 'hyperstack/component/jquery', client_only: true
# The following are less common settings which you should never have to change:
Hyperstack.prerendering_files = ['hyperstack-prerender-loader.js']
Hyperstack.public_model_directories = ['app/hyperstack/models']
# change definition of on_error to control how errors such as validation
# exceptions are reported on the server
def self.on_error(operation, err, params, formatted_error_message)
'To further investigate you may want to add a debugging '\
'breakpoint to the on_error method in config/initializers/hyperstack.rb'
end if Rails.env.development?
The default content of these packs are as follows:
// these packages will be loaded both during prerendering and on the client
React = require('react'); // react-js library
createReactClass = require('create-react-class'); // backwards compatibility with ECMA5
History = require('history'); // react-router history library
ReactRouter = require('react-router'); // react-router js library
ReactRouterDOM = require('react-router-dom'); // react-router DOM interface
ReactRailsUJS = require('react_ujs'); // interface to react-rails
// to add additional NPM packages run `yarn add package-name@version`
// then add the require here.
// add any requires for packages that will run client side only
ReactDOM = require('react-dom'); // react-js client side code
jQuery = require('jquery'); // remove if you don't need jQuery
// to add additional NPM packages call run yarn add package-name@version
// then add the require here.