lib/surrogate/endower.rb in surrogate-0.6.2 vs lib/surrogate/endower.rb in surrogate-0.6.3
- old
+ new
@@ -3,26 +3,34 @@
# Adds surrogate behaviour to your class / singleton class / instances
#
# please refactor me! ...may not be possible :(
# Can we move all method definitions into this class?
class Endower
+ def self.uninitialized_instance_for(surrogate_class)
+ instance = surrogate_class.allocate
+ hatchery = surrogate_class.instance_variable_get :@hatchery
+ surrogate_class.last_instance = instance
+ instance.instance_variable_set :@hatchling, Hatchling.new(instance, hatchery)
+ instance
+ end
+
def self.add_hook(&block)
hooks << block
end
def self.hooks
@hooks ||= []
end
- def self.endow(klass, &block)
- new(klass, &block).endow
+ def self.endow(klass, options, &block)
+ new(klass, options, &block).endow
end
- attr_accessor :klass, :block
+ attr_accessor :klass, :block, :options
- def initialize(klass, &block)
- self.klass, self.block = klass, block
+ def initialize(klass, options, &block)
+ self.klass, self.options, self.block = klass, options, block
end
def endow
endow_klass
endow_singleton_class
@@ -30,12 +38,13 @@
private
def endow_klass
klass.extend ClassMethods
- add_hatchery_to klass
+ add_hatchery_to klass, options # do we want to pick out which options to pass?
enable_defining_methods klass
+ enable_factory klass, options.fetch(:factory, :factory)
klass.send :include, InstanceMethods
enable_generic_override klass
invoke_hooks klass
end
@@ -61,42 +70,54 @@
def singleton
klass.singleton_class
end
- def add_hatchery_to(klass)
- klass.instance_variable_set :@hatchery, Surrogate::Hatchery.new(klass)
+ def add_hatchery_to(klass, options={})
+ klass.instance_variable_set :@hatchery, Surrogate::Hatchery.new(klass, options)
end
def enable_defining_methods(klass)
def klass.define(method_name, options={}, &block)
@hatchery.define method_name, options, &block
end
end
+
+ def enable_factory(klass, factory_name)
+ return unless factory_name
+ klass.define_singleton_method factory_name do |overrides={}|
+ instance = begin
+ new
+ rescue ArgumentError
+ Endower.uninitialized_instance_for self
+ end
+ overrides.each { |attribute, value| instance.will_override attribute, value }
+ instance
+ end
+ end
end
# use a module so that the method is inherited (important for substitutability)
module ClassMethods
# Should this be dup? (dup seems to copy singleton methods) and may be able to use #initialize_copy to reset ivars
# Can we just remove this feature an instead provide a reset feature which could be hooked into in before/after blocks (e.g. https://github.com/rspec/rspec-core/blob/622505d616d950ed53d12c6e82dbb953ba6241b4/lib/rspec/core/mocking/with_rspec.rb)
- def clone
+ def clone(overrides={})
hatchling, hatchery, parent_name = @hatchling, @hatchery, name
Class.new self do
extend Module.new { define_method(:name) { parent_name && parent_name + '.clone' } } # inherit the name -- use module so that ApiComparison comes out correct (real classes inherit their name method)
- Surrogate.endow self do
+ Surrogate.endow self, hatchery.options do
hatchling.api_methods.each { |name, options| define name, options.to_hash, &options.default_proc }
end
hatchery.api_methods.each { |name, options| define name, options.to_hash, &options.default_proc }
+ overrides.each { |attribute, value| @hatchling.prepare_method attribute, [value] }
end
end
# Custom new, because user can define initialize, and we need to record it
def new(*args)
- instance = allocate
- self.last_instance = instance
- instance.instance_variable_set :@hatchling, Hatchling.new(instance, @hatchery)
+ instance = Endower.uninitialized_instance_for self
instance.__send__ :initialize, *args
instance
end
def last_instance