Adds controller functionality to Roda
Configure your Roda application to use this plugin:
plugin :controller
You can already register your controllers:
plugin :controller, controllers: { users: UsersController }
# Infer controller key to 'users'
plugin :controller, controllers: [UsersController]
# Infer controller key to 'users_handler'
plugin :controller, controllers: [UsersHandler]
You may also register controllers later, inside your application class declaration:
register_controller UsersController
register_controller MoviesController
# Passing a list of controllers
register_controller [UsersController, MoviesController]
# Explicitly declaring the controller key
register_controller :users, UsersHandler
register_controller :movies, MoviesHandler
The controller class is not required to follow any naming convention, specially if you specify explicitly the controller key when registered.
However if it ends with Controller
this will be stripped from the
controller key if it is inferred. I.E. UsersController => 'users'
and
UsersHandler => 'users_handler'
You just need to implement the methods with the action names you are going to use:
class UsersController
def index
"User Index"
end
def show(user_id)
"User Show ##{user_id}"
end
end
Call #dispatch
whenever you want to receive the result of the
controller action, this is a textbook RESTful routing definition:
r.on("users") do
r.is do
r.get do
dispatch('users#index')
end
r.post do
dispatch('users#create', args: r.params)
end
end
r.on(:id) do |user_id|
r.is do
r.get do
dispatch('users#show', args: user_id)
end
r.patch do # requires all_verbs plugin to be loaded
dispatch('users#update', args: [user_id, r.params])
end
r.delete do # requires all_verbs plugin to be loaded
dispatch('users#destroy', args: user_id)
end
end
end
end
You may expose the controller responses using the @responds_with
variable:
# users_controller.rb
class UsersController
def show(user_id)
user = user: User.find(user_id)
@responds_with = { user: user, best_movie: Movie.best_for(user) }
end
end
# app.rb
r.get do
dispatch('users#show', args: user_id)
# Do stuff with @user and @best_movie, or access @responds_with directly
# if the instance variable cannot be overwritten
end
If you are using the render plugin you can use the #controller
method instead
of #dispatch
. It receives the same parameters but will call the #view
method with the following convention:
controller('users#show', args: user_id) # will call view('users/show')
You may specify parameters for controller initialization, to provide context to your actions without polluting the method arguments:
# users_controller.rb
class UsersController
attr_reader :request, :response
def initialize(request, response)
@request = request
@response = response
end
# be able to access the request and response inside actions
# app.rb
## inside routes
dispatch('users#update', args: [user_id, r.params], inject: [request, response])
## registering controller
register_controller(:users, -> { |req, res| UsersController.new(req, res) }
You can setup default arguments for the #dispatch
and #controller
methods:
# Will always initialize controllers with request and response args
plugin :controller, inject: ->{ [request, response] }
By following conventions you may dry everything up.
This:
- Creates a
RodaController
class to be inherited on your controllers - Exposes request and response variables inside actions
- Adds helper methods to your actions
- Automatically loads all controllers inside
controllers
folder
# roda_controller.rb
class RodaController
attr_reader :request, :response
# Access request and response methods
def initialize(request, response)
@request = request
@response = response
end
# Enables respond_with exposed_variable: local_variable
def respond_with(opts)
@responds_with ||= {}
@responds_with.merge!(opts)
end
# Get list of all controllers
def self.descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
# app.rb
## configuring the plugin
plugin :controller, inject: ->{ [request, response] }
## automatically registering all controllers inside controllers folder
Dir["controllers/*.rb"].each { |file| require_relative file }
RodaController.descendants.each do |controller|
controller_key = Roda::RodaPlugins::Controller.underscore(controller.name)
controller_proc = -> (req, res) { controller.new(req, res) }
register_controller(controller_key, controller_proc)
end
Bug reports and pull requests are welcome on GitHub at https://github.com/badosu/roda-controller
The gem is available as open source under the terms of the MIT License.