Skip to content

Commit

Permalink
Controller parameters wrapping feature
Browse files Browse the repository at this point in the history
Required singleton for StrongParams wrapper

Added comments
  • Loading branch information
alex-rogachev committed Jun 19, 2024
1 parent 977fe3c commit 38dab69
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 24 deletions.
2 changes: 2 additions & 0 deletions lib/rage/all.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
require_relative "router/util"

require_relative "controller/api"
require_relative "controller/parameters_wrappers/parameters_wrapper"
require_relative "controller/parameters_wrappers/strong_parameters"

require_relative "logger/text_formatter"
require_relative "logger/json_formatter"
Expand Down
1 change: 1 addition & 0 deletions lib/rage/code_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def reload
@loader.reload
Rage.__router.reset_routes
load("#{Rage.root}/config/routes.rb")
Rage.__router.set_params_wrappers
end

# in Rails mode - reset the routes; everything else will be done by Rails
Expand Down
77 changes: 53 additions & 24 deletions lib/rage/controller/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,25 @@ def __run_#{action}
RUBY
end
# @private
# set the parameters wrappers stack;
# this is called when controller classes are loaded and their class variables that control wrappers plugging in are
# set;
def __set_params_wrappers
unless @__wrap_parameters_key.nil?
__params_wrappers_stack << ParametersWrappers::ParametersWrapper.new(@__wrap_parameters_key, @__wrap_parameters_options)
end
if defined?(::ActionController::Parameters)
__params_wrappers_stack << ParametersWrappers::StrongParameters.instance
end
end
# @private
def __params_wrappers_stack
@__params_wrappers_stack ||= []
end
# @private
attr_writer :__before_actions, :__after_actions, :__rescue_handlers
Expand Down Expand Up @@ -277,6 +296,20 @@ def skip_before_action(action_name, only: nil, except: nil)
@__before_actions[i] = action
end
# Initialize controller params wrapping into a nested hash.
# If initialized, params wrapping logic will be added to the controller.
#
# @param key [Symbol] key that the params hash will nested under
# @param options [Hash] wrapping options
# @example
# wrap_parameters :user, include: [:name, :age]
# @example
# wrap_parameters :user, exclude: :address
def wrap_parameters(key, options = {})
@__wrap_parameters_key = key
@__wrap_parameters_options = options
end
private
# used by `before_action` and `after_action`
Expand Down Expand Up @@ -450,30 +483,26 @@ def request_http_token_authentication
render plain: "HTTP Token: Access denied.", status: 401
end
if !defined?(::ActionController::Parameters)
# Get the request data. The keys inside the hash are symbols, so `params.keys` returns an array of `Symbol`.<br>
# You can also load Strong Params to have Rage automatically wrap `params` in an instance of `ActionController::Parameters`.<br>
# At the same time, if you are not implementing complex filtering rules or working with nested structures, consider using native `Hash#fetch` and `Hash#slice` instead.
#
# For multipart file uploads, the uploaded files are represented by an instance of {Rage::UploadedFile}.
#
# @return [Hash{Symbol=>String,Array,Hash,Numeric,NilClass,TrueClass,FalseClass}]
# @example
# # make sure to load strong params before the `require "rage/all"` call
# require "active_support/all"
# require "action_controller/metal/strong_parameters"
#
# params.permit(:user).require(:full_name, :dob)
# @example
# # without strong params
# params.fetch(:user).slice(:full_name, :dob)
def params
@__params
end
else
def params
@params ||= ActionController::Parameters.new(@__params)
end
# Get the request data. The keys inside the hash are symbols, so `params.keys` returns an array of `Symbol`.<br>
# You can also load Strong Params to have Rage automatically wrap `params` in an instance of `ActionController::Parameters`.<br>
# At the same time, if you are not implementing complex filtering rules or working with nested structures, consider using native `Hash#fetch` and `Hash#slice` instead.
#
# For multipart file uploads, the uploaded files are represented by an instance of {Rage::UploadedFile}.
#
# @return [Hash{Symbol=>String,Array,Hash,Numeric,NilClass,TrueClass,FalseClass}]
# @example
# # make sure to load strong params before the `require "rage/all"` call
# require "active_support/all"
# require "action_controller/metal/strong_parameters"
#
# params.permit(:user).require(:full_name, :dob)
# @example
# # without strong params
# params.fetch(:user).slice(:full_name, :dob)
def params
self.class.__params_wrappers_stack.each { |wrapper| @__params = wrapper.wrap_params(@__params) }
@__params
end
# Checks if the request is stale to decide if the action has to be rendered or the cached version is still valid. Use this method to implement conditional GET.
Expand Down
24 changes: 24 additions & 0 deletions lib/rage/controller/parameters_wrappers/parameters_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module ParametersWrappers
class ParametersWrapper
def initialize(wrapper_key, options)
@wrapper_key = wrapper_key
@options = options
end

def wrap_params(params)
params.merge(wrapper_key => filtered_params(params))
end

private

attr_reader :wrapper_key, :options

def filtered_params(params)
if options[:include]
params.slice(*[options[:include]].flatten)
elsif options[:exclude]
params.except(*[options[:exclude]].flatten)
end
end
end
end
11 changes: 11 additions & 0 deletions lib/rage/controller/parameters_wrappers/strong_parameters.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'singleton'

module ParametersWrappers
class StrongParameters
include Singleton

def wrap_params(params)
ActionController::Parameters.new(params)
end
end
end
8 changes: 8 additions & 0 deletions lib/rage/router/backend.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ def lookup(env)
find(env, constraints)
end

def set_params_wrappers(controller = RageController::API)
controller.__set_params_wrappers

controller.subclasses.each do |subclass|
set_params_wrappers(subclass)
end
end

private

def __on(method, path, handler, constraints, defaults, meta)
Expand Down
2 changes: 2 additions & 0 deletions lib/rage/setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
# Load application classes
Rage.code_loader.setup

Rage.__router.set_params_wrappers

require_relative "#{Rage.root}/config/routes"

require "rage/ext/setup"

0 comments on commit 38dab69

Please sign in to comment.