Skip to content

Instantly share code, notes, and snippets.

@frankjmattia
Last active January 3, 2016 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frankjmattia/0096f7436a14d95b5ed9 to your computer and use it in GitHub Desktop.
Save frankjmattia/0096f7436a14d95b5ed9 to your computer and use it in GitHub Desktop.
MemoizedHelpers is based on the implementation of #let in Rspec found here: https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/memoized_helpers.rb. When I run this I get NoMethodError super: no superclass method `tenant' for #<RegistrationForm:0x007fb25f0c3a78> at Line 28 of memoized_helpers.rb
class FormObject::Base
include ActiveModel::Model
include 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
module FormObject
module MemoizedHelpers
private
def __memoized
@__memoized ||= {}
end
def self.included(mod)
mod.extend(ClassMethods)
end
def self.definitions_module_for(form_object)
if form_object.const_defined?(:ModelDefinitions, false)
form_object.const_get(:ModelDefinitions, false)
else
form_object.const_set(:ModelDefinitions, Module.new)
end
end
module ClassMethods
def exposing(name, &block)
raise "#exposing called without a block" unless block_given?
MemoizedHelpers.definitions_module_for(self).send(:define_method, name, &block)
# If the exposing block has a parameter set it to the FormObject instance. I'd
# rather not do this at all and just always execute in the correct context.
# eg: remove the block.arity conditional and just expect arity to be 0.
if block.arity == 1
define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(self, &nil) } }
else
define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(&nil) } }
end
end
end
end
end
class RegistrationForm < FormObject::Base
# Instead of having to specify |form_instance| I would like the block
# to be automatically executed in the context of the form_instance
# so I can do something like User.new(user_params) instead of form_instance.user_params
exposing(:user) { |form_instance| User.new(form_instance.user_params) { |u| u.is_admin = true } }
exposing(:tenant) { |form_instance| user.build_tenant(form_instance.tenant_params) }
# Ultimately, the #expsosing calls will dynamically create these validate blocks
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment