Skip to content
This repository has been archived by the owner on Oct 19, 2018. It is now read-only.

Hyperloop elevator pitch

Barrie Hadfield edited this page Jan 7, 2017 · 10 revisions

Hyperloop elevator pitch

Hyperloop is an Isomorphic Web Application Framework which empowers developers to build modern interactive web applications quickly. Hyperloop includes full access to the entire Rails ecosystem and all front-end JavaScript libraries including React - all using one language - Ruby.

The goal of Hyperloop is to allow the developer to write code that is directed toward solving the user's needs in the most straight forward manner, without redundant code, unnecessary APIs, or artificial separation between client and server.

This guide will take you through all the main Hyperloop architectural layers.

Components

We have a Component based DSL which wraps React and is compiled to JavaScript using Opal. We also have access to the full universe of JavaScript libraries seamlessly from within our Ruby code. Our DSL looks like this:

class BookList < React::Component::Base
  render(UL) do
    10.times do |i|
      LI { "Number #{i}" }.on(:click) do
          Alert "You clicked on #{i}!"
      end 
    end
  end
end

Models

We also have full access to the ActiveRecord models on the client or the server which means we can use the models directly within our Components without needing the abstraction of an API:

class BookList < React::Component::Base
  # Display each book in our catalog 
  render(UL) do
    Book.all.each do |book|
      LI { "Add #{book.name}" }.on(:click) do
           ...
        end
      end 
    end
  end
end

Changes made to Models on a client or server are automatically synchronized to all other authorized connected clients using ActionCable, pusher.com or polling. The synchronization is completely automatic and magical to behold.

Operations

Our business logic is encapsulated in reusable Operations that do not complicate our Models or Components. Operations are isomorphic and we can execute them on the client or server:

class BookList < React::Component::Base
  # Display each book in our catalog unless its already in the cart basket.
  # When the user clicks on a book, add it the Basket.
  render(UL) do
    Book.all.each do |book|
      LI { "Add #{book.name}" }.on(:click) do
        AddBookToBasket(book: book) do |outcome|
          alert "Failed to add the book" unless outcome.success?
        end
      end unless acting_user.basket.include? book
    end
  end
end

class AddBookToBasket < HyperLoop::Operation
  # Add a book to the basket and add to users watchlist
  param :book, type: Book

  def execute
    acting_user.basket << book 
    AddToActingUsersWatchList(book: params.book)
  end
end

Some Operations only make sense on the server so we have a concept of a Server Operation which will not only execute on the server but is also available to the client. Hyperloop handles the mechanics of this seamlessly without the need for an API. This blending of client-server functionality is a key attribute of this architecture.

class AddToActingUsersWatchList < HyperLoop::ServerOperation
  # Add a book the the current acting_user's watch list, and
  # send an initial email about the book.

  # This Operation can only be run on the server because it is going
  # to be sending mail, so it inherits from ServerOperation.

  # only clients with a logged in user can access this action.
  allow_operation { acting_user }

  param :book, type: Book

  def execute
    return if acting_user.watch_list.include? params.book
    WatchListMailer.new_book_email WatchList.create(user: acting_user,  book: params.book) 
  end
end

Authorization

Access to our Models and Operations is controlled by Policies that describe what the current acting user is authorized to do.

# the regulation can be defined directly in the operation class:
class UserSignup < HyperLoop::Operation
  allow_operation { acting_user } # must have a logged in user to do this operation
end

# or even better: keep Policies separate from implementation
class OperationPolicy
  UserSignup.allow_operation { acting_user }
end

# Access to models is also controlled by policies
class BookPolicy
  # only admins can create or update books.  Nobody can destroy
  allow_change(on: [:create, :update]) { acting_user.admin? }
  # etc
end

Routing

We also provide a DSL which wraps ReactRouter for full single page application routing and passing data between components:

route("/", mounts: App, index: Home) do
  route("about")
  route("books") do
    redirect('book/:id').to { | params | "/books/#{params[:id]}" }
  end
  route(mounts: Books) do
    route("books/:id")
  end
end

Benefits

  • One language - Ruby everywhere - reduces complexity and lets developers build solutions quickly
  • Client-side execution for distributed processing and a rich interactive user experience
  • Full power of Rails, React and the entire JavaScript universe
  • Transparent, automatic and secure client-server communication built into Models and Operations
  • Components, Models and Operations architecture encapsulate functionality for clean, predictable, testable code
Clone this wiki locally