Project

hobby-rpc

0.0
No release in over a year
A simple RPC system for callable Ruby constants. It allows to expose them over HTTP safely, only to authorized users.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

~> 0.2.2
~> 0.1.0
~> 0.1.0
 Project Readme

Introduction

Hobby-RPC is a simple RPC system for callable Ruby constants. It allows to expose them over HTTP safely, only to authorized users.

It is available on RubyGems as hobby-rpc.

The idea is to separate transport and business logic. Hobby-RPC handles the transport layer and provides a client for browsers; we can focus on defining business logic, which will be transport-independent.

Usage

Defining user roles

First, we need to define who the users are and what they can do. Otherwise, all the requests will return 403 Forbidden.

We can do this by creating at least one user role. A user role is a class that includes Hobby::RPC::User and implements .find_by_token and #can? methods. For example:

require 'hobby/rpc'

class Client
  include Hobby::RPC::User

  # Accepts a `token`(String).
  #
  # Here we can look up at the place where we store active sessions.
  # It could be a Redis server mapping session tokens to users.
  #
  # Returns a user or nil(if a session for such token does not exist).
  def self.find_by_token token
    new if 'the token' == token
  end

  # Accepts a `function`(String).
  #
  # This determines what a user can do. A user will be allowed to call
  # `function` only when this method returns truthy value.
  #
  # One way to implement it is with Redis Sets:
  # https://redis.io/commands/sismember
  def can? function
    ['SomeFunction', 'SomeNamespace::SomeFunction'].include? function
  end

Client is the only role at the moment, but we can define as many as we would like. The role names would be specific to the business logic of your application.

Defining functions

An RPC function is a Ruby constant whose object implements .call method that can accept user and input parameters. It can accept them either as a Hash or as keyword arguments. For example:

module SomeFunction
  def self.call hash
    hash[:user]
    hash[:input]
    'return any value serializable to JSON'
  end
end

module SomeNamespace
  module SomeFunction
    def self.call user:, input:
      'return any value serializable to JSON'
    end
  end
end

A user would be an instance of a user role defined earlier.

An input would be an object deserialized from JSON or nil. It is what the client can pass to a function as an argument.

The functions must return something serializable #to_json. The client will get this as a response. Or, in case of an error inside of a function, the client will get 400 Bad Request.

Running a server

To start a server, we can use rackup. For that, we can put the following into config.ru:

run Hobby::RPC.new

By default, it will return permissive CORS headers(Access-Control-Allow-Origin: *) for requests from any origin. For private APIs, you might want to restrict that:

Hobby::RPC.new cors_origins: ['https://some.domain', 'https://another.domain']

Calling functions

To call RPC functions from browsers, you can use this client. It works via the Hobby-RPC protocol.

Development

To work on hobby-rpc itself, you can build the project and run the tests:

bundle exec rake