diff --git a/TUTORIAL.md b/TUTORIAL.md index 56ad581..c833c51 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -510,12 +510,11 @@ This is because the first contract eliminated the possibility of `age` being les ## Contracts in modules -To use contracts on module you need to include both `Contracts` and `Contracts::Modules` into it: +Usage is the same as contracts in classes: ```ruby module M include Contracts - include Contracts::Modules Contract String => String def self.parse diff --git a/lib/contracts.rb b/lib/contracts.rb index f50087d..909d5de 100644 --- a/lib/contracts.rb +++ b/lib/contracts.rb @@ -1,12 +1,12 @@ require "contracts/builtin_contracts" require "contracts/decorators" -require "contracts/eigenclass" require "contracts/errors" require "contracts/formatters" require "contracts/invariants" require "contracts/method_reference" -require "contracts/modules" require "contracts/support" +require "contracts/engine" +require "contracts/method_handler" module Contracts def self.included(base) @@ -18,15 +18,13 @@ def self.extended(base) end def self.common(base) - Eigenclass.lift(base) - return if base.respond_to?(:Contract) base.extend(MethodDecorators) base.instance_eval do def functype(funcname) - contracts = decorated_methods[:class_methods][funcname] + contracts = Engine.fetch_from(self).decorated_methods_for(:class_methods, funcname) if contracts.nil? "No contract for #{self}.#{funcname}" else @@ -36,22 +34,14 @@ def functype(funcname) end base.class_eval do - unless base.instance_of?(Module) - def Contract(*args) - return if ENV["NO_CONTRACTS"] - if self.class == Module - puts %{ -Warning: You have added a Contract on a module function -without including Contracts::Modules. Your Contract will -just be ignored. Please include Contracts::Modules into -your module.} - end - self.class.Contract(*args) - end + # TODO: deprecate + # Required when contracts are included in global scope + def Contract(*args) + self.class.Contract(*args) end def functype(funcname) - contracts = self.class.decorated_methods[:instance_methods][funcname] + contracts = Engine.fetch_from(self.class).decorated_methods_for(:instance_methods, funcname) if contracts.nil? "No contract for #{self.class}.#{funcname}" else diff --git a/lib/contracts/decorators.rb b/lib/contracts/decorators.rb index ab0290c..278c26c 100644 --- a/lib/contracts/decorators.rb +++ b/lib/contracts/decorators.rb @@ -1,208 +1,18 @@ module Contracts module MethodDecorators def self.extended(klass) - return if klass.respond_to?(:decorated_methods=) - - class << klass - attr_accessor :decorated_methods - end - end - - module EigenclassWithOwner - def self.lift(eigenclass) - fail Contracts::ContractsNotIncluded unless with_owner?(eigenclass) - - eigenclass - end - - private - - def self.with_owner?(eigenclass) - eigenclass.respond_to?(:owner_class) && eigenclass.owner_class - end + Engine.apply(klass) end - # first, when you write a contract, the decorate method gets called which - # sets the @decorators variable. Then when the next method after the contract - # is defined, method_added is called and we look at the @decorators variable - # to find the decorator for that method. This is how we associate decorators - # with methods. def method_added(name) - common_method_added name, false + MethodHandler.new(name, false, self).handle super end def singleton_method_added(name) - common_method_added name, true + MethodHandler.new(name, true, self).handle super end - - def pop_decorators - Array(@decorators).tap { @decorators = nil } - end - - def fetch_decorators - pop_decorators + Eigenclass.lift(self).pop_decorators - end - - def common_method_added(name, is_class_method) - decorators = fetch_decorators - return if decorators.empty? - - @decorated_methods ||= { :class_methods => {}, :instance_methods => {} } - - if is_class_method - method_reference = SingletonMethodReference.new(name, method(name)) - method_type = :class_methods - else - method_reference = MethodReference.new(name, instance_method(name)) - method_type = :instance_methods - end - - @decorated_methods[method_type][name] ||= [] - - unless decorators.size == 1 - fail %{ -Oops, it looks like method '#{name}' has multiple contracts: -#{decorators.map { |x| x[1][0].inspect }.join("\n")} - -Did you accidentally put more than one contract on a single function, like so? - -Contract String => String -Contract Num => String -def foo x -end - -If you did NOT, then you have probably discovered a bug in this library. -Please file it along with the relevant code at: -https://github.com/egonSchiele/contracts.ruby/issues - } - end - - pattern_matching = false - decorators.each do |klass, args| - # a reference to the method gets passed into the contract here. This is good because - # we are going to redefine this method with a new name below...so this reference is - # now the *only* reference to the old method that exists. - # We assume here that the decorator (klass) responds to .new - decorator = klass.new(self, method_reference, *args) - new_args_contract = decorator.args_contracts - matched = @decorated_methods[method_type][name].select do |contract| - contract.args_contracts == new_args_contract - end - unless matched.empty? - fail ContractError.new(%{ -It looks like you are trying to use pattern-matching, but -multiple definitions for function '#{name}' have the same -contract for input parameters: - -#{(matched + [decorator]).map(&:to_s).join("\n")} - -Each definition needs to have a different contract for the parameters. - }, {}) - end - @decorated_methods[method_type][name] << decorator - pattern_matching ||= decorator.pattern_match? - end - - if @decorated_methods[method_type][name].any? { |x| x.method != method_reference } - @decorated_methods[method_type][name].each(&:pattern_match!) - - pattern_matching = true - end - - method_reference.make_alias(self) - - return if ENV["NO_CONTRACTS"] && !pattern_matching - - # in place of this method, we are going to define our own method. This method - # just calls the decorator passing in all args that were to be passed into the method. - # The decorator in turn has a reference to the actual method, so it can call it - # on its own, after doing it's decorating of course. - - # Very important: THe line `current = #{self}` in the start is crucial. - # Not having it means that any method that used contracts could NOT use `super` - # (see this issue for example: https://github.com/egonSchiele/contracts.ruby/issues/27). - # Here's why: Suppose you have this code: - # - # class Foo - # Contract String - # def to_s - # "Foo" - # end - # end - # - # class Bar < Foo - # Contract String - # def to_s - # super + "Bar" - # end - # end - # - # b = Bar.new - # p b.to_s - # - # `to_s` in Bar calls `super`. So you expect this to call `Foo`'s to_s. However, - # we have overwritten the function (that's what this next defn is). So it gets a - # reference to the function to call by looking at `decorated_methods`. - # - # Now, this line used to read something like: - # - # current = self#{is_class_method ? "" : ".class"} - # - # In that case, `self` would always be `Bar`, regardless of whether you were calling - # Foo's to_s or Bar's to_s. So you would keep getting Bar's decorated_methods, which - # means you would always call Bar's to_s...infinite recursion! Instead, you want to - # call Foo's version of decorated_methods. So the line needs to be `current = #{self}`. - - current = self - method_reference.make_definition(self) do |*args, &blk| - ancestors = current.ancestors - ancestors.shift # first one is just the class itself - while current && !current.respond_to?(:decorated_methods) || current.decorated_methods.nil? - current = ancestors.shift - end - if !current.respond_to?(:decorated_methods) || current.decorated_methods.nil? - fail "Couldn't find decorator for method " + self.class.name + ":#{name}.\nDoes this method look correct to you? If you are using contracts from rspec, rspec wraps classes in it's own class.\nLook at the specs for contracts.ruby as an example of how to write contracts in this case." - end - methods = current.decorated_methods[method_type][name] - - # this adds support for overloading methods. Here we go through each method and call it with the arguments. - # If we get a ContractError, we move to the next function. Otherwise we return the result. - # If we run out of functions, we raise the last ContractError. - success = false - i = 0 - result = nil - expected_error = methods[0].failure_exception - until success - method = methods[i] - i += 1 - begin - success = true - result = method.call_with(self, *args, &blk) - rescue expected_error => error - success = false - unless methods[i] - begin - ::Contract.failure_callback(error.data, false) - rescue expected_error => final_error - raise final_error.to_contract_error - end - end - end - end - result - end - end - - def decorate(klass, *args) - if Support.eigenclass? self - return EigenclassWithOwner.lift(self).owner_class.decorate(klass, *args) - end - - @decorators ||= [] - @decorators << [klass, args] - end end class Decorator @@ -220,7 +30,7 @@ def self.inherited(klass) # inside, `decorate` is called with those params. MethodDecorators.module_eval <<-ruby_eval, __FILE__, __LINE__ + 1 def #{klass}(*args, &blk) - decorate(#{klass}, *args, &blk) + ::Contracts::Engine.fetch_from(self).decorate(#{klass}, *args, &blk) end ruby_eval end diff --git a/lib/contracts/eigenclass.rb b/lib/contracts/eigenclass.rb deleted file mode 100644 index e95ece4..0000000 --- a/lib/contracts/eigenclass.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Contracts - module Eigenclass - def self.extended(eigenclass) - return if eigenclass.respond_to?(:owner_class=) - - class << eigenclass - attr_accessor :owner_class - end - end - - def self.lift(base) - return NullEigenclass if Support.eigenclass? base - - eigenclass = Support.eigenclass_of base - - eigenclass.extend(Eigenclass) unless eigenclass.respond_to?(:owner_class=) - - unless eigenclass.respond_to?(:pop_decorators) - eigenclass.extend(MethodDecorators) - eigenclass.send(:include, Contracts) - end - - eigenclass.owner_class = base - - eigenclass - end - - module NullEigenclass - def self.owner_class - self - end - - def self.pop_decorators - [] - end - end - end -end diff --git a/lib/contracts/engine.rb b/lib/contracts/engine.rb new file mode 100644 index 0000000..063a397 --- /dev/null +++ b/lib/contracts/engine.rb @@ -0,0 +1,26 @@ +require "contracts/engine/base" +require "contracts/engine/target" +require "contracts/engine/eigenclass" + +require "forwardable" + +module Contracts + # Engine facade, normally you shouldn't refer internals of Engine + # module directly. + module Engine + class << self + extend Forwardable + + # .apply(klass) - enables contracts engine on klass + # .applied?(klass) - returns true if klass has contracts engine + # .fetch_from(klass) - returns contracts engine for klass + def_delegators :base_engine, :apply, :applied?, :fetch_from + + private + + def base_engine + Base + end + end + end +end diff --git a/lib/contracts/engine/base.rb b/lib/contracts/engine/base.rb new file mode 100644 index 0000000..cd86630 --- /dev/null +++ b/lib/contracts/engine/base.rb @@ -0,0 +1,136 @@ +module Contracts + module Engine + # Contracts engine + class Base + # Enable contracts engine for klass + # + # @param [Class] klass - target class + def self.apply(klass) + Engine::Target.new(klass).apply + end + + # Returns true if klass has contracts engine + # + # @param [Class] klass - target class + # @return [Bool] + def self.applied?(klass) + Engine::Target.new(klass).applied? + end + + # Fetches contracts engine out of klass + # + # @param [Class] klass - target class + # @return [Engine::Base or Engine::Eigenclass] + def self.fetch_from(klass) + Engine::Target.new(klass).engine + end + + # Creates new instance of contracts engine + # + # @param [Class] klass - class that owns this engine + def initialize(klass) + @klass = klass + end + + # Adds provided decorator to the engine + # It validates that decorator can be added to this engine at the + # moment + # + # @param [Decorator:Class] decorator_class + # @param args - arguments for decorator + def decorate(decorator_class, *args) + validate! + decorators << [decorator_class, args] + end + + # Sets eigenclass' owner to klass + def set_eigenclass_owner + eigenclass_engine.owner_class = klass + end + + # Fetches all accumulated decorators (both this engine and + # corresponding eigenclass' engine) + # It clears all accumulated decorators + # + # @return [ArrayOf[Decorator]] + def all_decorators + pop_decorators + eigenclass_engine.all_decorators + end + + # Fetches decorators of specified type for method with name + # + # @param [Or[:class_methods, :instance_methods]] type - method type + # @param [Symbol] name - method name + # @return [ArrayOf[Decorator]] + def decorated_methods_for(type, name) + Array(decorated_methods[type][name]) + end + + # Returns true if there are any decorated methods + # + # @return [Bool] + def decorated_methods? + !decorated_methods[:class_methods].empty? || + !decorated_methods[:instance_methods].empty? + end + + # Adds method decorator + # + # @param [Or[:class_methods, :instance_methods]] type - method type + # @param [Symbol] name - method name + # @param [Decorator] decorator - method decorator + def add_method_decorator(type, name, decorator) + decorated_methods[type][name] ||= [] + decorated_methods[type][name] << decorator + end + + # Returns nearest ancestor's engine that has decorated methods + # + # @return [Engine::Base or Engine::Eigenclass] + def nearest_decorated_ancestor + current = klass + current_engine = self + ancestors = current.ancestors[1..-1] + + while current && current_engine && !current_engine.decorated_methods? + current = ancestors.shift + current_engine = Engine.fetch_from(current) + end + + current_engine + end + + private + + attr_reader :klass + + def decorated_methods + @_decorated_methods ||= { :class_methods => {}, :instance_methods => {} } + end + + # No-op because it is safe to add decorators to normal classes + def validate! + end + + def pop_decorators + decorators.tap { clear_decorators } + end + + def eigenclass + Support.eigenclass_of(klass) + end + + def eigenclass_engine + Eigenclass.lift(eigenclass, klass) + end + + def decorators + @_decorators ||= [] + end + + def clear_decorators + @_decorators = [] + end + end + end +end diff --git a/lib/contracts/engine/eigenclass.rb b/lib/contracts/engine/eigenclass.rb new file mode 100644 index 0000000..be6f66c --- /dev/null +++ b/lib/contracts/engine/eigenclass.rb @@ -0,0 +1,46 @@ +module Contracts + module Engine + # Special case of contracts engine for eigenclasses + # We don't care about eigenclass of eigenclass at this point + class Eigenclass < Base + # Class that owns this eigenclass + attr_accessor :owner_class + + # Automatically enables eigenclass engine if it is not + # Returns its engine + # NOTE: Required by jruby in 1.9 mode. Otherwise inherited + # eigenclasses don't have their engines + # + # @param [Class] eigenclass - class in question + # @param [Class] owner - owner of eigenclass + # @return [Engine::Eigenclass] + def self.lift(eigenclass, owner) + return Engine.fetch_from(eigenclass) if Engine.applied?(eigenclass) + + Target.new(eigenclass).apply(Eigenclass) + Engine.fetch_from(owner).set_eigenclass_owner + Engine.fetch_from(eigenclass) + end + + # No-op for eigenclasses + def set_eigenclass_owner + end + + # Fetches just eigenclasses decorators + def all_decorators + pop_decorators + end + + private + + # Fails when contracts are not included in owner class + def validate! + fail ContractsNotIncluded unless owner? + end + + def owner? + !!owner_class + end + end + end +end diff --git a/lib/contracts/engine/target.rb b/lib/contracts/engine/target.rb new file mode 100644 index 0000000..f8553f6 --- /dev/null +++ b/lib/contracts/engine/target.rb @@ -0,0 +1,68 @@ +module Contracts + module Engine + # Represents class in question + class Target + # Creates new instance of Target + # + # @param [Class] target - class in question + def initialize(target) + @target = target + end + + # Enable contracts engine for target + # - it is no-op if contracts engine is already enabled + # - it automatically enables contracts engine for its eigenclass + # - it sets owner class to target for its eigenclass + # + # @param [Engine::Base:Class] engine_class - type of engine to + # enable (Base or Eigenclass) + def apply(engine_class = Base) + return if applied? + + apply_to_eigenclass + + eigenclass.class_eval do + define_method(:__contracts_engine) do + @__contracts_engine ||= engine_class.new(self) + end + end + + engine.set_eigenclass_owner + end + + # Returns true if target has contracts engine already + # + # @return [Bool] + def applied? + target.respond_to?(:__contracts_engine) + end + + # Returns contracts engine of target + # + # @return [Engine::Base or Engine::Eigenclass] + def engine + applied? && target.__contracts_engine + end + + private + + attr_reader :target + + def apply_to_eigenclass + return unless meaningless_eigenclass? + + self.class.new(eigenclass).apply(Eigenclass) + eigenclass.extend(MethodDecorators) + eigenclass.send(:include, Contracts) + end + + def eigenclass + Support.eigenclass_of(target) + end + + def meaningless_eigenclass? + !Support.eigenclass?(target) + end + end + end +end diff --git a/lib/contracts/method_handler.rb b/lib/contracts/method_handler.rb new file mode 100644 index 0000000..ee16b6b --- /dev/null +++ b/lib/contracts/method_handler.rb @@ -0,0 +1,195 @@ +module Contracts + # Handles class and instance methods addition + # Represents single such method + class MethodHandler + METHOD_REFERENCE_FACTORY = { + :class_methods => SingletonMethodReference, + :instance_methods => MethodReference + } + + RAW_METHOD_STRATEGY = { + :class_methods => lambda { |target, name| target.method(name) }, + :instance_methods => lambda { |target, name| target.instance_method(name) } + } + + # Creates new instance of MethodHandler + # + # @param [Symbol] method_name + # @param [Bool] is_class_method + # @param [Class] target - class that method got added to + def initialize(method_name, is_class_method, target) + @method_name = method_name + @is_class_method = is_class_method + @target = target + end + + # Handles method addition + def handle + return unless engine? + return if decorators.empty? + + validate_decorators! + validate_pattern_matching! + + engine.add_method_decorator(method_type, method_name, decorator) + mark_pattern_matching_decorators + method_reference.make_alias(target) + redefine_method + end + + private + + attr_reader :method_name, :is_class_method, :target + + def engine? + Engine.applied?(target) + end + + def engine + Engine.fetch_from(target) + end + + def decorators + @_decorators ||= engine.all_decorators + end + + def method_type + @_method_type ||= is_class_method ? :class_methods : :instance_methods + end + # _method_type is required for assigning it to local variable with + # the same name. See: #redefine_method + alias_method :_method_type, :method_type + + def method_reference + @_method_reference ||= METHOD_REFERENCE_FACTORY[method_type].new(method_name, raw_method) + end + + def raw_method + RAW_METHOD_STRATEGY[method_type].call(target, method_name) + end + + def ignore_decorators? + ENV["NO_CONTRACTS"] && !pattern_matching? + end + + def decorated_methods + @_decorated_methods ||= engine.decorated_methods_for(method_type, method_name) + end + + def pattern_matching? + return @_pattern_matching if defined?(@_pattern_matching) + @_pattern_matching = decorated_methods.any? { |x| x.method != method_reference } + end + + def mark_pattern_matching_decorators + return unless pattern_matching? + decorated_methods.each(&:pattern_match!) + end + + def decorator + @_decorator ||= decorator_class.new(target, method_reference, *decorator_args) + end + + def decorator_class + decorators.first[0] + end + + def decorator_args + decorators.first[1] + end + + def redefine_method + return if ignore_decorators? + + # Those are required for instance_eval to be able to refer them + name = method_name + method_type = _method_type + current_engine = engine + + # We are gonna redefine original method here + method_reference.make_definition(target) do |*args, &blk| + engine = current_engine.nearest_decorated_ancestor + + # If we weren't able to find any ancestor that has decorated methods + # FIXME : this looks like untested code (commenting it out doesn't make specs red) + unless engine + fail "Couldn't find decorator for method " + self.class.name + ":#{name}.\nDoes this method look correct to you? If you are using contracts from rspec, rspec wraps classes in it's own class.\nLook at the specs for contracts.ruby as an example of how to write contracts in this case." + end + + # Fetch decorated methods out of the contracts engine + decorated_methods = engine.decorated_methods_for(method_type, name) + + # This adds support for overloading methods. Here we go + # through each method and call it with the arguments. + # If we get a failure_exception, we move to the next + # function. Otherwise we return the result. + # If we run out of functions, we raise the last error, but + # convert it to_contract_error. + success = false + i = 0 + result = nil + expected_error = decorated_methods[0].failure_exception + + until success + decorated_method = decorated_methods[i] + i += 1 + begin + success = true + result = decorated_method.call_with(self, *args, &blk) + rescue expected_error => error + success = false + unless decorated_methods[i] + begin + ::Contract.failure_callback(error.data, false) + rescue expected_error => final_error + raise final_error.to_contract_error + end + end + end + end + + # Return the result of successfully called method + result + end + end + + def validate_decorators! + return if decorators.size == 1 + + fail %{ +Oops, it looks like method '#{name}' has multiple contracts: +#{decorators.map { |x| x[1][0].inspect }.join("\n")} + +Did you accidentally put more than one contract on a single function, like so? + +Contract String => String +Contract Num => String +def foo x +end + +If you did NOT, then you have probably discovered a bug in this library. +Please file it along with the relevant code at: +https://github.com/egonSchiele/contracts.ruby/issues + } + end + + def validate_pattern_matching! + new_args_contract = decorator.args_contracts + matched = decorated_methods.select do |contract| + contract.args_contracts == new_args_contract + end + + return if matched.empty? + + fail ContractError.new(%{ +It looks like you are trying to use pattern-matching, but +multiple definitions for function '#{method_name}' have the same +contract for input parameters: + +#{(matched + [decorator]).map(&:to_s).join("\n")} + +Each definition needs to have a different contract for the parameters. + }, {}) + end + end +end diff --git a/lib/contracts/modules.rb b/lib/contracts/modules.rb deleted file mode 100644 index 1e2a72c..0000000 --- a/lib/contracts/modules.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Contracts - module Modules - def self.included(base) - common(base) - end - - def self.extended(base) - common(base) - end - - def self.common(base) - return unless base.instance_of?(Module) - base.extend(MethodDecorators) - Eigenclass.lift(base) - end - end -end diff --git a/lib/contracts/support.rb b/lib/contracts/support.rb index 671ad9f..718d5c3 100644 --- a/lib/contracts/support.rb +++ b/lib/contracts/support.rb @@ -1,44 +1,55 @@ module Contracts module Support - def self.method_position(method) - return method.method_position if method.is_a?(MethodReference) + class << self + def method_position(method) + return method.method_position if method.is_a?(MethodReference) - if RUBY_VERSION =~ /^1\.8/ - if method.respond_to?(:__file__) - method.__file__ + ":" + method.__line__.to_s + if RUBY_VERSION =~ /^1\.8/ + if method.respond_to?(:__file__) + method.__file__ + ":" + method.__line__.to_s + else + method.inspect + end else - method.inspect + file, line = method.source_location + file + ":" + line.to_s end - else - file, line = method.source_location - file + ":" + line.to_s end - end - def self.method_name(method) - method.is_a?(Proc) ? "Proc" : method.name - end + def method_name(method) + method.is_a?(Proc) ? "Proc" : method.name + end - # Generates unique id, which can be used as a part of identifier - # - # Example: - # Contracts::Support.unique_id # => "i53u6tiw5hbo" - def self.unique_id - # Consider using SecureRandom.hex here, and benchmark which one is better - (Time.now.to_f * 1000).to_i.to_s(36) + rand(1_000_000).to_s(36) - end + # Generates unique id, which can be used as a part of identifier + # + # Example: + # Contracts::Support.unique_id # => "i53u6tiw5hbo" + def unique_id + # Consider using SecureRandom.hex here, and benchmark which one is better + (Time.now.to_f * 1000).to_i.to_s(36) + rand(1_000_000).to_s(36) + end - def self.eigenclass_hierarchy_supported? - return false if RUBY_PLATFORM == "java" && RUBY_VERSION.to_f < 2.0 - RUBY_VERSION.to_f > 1.8 - end + def eigenclass_hierarchy_supported? + return false if RUBY_PLATFORM == "java" && RUBY_VERSION.to_f < 2.0 + RUBY_VERSION.to_f > 1.8 + end - def self.eigenclass_of(target) - class << target; self; end - end + def eigenclass_of(target) + class << target; self; end + end + + def eigenclass?(target) + module_eigenclass?(target) || + target <= eigenclass_of(Object) + end + + private - def self.eigenclass?(target) - target <= eigenclass_of(Object) + # Module eigenclass can be detected by its ancestor chain + # containing a Module + def module_eigenclass?(target) + target < Module + end end end end diff --git a/spec/contracts_spec.rb b/spec/contracts_spec.rb index 3c63c30..af4359c 100644 --- a/spec/contracts_spec.rb +++ b/spec/contracts_spec.rb @@ -250,7 +250,6 @@ def greeting(name) let(:mod) do Module.new do include Contracts - include Contracts::Modules Contract String => String def greeting(name) diff --git a/spec/fixtures/fixtures.rb b/spec/fixtures/fixtures.rb index ad44fbe..018a309 100644 --- a/spec/fixtures/fixtures.rb +++ b/spec/fixtures/fixtures.rb @@ -528,10 +528,7 @@ def on_response(status, body) end module ModuleExample - # This inclusion is required to actually override `method_added` - # hooks for module. include Contracts - include Contracts::Modules Contract Num, Num => Num def plus(a, b)