“...I've been working since 2008 with Ruby / Ruby on Rails, love a bit of Elixir / Phoenix and learning Rust. I also poke through other people's code and make PRs for OpenSource Ruby projects that sometimes make it. Currently working for InPay...”

Rob Lacey
Senior Software Engineer, UK

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
GPK of the Day Wrappin' RUTH