Top Level Namespace
Defined Under Namespace
Modules: ClassInterface
Instance Method Summary collapse
-
#extract_caller_path(caller_locations) ⇒ String
extracts the (module) class name of the caller e.g.
-
#implements(interface_constant) ⇒ Object
-
ensures self.static methods to be defined (checking parameters count to be equal) - ensures instance methods to be defined (checking parameters count to be equal) - ensures CONSTANTS to be defined (checking for type of CONSTANTS).
-
Instance Method Details
#extract_caller_path(caller_locations) ⇒ String
extracts the (module) class name of the caller e.g. ModuleName::ClassName
104 105 106 107 108 109 110 111 112 113 |
# File 'lib/class_interface.rb', line 104 def extract_caller_path(caller_locations) path_elements = [] caller_locations.each do |el| if el.label.start_with?('<class:') || el.label.start_with?('<module:') name = el.label.gsub(/\<class:|\<module:/, '')[0...-1] path_elements.push name end end path_elements.reverse.join("::") end |
#implements(interface_constant) ⇒ Object
-
ensures self.static methods to be defined (checking parameters count to be equal)
-
ensures instance methods to be defined (checking parameters count to be equal)
-
ensures CONSTANTS to be defined (checking for type of CONSTANTS)
19 20 21 22 23 24 25 26 27 28 29 30 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/class_interface.rb', line 19 def implements(interface_constant) implementation_class = extract_caller_path caller_locations(1, 16) if Object.const_defined? interface_constant.to_s #-------------------------------------------------------------------------- # check if definitions exist #-------------------------------------------------------------------------- # instance methods interface_instance_methods = Object.const_get(interface_constant.to_s).instance_methods false implementation_instance_methods = Object.const_get(implementation_class.to_s).instance_methods false unimplemented_instance_methods = interface_instance_methods - implementation_instance_methods # static methods interface_static_methods = Object.const_get(interface_constant.to_s).methods false implementation_static_methods = Object.const_get(implementation_class.to_s).methods false unimplemented_static_methods = interface_static_methods - implementation_static_methods # constants interface_constants = Object.const_get(interface_constant.to_s).constants implementation_constants = Object.const_get(implementation_class.to_s).constants unimplemented_constants = interface_constants - implementation_constants if unimplemented_instance_methods.any? || unimplemented_static_methods.any? || unimplemented_constants.any? = "\nThe interface '#{interface_constant}' was not completely implemented in class '#{implementation_class}'." += "\n - missing instance methods:\n #{unimplemented_instance_methods.join(' ')}" if unimplemented_instance_methods.any? += "\n - missing static methods:\n #{unimplemented_static_methods.join(' ')}" if unimplemented_static_methods.any? += "\n - missing constants:\n #{unimplemented_constants.join(' ')}" if unimplemented_constants.any? raise ClassInterface::ImplementationIncompleteError, end #-------------------------------------------------------------------------- # check if method parameters count does match on implementation and interface #-------------------------------------------------------------------------- interface_instance_methods.each do |method_name| if_params_count_arity = Object.const_get(interface_constant.to_s).new.method(method_name).arity impl_params_count_arity = Object.const_get(implementation_class).new.method(method_name).arity if if_params_count_arity != impl_params_count_arity raise ClassInterface::ImplementationParameterCountError, "Parameters of instance method '#{implementation_class}##{method_name}' do not match with interface '#{interface_constant}##{method_name}' (given #{impl_params_count_arity.abs}, expected #{if_params_count_arity.abs})" end end interface_static_methods.each do |method_name| if_params_count_arity = Object.const_get(interface_constant.to_s).method(method_name).arity impl_params_count_arity = Object.const_get(implementation_class).method(method_name).arity if if_params_count_arity != impl_params_count_arity raise ClassInterface::ImplementationParameterCountError, "Parameters of static method '#{implementation_class}##{method_name}' do not match with interface '#{interface_constant}##{method_name}' (given #{impl_params_count_arity.abs}, expected #{if_params_count_arity.abs})" end end #-------------------------------------------------------------------------- # check if class types of CONSTANTS do match #-------------------------------------------------------------------------- invalid_const_definitions = [] if_const_class_type = nil interface_constants.each do |const_name| if_const_string = "%s::%s" % [interface_constant, const_name] if_const_class_type = Object.const_get(if_const_string) if_const_class_type = Object.const_get(if_const_string).class unless Object.const_get(if_const_string).class == Class invalid_const_definitions += [const_name] unless if_const_class_type.class == Class next if if_const_class_type.to_s == 'NilClass' impl_const_value_type = Object.const_get("%s::%s" % [implementation_class, const_name]).class ruby_version_lt_24 = (1.class.to_s == 'Fixnum') ruby_version_gte_24 = (1.class.to_s == 'Integer') # Ruby < 2.4 if ruby_version_lt_24 && if_const_class_type.to_s == 'Numeric' && %w[Fixnum Float Bignum].include?(impl_const_value_type.to_s) # we are fine, specific case for numbers # Ruby => 2.4 elsif ruby_version_gte_24 && if_const_class_type.to_s == 'Numeric' && %w[Integer Float].include?(impl_const_value_type.to_s) # we are fine, specific case for numbers elsif ruby_version_lt_24 && ((if_const_class_type.to_s == 'Bignum' && impl_const_value_type.to_s == 'Integer') || (if_const_class_type.to_s == 'Fixnum' && impl_const_value_type.to_s == 'Integer') || (if_const_class_type.to_s == 'Integer' && impl_const_value_type.to_s == 'Fixnum') || (if_const_class_type.to_s == 'Integer' && impl_const_value_type.to_s == 'Bignum')) # Ruby 2.4 unifies Fixnum and Bignum into Integer elsif if_const_class_type != impl_const_value_type raise ClassInterface::ImplementationConstantTypeError, "Value type of constant '#{implementation_class}::#{const_name}' does not match interface '#{if_const_string}'. (#{impl_const_value_type} given, #{if_const_class_type} expected)" end end raise ClassInterface::InterfaceConstantDefinitionError, "Value of constant(s) '#{invalid_const_definitions.join(" ")}' of interface '#{interface_constant}' must be a class constant or nil" if invalid_const_definitions.any? else raise ClassInterface::InterfaceNotDefinedError, "Interface '#{interface_constant}' is not defined" end end |