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