lib/contracts.fy in fancy-0.7.0 vs lib/contracts.fy in fancy-0.8.0

- old
+ new

@@ -1,57 +1,89 @@ -# class Class { -# class Contracts { -# class ConditionBuilder : Proxy { -# def initialize: block for: @method class: @class { -# @conditions = <[]> -# block call: [self] -# insert_validations -# } +class Class { + class Contracts { + class InterfaceNotImplementedError : ArgumentError { + """ + Exception class that gets raised during @Class@ inclusion + whenever a class doesn't implement another class' required + interface. + """ -# def unknown_message: m with_params: p { -# if: (p size > 0 ) then: { -# @conditions[m to_s[[0,-2]]]: $ p first -# } -# } + read_slots: ('methods, 'interface, 'including_class) + def initialize: @methods interface: @interface including_class: @including_class { + initialize: \ + "Methods #{@methods inspect} not implemented for interface: #{@interface name} required in class: #{@including_class name}" + } + } + } -# def insert_validations { -# name = @method name() -# alias_name = "__#{name}__orig__" -# conditions = @conditions + lazy_slot: 'expected_interface_methods value: { [] } + lazy_slot: 'provided_interface_methods value: { [] } -# @class alias_method: alias_name for: name -# @class define_method: name with: |p| { -# conditions each: |attr block| { -# # TODO -# } -# receive_message: alias_name with_params: p -# } -# } -# } -# } + def expects_interface_on_inclusion: methods { + """ + @methods Collection of method signatures to expect on inclusion into another @Class@. -# def preconditions: block for: method { -# Contracts ConditionBuilder new: block for: method class: self -# } + Declares a required interface (collection of method signatures) an including class has to provide. -# def postconditions: block for: method { -# Contracts ConditionBuilder new: block for: method class: self -# } -# } + Example: + class Enumerable { + expects_interface_on_inclusion: ['each:] + } + """ -# # example + @expected_interface_methods = methods to_a + } -# class Person { -# read_slots: ('name, 'age) + alias_method: 'expects_interface: for: 'expects_interface_on_inclusion: -# preconditions: @{ -# name: @{ blank? not } -# age: @{ > 0 } -# } for: $ def initialize: @name age: @age -# } + def provides_interface: methods { + """ + @methods Collection of method signatures this class explicitly declares to provide. + Example: + class MyCollection { + # you can skip this if you actually define each: before you include Fancy Enumerable. + provides_interface: ['each] + includes: Fancy Enumerable + } + """ -# p = Person new: "chris" age: 25 -# p inspect println + provided_interface_methods append: (methods to_a) + } + def provides_interface?: methods { + """ + @methods Collection of method signatures (an interface) to check for. + @return @true if all methods in @methods are provided by @self, @false otherwise. + """ -nil + pim = Set new: $ provided_interface_methods map: @{ message_name to_s } + instance_methods + methods all?: |m| { pim includes?: (m message_name to_s) } + } + + def missing_methods_for_interface: methods { + """ + @methods Collection of method signatures to check. + @return Collection of methods in @methods this class doesn't provide. + """ + + pim = Set new: $ provided_interface_methods map: @{ message_name to_s } + instance_methods + methods select: |m| { pim includes?: (m message_name to_s) . not } + } + + def included: class { + """ + @class @Class@ to be included in @self. Checks possible interface requirements. + + Default include hook. Make sure to call this via `super included: class`. + """ + + class provides_interface?: expected_interface_methods + unless: (class provides_interface?: expected_interface_methods) do: { + not_implemented_methods = class missing_methods_for_interface: expected_interface_methods + if: (not_implemented_methods size > 0) then: { + Contracts InterfaceNotImplementedError new: not_implemented_methods interface: self including_class: class . raise! + } + } + true + } +} \ No newline at end of file