I've entirely reworded this question as I feel this more accurately reflects what I wanted to ask the first time in a less roundabout way.
After instantiating a FormObject, calls to dynamically defined methods do not evaluate their block parameter in the context I'm trying for. For example:
@registration = RegistrationForm.new
@registration.user
# undefined local variable or method `user_params' for RegistrationForm:Class
RegistrationForm calls a class method exposing(:user) { User.new(user_params) }
which I would like to have define a new method that looks like this:
def user
@user ||= User.new(user_params)
end
My implementation doesn't use @ivar ||=
to cache the value (since falsey values will cause the method to be re-evaluated). I borrowed the idea from rspec's memoized_helpers and I 'think' I understand how it works. What I don't understand is what I should replace class_eval
with in lib/form_object/memoized_helpers.rb
.
Thank you
lib/form_object/base.rb
class FormObject::Base
include ActiveModel::Model
include FormObject::MemoizedHelpers
attr_reader :params, :errors
def initialize(params = {})
@params = ActionController::Parameters.new(params)
@errors = ActiveModel::Errors.new(self)
end
def save
valid? && persist
end
end
lib/form_object/memoized_helpers.rb
module FormObject
module MemoizedHelpers
private
def __memoized
@__memoized ||= {}
end
def self.included(mod)
mod.extend(ClassMethods)
end
module ClassMethods
def exposing(name, &block)
raise "#exposing called without a block" unless block_given?
class_eval do
define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = block.call } }
end
end
end
end
end
app/forms/registration_form.rb
class RegistrationForm < FormObject::Base
exposing(:user) { User.new(user_params) { |u| u.is_admin = true } }
exposing(:tenant) { user.build_tenant(tenant_params) }
validate do
tenant.errors.each do |key, value|
errors.add("#{tenant.class.name.underscore}_#{key}", value)
end unless tenant.valid?
end
validate do
user.errors.each do |key, value|
errors.add("#{user.class.name.underscore}_#{key}", value)
end unless user.valid?
end
private
def persist
user.save
end
def user_params
params.fetch(:user, {}).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
def tenant_params
params.fetch(:tenant, {}).permit(:name)
end
end