Class: Hg::Router

Inherits:
Object
  • Object
show all
Defined in:
lib/hg/router.rb

Overview

The router is responsible for routing an incoming action to the appropriate controller:

router.handle({ action: 'orderPizza', parameters: { size: 'large', toppings: ['pepperoni', 'sausage'] } })

The handle method expects a request object consisting of, at least, action and parameters keys. Postbacks and raw messages sent through an NLU should use the same set of action and parameter names, so that request objects will look the same either way by the time they reach the router.

Creating a router

Add a router to your bot by subclassing Hg::Router:

class PizzaBotRouter < Hg::Router; end

Adding routes

You can add a route for an action with the action method:

class PizzaBotRouter < Hg::Router action 'orderPizza', controller: PizzaOrdersController, with: :create end

Here, we're specifying that the orderPizza action should map to the PizzaOrdersController's create handler method.

Note that it's probably a good idea to store your action names as constants.

The route map

The routes map is a hash with action names as keys, pointing at nested hash values with the following structure:

{ controller: PizzaOrderController, handler: :create }

Routing

The router will map an inbound request to the appropriate handler method. request objects take the following form:

request = { action: 'orderPizza', parameters: { size: 'large', toppings: ['pepperoni', 'sausage'] } }

Passing a request to handle directly off the bot's router class will call the action's matching handler method on its controller class:

PizzaBotRouter.handle(request) # => PizzaOrderController.call(:create)

Defined Under Namespace

Classes: ActionNotRegisteredError

Constant Summary collapse

INTERNAL_ROUTES =

The actions used internally by Hg.

{
  Hg::InternalActions::DISPLAY_CHUNK => {
    controller: Hg::Controllers::ChunksController,
    handler: :display_chunk
  }
}

Class Method Summary collapse

Class Method Details

.action(action_name, controller:, with:) ⇒ Object

Add the action to the routes map.

Parameters:

  • action_name (String, Symbol)

    The name of the action to be matched by the router.

  • controller (Class)

    The class of the controller which contains this action's handler method.

  • with (Symbol)

    The name of the handler method on the controller class for this action.



103
104
105
106
107
108
109
110
# File 'lib/hg/router.rb', line 103

def action(action_name, controller:, with:)
  handler_method_name = with

  @routes[action_name] = {
    controller: controller,
    handler: handler_method_name
  }
end

.controller(controller_class, &block) ⇒ Object



125
126
127
128
129
# File 'lib/hg/router.rb', line 125

def controller(controller_class, &block)
  Thread.current[:current_controller] = controller_class

  yield
end

.default(controller, handler_method_name) ⇒ Object

Set up a handler for the default action.

Parameters:

  • controller (Class)

    The class of the controller which contains the default action's handler method.

  • handler_method_name (Symbol)

    The name of the handler method on the controller class for the default action.



162
163
164
165
166
# File 'lib/hg/router.rb', line 162

def default(controller, handler_method_name)
  action Hg::InternalActions::DEFAULT,
         controller: controller,
         with: handler_method_name
end

.handle(request) ⇒ Object

Handle an inbound request by finding its matching handler method and executing it.

Parameters:

  • request (Hash)

    The inbound request.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/hg/router.rb', line 135

def handle(request)
  # Don't use the router if a route has already been specified.
  unless route = request.route
    begin
      route = routes.fetch(request.action)
      request.route = route
    rescue KeyError
      raise ActionNotRegisteredError.new(request.action)
    end
  end

  handler_name = route[:handler]

  controller_for_request = route[:controller].new(
    request:      request,
    router:       self,
    handler_name: handler_name
  )
  controller_for_request.process_action(handler_name)
end

.handler(action_name, handler_method_name) ⇒ Object

Add a route for an action from within a controller block.

Parameters:

  • action_name (String, Symbol)

    The name of the action to be matched by the router.

  • handler_method_name (Symbol)

    The name of the handler method on the controller class for this action.



117
118
119
120
121
122
123
# File 'lib/hg/router.rb', line 117

def handler(action_name, handler_method_name)
  # TODO: BUG Thread.current isn't going to work in case of multiple routers
  # Needs to be a concurrent object, or dry-ruby_configurable
  action(action_name,
         controller: Thread.current[:current_controller],
         with: handler_method_name)
end

.inherited(subclass) ⇒ Object

Create a new class instance variable routes on any subclasses.



82
83
84
85
86
87
88
89
# File 'lib/hg/router.rb', line 82

def inherited(subclass)
  # Default routes to new hash.
  subclass.instance_variable_set(:@routes, {})

  # TODO: Need to figure this out.
  # Since the class itself is the router, make it immutable for thread-safety.
  # subclass.freeze
end

.routesHash

Returns The routes map.

Returns:

  • (Hash)

    The routes map.



92
93
94
# File 'lib/hg/router.rb', line 92

def routes
  @memoized_routes ||= INTERNAL_ROUTES.merge(@routes)
end