Sometimes you want to run some code before or after an action is
executed. In Lucky, we call these pipes. There is the before
pipe and
after
pipe macros.
class Admin::Users::Index < BrowserAction
before require_admin
get "/admin/users" do
plain_text "List of users"
end
private def require_admin
if current_user.admin?
continue
else
redirect to: SignIns::New
end
end
private def current_user
# Get the currently signed in user somehow
end
end
Pipes must return continue
or a standard
Lucky::Response
.
If a
Lucky::Response
is returned by rendering or redirecting, then no other pipe will run.
Sharing code between actions will be pretty common. Whether it’s for authentication, authorization, or just some logging, it’s recommended to place these common methods in a module that can be included in the actions that need them.
# src/actions/mixins/log_request.cr
module LogRequest
macro included
after log_request_path
end
private def log_request_path
Log.dexter.info { {method: request.method, path: request.path} }
continue
end
end
With our mixin defined, we can include it in each action that requires it.
class Dashboard::Show < BrowserAction
include LogRequest
get "/dashboard" do
plain_text "The dashboard"
end
end
You could also include this in a base class like the built-in BrowserAction
or ApiAction
, so all actions that inherit those will run the pipes.
You could also create a new base action using an abstract class
like we do with the built-in ones. For example, you could have an
AdminAction
that inherits from BrowserAction
and includes your
authorization-based pipes. Then all admin actions can inherit from
AdminAction
and the authorization-based pipes will be run.
Sometimes you’ll have a pipe in an abstract base class that you want to
skip for certain actions. In those cases you can use the skip
macro.
Let’s say we log the request in our BrowserAction
. That means all actions
that inherit from it will log the request.
class BrowserAction < Lucky::Action
after log_request_path
def log_request_path
Log.dexter.info { {method: request.method, path: request.path} }
continue
end
end
But if we want to skip this in one of our actions, we can with skip
.
class Users::Index < BrowserAction
skip log_request_path
end