rubyhyperstack

Restrict Hyperstack components/models to only those necessary for some specific page to work?


When you access some page in Hyperstack, every model from app/hyperstack/models directory is included and sent to the browser. For the sake of security and performance, is it possible to load (automatically or by some directive) only the required code for the models and not that are used from the components?

Maybe this can be done in a way similar that policies work for data? So I am asking if there is some kind of policy in Hyperstack that restricts the code that is sent to the browser?


Solution

  • There is no nice automatic way of doing this, but it's not too much work to achieve this. You would have to create a shared JS file of anything that's used on every page (opal, hyperstack, react, etc), and then create a separate JS file for every Page/Single Page App.


    Here's a very basic example:

    Application.js

    # app/assets/javascripts/application.rb
    
    require 'webpack/client_only'
    require 'components/shared'
    

    Shared JS file

    # app/hyperstack/components/shared.rb
    
    require 'opal'
    
    require 'webpack/client_and_server'
    require 'hyperstack-config'
    
    # etc...
    

    User Dashboard SPA:

    # app/hyperstack/components/user_dashboard.rb
    
    # Require shared models
    require './models/user'
    
    # Require shared components
    require_tree './components/user_dashboard'
    

    Todo List SPA:

    # app/hyperstack/components/todo_list.rb
    
    # Require shared models
    require './models/todo'
    require './models/user'
    
    # Require shared components
    require_tree './components/todo_list'
    

    User Dashboard HTML Layout

    # app/views/layouts/user_dashboard.html.erb
    
    <%= javascript_include_tag('application.js') %>
    <%= javascript_include_tag('components/todo_list.js') %>
    

    Todo List HTML Layout

    # app/views/layouts/todo_list.html.erb
    
    <%= javascript_include_tag('application.js') %>
    <%= javascript_include_tag('components/todo_list.js') %>
    


    What I've also done to make it more dynamic is create a helper method in the controller to define the name of the separate JS file to use based on the controller. This also allows you to use only one layout file if desired:

    # app/controllers/application_controller.rb
    
    class ApplicationController
      helper_method :spa_asset
    
      def spa_asset
        params[:controller]
      end
    end
    
    # app/views/layouts/application.html.erb
    
    <%= javascript_include_tag('application.js') %>
    <%= javascript_include_tag(spa_asset) %>
    

    And then inheriting controller can redefine it if the file name doesn't match the controller name:

    # app/controllers/foo_controller.rb
    
    class FooController
      def spa_asset
        'todo_list'
      end
    end
    

    Hope this helps!