187 lines
4.7 KiB
Ruby
187 lines
4.7 KiB
Ruby
|
require 'naught/basic_object'
|
||
|
require 'naught/conversions'
|
||
|
|
||
|
module Naught
|
||
|
class NullClassBuilder
|
||
|
# make sure this module exists
|
||
|
module Commands
|
||
|
end
|
||
|
|
||
|
attr_accessor :base_class, :inspect_proc, :interface_defined
|
||
|
|
||
|
def initialize
|
||
|
@interface_defined = false
|
||
|
@base_class = Naught::BasicObject
|
||
|
@inspect_proc = lambda { '<null>' }
|
||
|
@stub_strategy = :stub_method_returning_nil
|
||
|
define_basic_methods
|
||
|
end
|
||
|
|
||
|
def interface_defined?
|
||
|
!!@interface_defined
|
||
|
end
|
||
|
|
||
|
def customize(&customization_block)
|
||
|
return unless customization_block
|
||
|
customization_module.module_exec(self, &customization_block)
|
||
|
end
|
||
|
|
||
|
def customization_module
|
||
|
@customization_module ||= Module.new
|
||
|
end
|
||
|
|
||
|
def null_equivalents
|
||
|
@null_equivalents ||= [nil]
|
||
|
end
|
||
|
|
||
|
def generate_class
|
||
|
respond_to_any_message unless interface_defined?
|
||
|
generation_mod = Module.new
|
||
|
customization_mod = customization_module # get a local binding
|
||
|
builder = self
|
||
|
|
||
|
apply_operations(operations, generation_mod)
|
||
|
|
||
|
null_class = Class.new(@base_class) do
|
||
|
const_set :GeneratedMethods, generation_mod
|
||
|
const_set :Customizations, customization_mod
|
||
|
const_set :NULL_EQUIVS, builder.null_equivalents
|
||
|
include Conversions
|
||
|
remove_const :NULL_EQUIVS
|
||
|
Conversions.instance_methods.each do |instance_method|
|
||
|
undef_method(instance_method)
|
||
|
end
|
||
|
const_set :Conversions, Conversions
|
||
|
|
||
|
include NullObjectTag
|
||
|
include generation_mod
|
||
|
include customization_mod
|
||
|
end
|
||
|
|
||
|
apply_operations(class_operations, null_class)
|
||
|
|
||
|
null_class
|
||
|
end
|
||
|
|
||
|
def method_missing(method_name, *args, &block)
|
||
|
command_name = command_name_for_method(method_name)
|
||
|
if Commands.const_defined?(command_name)
|
||
|
command_class = Commands.const_get(command_name)
|
||
|
command_class.new(self, *args, &block).call
|
||
|
else
|
||
|
super
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if RUBY_VERSION >= '1.9'
|
||
|
def respond_to_missing?(method_name, include_private = false)
|
||
|
command_name = command_name_for_method(method_name)
|
||
|
Commands.const_defined?(command_name) || super
|
||
|
rescue NameError
|
||
|
super
|
||
|
end
|
||
|
else
|
||
|
def respond_to?(method_name, include_private = false)
|
||
|
command_name = command_name_for_method(method_name)
|
||
|
Commands.const_defined?(command_name) || super
|
||
|
rescue NameError
|
||
|
super
|
||
|
end
|
||
|
end
|
||
|
|
||
|
############################################################################
|
||
|
# Builder API
|
||
|
#
|
||
|
# See also the contents of lib/naught/null_class_builder/commands
|
||
|
############################################################################
|
||
|
|
||
|
def black_hole
|
||
|
@stub_strategy = :stub_method_returning_self
|
||
|
end
|
||
|
|
||
|
def respond_to_any_message
|
||
|
defer(:prepend => true) do |subject|
|
||
|
subject.module_eval do
|
||
|
def respond_to?(*)
|
||
|
true
|
||
|
end
|
||
|
end
|
||
|
stub_method(subject, :method_missing)
|
||
|
end
|
||
|
@interface_defined = true
|
||
|
end
|
||
|
|
||
|
def defer(options = {}, &deferred_operation)
|
||
|
list = options[:class] ? class_operations : operations
|
||
|
if options[:prepend]
|
||
|
list.unshift(deferred_operation)
|
||
|
else
|
||
|
list << deferred_operation
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def stub_method(subject, name)
|
||
|
send(@stub_strategy, subject, name)
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def define_basic_methods
|
||
|
define_basic_instance_methods
|
||
|
define_basic_class_methods
|
||
|
end
|
||
|
|
||
|
def apply_operations(operations, module_or_class)
|
||
|
operations.each do |operation|
|
||
|
operation.call(module_or_class)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def define_basic_instance_methods
|
||
|
defer do |subject|
|
||
|
subject.module_exec(@inspect_proc) do |inspect_proc|
|
||
|
define_method(:inspect, &inspect_proc)
|
||
|
def initialize(*)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def define_basic_class_methods
|
||
|
defer(:class => true) do |subject|
|
||
|
subject.module_eval do
|
||
|
class << self
|
||
|
alias_method :get, :new
|
||
|
end
|
||
|
klass = self
|
||
|
define_method(:class) { klass }
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def class_operations
|
||
|
@class_operations ||= []
|
||
|
end
|
||
|
|
||
|
def operations
|
||
|
@operations ||= []
|
||
|
end
|
||
|
|
||
|
def stub_method_returning_nil(subject, name)
|
||
|
subject.module_eval do
|
||
|
define_method(name) { |*| nil }
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def stub_method_returning_self(subject, name)
|
||
|
subject.module_eval do
|
||
|
define_method(name) { |*| self }
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def command_name_for_method(method_name)
|
||
|
method_name.to_s.gsub(/(?:^|_)([a-z])/) { Regexp.last_match[1].upcase }
|
||
|
end
|
||
|
end
|
||
|
end
|