Module: Interfaces::Castable
- Included in:
- Object
- Defined in:
- lib/interfaces/castable.rb
Overview
a mixin that defines the ‘as’ method to allow an object to be cast as an interface
Constant Summary collapse
- BUILT_IN_CONVERSIONS =
{ Array => :to_a, String => :to_s, Symbol => :to_sym, Integer => :to_i, Float => :to_f, Hash => :to_h # only on ruby 2.0 }
Class Method Summary collapse
-
.convert_type(instance, type) ⇒ Object
attempts to convert non-interface types.
Instance Method Summary collapse
-
#as(interface) ⇒ Object
casts an object to an instance of a particular Interface.
-
#conforms_to?(interface) ⇒ Boolean
returns true if an object conforms to the given interface (implements the abstract methods).
Class Method Details
.convert_type(instance, type) ⇒ Object
attempts to convert non-interface types
14 15 16 17 18 19 20 21 |
# File 'lib/interfaces/castable.rb', line 14 def self.convert_type(instance, type) method = BUILT_IN_CONVERSIONS[type] if method && instance.respond_to?(method) instance.send(method) else raise NonConvertableObjectError, "Don't know how to convert #{instance} to #{type}" end end |
Instance Method Details
#as(interface) ⇒ Object
casts an object to an instance of a particular Interface
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/interfaces/castable.rb', line 31 def as(interface) # interface must be a class raise InterfaceError, "#{interface} is not a class" unless interface.is_a?(Class) # check if object already is an instance of interface return self if self.kind_of?(interface) # check if interface is really an interface if interface < Interface # cache the resulting interface so that we can load it faster next # time and so that it can save state cache = self.instance_variable_get(:@interface_cache) unless cache cache = {} self.instance_variable_set(:@interface_cache, cache) end cache[interface] ||= begin i = interface.new delegate = self non_implemented_methods = [] # define singleton methods that delegate back to this object for each abstract method interface.abstract_methods.each do |method| non_implemented_methods << method unless self.respond_to?(method) i.define_singleton_method(method) do |*args| delegate.send(method, *args) end end # raise an exception if all abstract methods are not overridden unless non_implemented_methods.empty? raise NonConformingObjectError, "#{self} does not conform to interface #{interface}. Expected methods not implemented: #{non_implemented_methods.join(", ")}" end # define singleton methods that delegate back to this object for each optional method interface.optional_methods.each do |method| if self.respond_to?(method) i.define_singleton_method(method) do |*args| delegate.send(method, *args) end end end i end else # interface is not really an interface, it's just a Class # use some built-in conversions Castable.convert_type(self, interface) end end |
#conforms_to?(interface) ⇒ Boolean
returns true if an object conforms to the given interface (implements the abstract methods)
24 25 26 27 28 |
# File 'lib/interfaces/castable.rb', line 24 def conforms_to?(interface) # interface must be an Interface raise InterfaceError, "#{interface} is not an Interface" unless interface < Interface interface.abstract_methods.all? { |method| self.respond_to?(method) } end |