Write code with code so you don't have to write code
I’m currently trying to bring in Strong Parameters, replacing attr_accessible with a custom class for each model class we want to sanitize params for. We don’t want to fill controllers with massive blah_params methods if we’re likely to re-use them everywhere. So if we hand this over to a class that can manage it….all the better.
BlahPermit.with(param.fetch(:blah, {})).permit
I’d rather not go through every file and create an accompanying class for what, at this stage, will only require a small tweak to each attr_accessible call.
So here we can cope with both classes we can load or define on the fly if they don’t already exist.
module NotAtAllAttrAccessible
extend ActiveSupport::Concern
module ClassMethods
def attr_accessible(*attrs)
klass = define_permit_class
klass.permits += attrs
define_method :accessible_attributes do
klass.permits
end unless method_defined? :accessible_attributes
end
private
def define_permit_class
name = "#{self.name}Permit"
Object.const_get(name)
rescue NameError
Object.const_set(name, Class.new(Permit))
end
end
end
And that will equate to
class Blah
attr_accessible :thing, :the, :blair, :turnip
end
class Permit
cattr_accessor :permits, default: []
class << self
def klass(klass)
"#{klass}Permit".constantize
rescue NameError
Permit
end
def with(params)
new.with(params)
end
end
def with(params)
@params = params.is_a?(ActionController::Parameters) ? params : ActionController::Parameters.new(params)
self
end
def permit!
permits.any? ? params.permit(*permits) : params.permit!
end
def permits
self.class.permits
end
def params
@params || ActionController::Parameters.new
end
end
class BlahPermit < Permit
@@permits = [:thing, :the, :blair, :turnip]
end
And we can then crush Parameters like a boss.
def resource_params
Permit.klass(Blah).with(param.fetch(:blah, {})).permit
end