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

automatically add dependencies for server side files. #361

Closed
catmando opened this issue Feb 25, 2021 · 0 comments
Closed

automatically add dependencies for server side files. #361

catmando opened this issue Feb 25, 2021 · 0 comments
Labels
enhancement New feature or request needs doc Everything is working, but documentation is needed.

Comments

@catmando
Copy link
Contributor

catmando commented Feb 25, 2021

You can now easily split your class definitions so that you have an isomorphic (i.e. runs on the server and client) definition, and a server-only definition.

This is especially useful for Server Operations and Active Record models.

Here is a sample directory structure, containing an application component, a model called Person, and an operation called PalindromeChecker. We also have a model called Orderbut it is only visible on the server. More on that later.

app -
  hyperstack -
    components -
      app.rb
    models -
      application_record.rb
      person.rb
    operations
      palindrome_checker.rb
  models -
    person.rb
    order.rb
  operations
    palindrome_checker.rb

Note that the person.rb and palindrome_checker.rb files exist in two places: one in the hyperstack directory, and the other in the outer app directory.

Files in the hyperstack models, operations and shared directories will be executed on both the client and the server. This is how a method can be understood by both the client and the server.

Files in the outer app subdirectory are only recognized by the Rails server. So any information in those files, will not be seen on the client.

As a side note files in the hyperstack /components, /libs, and in fact, any other directories except /models, /operations, and /shared directories are only seen on the client. So there three kinds of files: Server-only (those in the outer app directories), server and client (those in hyperstack /models, /operations, and /shared directories, and client-only (any other hyperstack subdirectories)

Here is how we can use this difference to nicely structure our code that is sharing information between the server and client. We can put class definitions that are only relevant to the server in the outer app directories, and put class definitions that are used by both the client and server in the hyperstack directories.

So for example here are the two PalindromeChecker files:

# app/hyperstack/operations/palindrome_checker.rb
class PalindromeChecker < Hyperstack::ServerOp
  param :string 
end
# app/operations/palindrome_checker.rb
class PalindromeChecker < Hyperstack::ServerOp
  param :acting_user, nils: true
  param :string 

  step { params.string = params.string.gsub(/\s+/, '').downcase }
  step { params.string.reverse == params.string }
end

All the client-side needs to know is that PalindromeChecker is a subclass of Hyperstack::ServerOp, and that it accepts a single param - a string to be checked. This definition is also available to the server, and will ensure that the type signature matches between the isomorphic (client and server) definition and the server-only definition.

The server-side file has all the implementation details including the acting_user declaration which can be used to validate who can be calling this operation. (In this case, we don't require an acting_user hence nils are allowed)

Let's look at an Active Record example:

# app/hyperstack/models/person.rb
class Person < ApplicationRecord 
  def full_name
    "#{first_name} #{last_name}"
  end
  
  scope :children, ->() { where("birthday > ?", Time.now - 21.years) }
  scope :adults, ->() { where("birthday <= ?", Time.now - 21.years) }
end
# app/hyperstack/models/person.rb 
class Person < ApplicationRecord 
  has_many :orders
end

Here we have a helper method and some scopes defined in the isomorphic definition: These are available to both the client and the server.

However the server-side file also has relationships with another model, and because these are defined on the server-side they are not visible to the client.

In order for this to work, you do need to add a small snippet of code to your initializers/hyperstack.rb file which trains Rails to look in both directories for any constant definition. This code will be released into Hyperstack soon, but in the meantime feel free to use this snippet and clean up your classes:

# Add to your `initializers/hyperstack.rb file
module ActiveSupport
  module Dependencies
    HYPERSTACK_DIR = 'hyperstack'  
    class << self
      alias original_require_or_load require_or_load

      # before requiring_or_loading a file, first check if
      # we have the same file in the server side directory
      # and add that as a dependency

      def require_or_load(file_name, const_path = nil)
        add_server_side_dependency(file_name)
        original_require_or_load(file_name, const_path)
      end

      # search the filename path from the end towards the beginning
      # for the HYPERSTACK_DIR directory.  If found, remove it from
      # the filename, and if a ruby file exists at that location then
      # add it as a dependency

      def add_server_side_dependency(file_name)
        path = File.expand_path(file_name.chomp('.rb'))
                   .split(File::SEPARATOR).reverse
        hs_index = path.find_index(HYPERSTACK_DIR)

        return unless hs_index # no hyperstack directory here

        new_path = (path[0..hs_index - 1] + path[hs_index + 1..-1]).reverse
        load_path = new_path.join(File::SEPARATOR)

        return unless File.exist? "#{load_path}.rb"

        require_dependency load_path
      end
    end
  end
end
@catmando catmando added the enhancement New feature or request label Feb 25, 2021
@catmando catmando added the needs doc Everything is working, but documentation is needed. label Feb 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs doc Everything is working, but documentation is needed.
Projects
None yet
Development

No branches or pull requests

1 participant