contracts 0.7 → 0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +16 -1
- data/README.md +12 -4
- data/TUTORIAL.md +67 -18
- data/benchmarks/bench.rb +20 -20
- data/benchmarks/invariants.rb +28 -18
- data/benchmarks/io.rb +62 -0
- data/benchmarks/wrap_test.rb +11 -13
- data/contracts.gemspec +1 -1
- data/lib/contracts.rb +167 -109
- data/lib/contracts/builtin_contracts.rb +132 -84
- data/lib/contracts/decorators.rb +41 -53
- data/lib/contracts/eigenclass.rb +3 -7
- data/lib/contracts/errors.rb +1 -1
- data/lib/contracts/formatters.rb +134 -0
- data/lib/contracts/invariants.rb +7 -8
- data/lib/contracts/method_reference.rb +33 -9
- data/lib/contracts/support.rb +9 -3
- data/lib/contracts/testable.rb +6 -6
- data/lib/contracts/version.rb +1 -1
- data/script/rubocop.rb +5 -0
- data/spec/builtin_contracts_spec.rb +57 -9
- data/spec/contracts_spec.rb +182 -53
- data/spec/fixtures/fixtures.rb +130 -9
- data/spec/invariants_spec.rb +0 -2
- data/spec/module_spec.rb +2 -2
- data/spec/ruby_version_specific/contracts_spec_1.9.rb +18 -0
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +22 -0
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +55 -0
- data/spec/spec_helper.rb +9 -2
- data/spec/support.rb +4 -0
- data/spec/support_spec.rb +21 -0
- metadata +11 -3
- data/lib/contracts/core_ext.rb +0 -15
    
        data/lib/contracts/decorators.rb
    CHANGED
    
    | @@ -10,9 +10,7 @@ module Contracts | |
| 10 10 |  | 
| 11 11 | 
             
                module EigenclassWithOwner
         | 
| 12 12 | 
             
                  def self.lift(eigenclass)
         | 
| 13 | 
            -
                    unless with_owner?(eigenclass)
         | 
| 14 | 
            -
                      raise Contracts::ContractsNotIncluded
         | 
| 15 | 
            -
                    end
         | 
| 13 | 
            +
                    fail Contracts::ContractsNotIncluded unless with_owner?(eigenclass)
         | 
| 16 14 |  | 
| 17 15 | 
             
                    eigenclass
         | 
| 18 16 | 
             
                  end
         | 
| @@ -51,18 +49,14 @@ module Contracts | |
| 51 49 | 
             
                  decorators = fetch_decorators
         | 
| 52 50 | 
             
                  return if decorators.empty?
         | 
| 53 51 |  | 
| 54 | 
            -
                  @decorated_methods ||= {:class_methods => {}, :instance_methods => {}}
         | 
| 52 | 
            +
                  @decorated_methods ||= { :class_methods => {}, :instance_methods => {} }
         | 
| 55 53 |  | 
| 56 54 | 
             
                  if is_class_method
         | 
| 57 55 | 
             
                    method_reference = SingletonMethodReference.new(name, method(name))
         | 
| 58 56 | 
             
                    method_type = :class_methods
         | 
| 59 | 
            -
                    # private_methods is an array of strings on 1.8 and an array of symbols on 1.9
         | 
| 60 | 
            -
                    is_private = self.private_methods.include?(name) || self.private_methods.include?(name.to_s)
         | 
| 61 57 | 
             
                  else
         | 
| 62 58 | 
             
                    method_reference = MethodReference.new(name, instance_method(name))
         | 
| 63 59 | 
             
                    method_type = :instance_methods
         | 
| 64 | 
            -
                    # private_instance_methods is an array of strings on 1.8 and an array of symbols on 1.9
         | 
| 65 | 
            -
                    is_private = self.private_instance_methods.include?(name) || self.private_instance_methods.include?(name.to_s)
         | 
| 66 60 | 
             
                  end
         | 
| 67 61 |  | 
| 68 62 | 
             
                  @decorated_methods[method_type][name] ||= []
         | 
| @@ -79,9 +73,7 @@ module Contracts | |
| 79 73 | 
             
                  end
         | 
| 80 74 |  | 
| 81 75 | 
             
                  if @decorated_methods[method_type][name].any? { |x| x.method != method_reference }
         | 
| 82 | 
            -
                    @decorated_methods[method_type][name].each | 
| 83 | 
            -
                      decorator.pattern_match!
         | 
| 84 | 
            -
                    end
         | 
| 76 | 
            +
                    @decorated_methods[method_type][name].each(&:pattern_match!)
         | 
| 85 77 |  | 
| 86 78 | 
             
                    pattern_matching = true
         | 
| 87 79 | 
             
                  end
         | 
| @@ -95,42 +87,40 @@ module Contracts | |
| 95 87 | 
             
                  # The decorator in turn has a reference to the actual method, so it can call it
         | 
| 96 88 | 
             
                  # on its own, after doing it's decorating of course.
         | 
| 97 89 |  | 
| 98 | 
            -
            = | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
                   | 
| 106 | 
            -
                   | 
| 107 | 
            -
             | 
| 108 | 
            -
                  end
         | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
                   | 
| 113 | 
            -
                   | 
| 114 | 
            -
             | 
| 115 | 
            -
                  end
         | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
                   | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
                call Foo's version of decorated_methods. So the line needs to be `current = #{self}`.
         | 
| 133 | 
            -
            =end
         | 
| 90 | 
            +
                  # Very important: THe line `current = #{self}` in the start is crucial.
         | 
| 91 | 
            +
                  # Not having it means that any method that used contracts could NOT use `super`
         | 
| 92 | 
            +
                  # (see this issue for example: https://github.com/egonSchiele/contracts.ruby/issues/27).
         | 
| 93 | 
            +
                  # Here's why: Suppose you have this code:
         | 
| 94 | 
            +
                  #
         | 
| 95 | 
            +
                  #     class Foo
         | 
| 96 | 
            +
                  #       Contract nil => String
         | 
| 97 | 
            +
                  #       def to_s
         | 
| 98 | 
            +
                  #         "Foo"
         | 
| 99 | 
            +
                  #       end
         | 
| 100 | 
            +
                  #     end
         | 
| 101 | 
            +
                  #
         | 
| 102 | 
            +
                  #     class Bar < Foo
         | 
| 103 | 
            +
                  #       Contract nil => String
         | 
| 104 | 
            +
                  #       def to_s
         | 
| 105 | 
            +
                  #         super + "Bar"
         | 
| 106 | 
            +
                  #       end
         | 
| 107 | 
            +
                  #     end
         | 
| 108 | 
            +
                  #
         | 
| 109 | 
            +
                  #     b = Bar.new
         | 
| 110 | 
            +
                  #     p b.to_s
         | 
| 111 | 
            +
                  #
         | 
| 112 | 
            +
                  #     `to_s` in Bar calls `super`. So you expect this to call `Foo`'s to_s. However,
         | 
| 113 | 
            +
                  #     we have overwritten the function (that's what this next defn is). So it gets a
         | 
| 114 | 
            +
                  #     reference to the function to call by looking at `decorated_methods`.
         | 
| 115 | 
            +
                  #
         | 
| 116 | 
            +
                  #     Now, this line used to read something like:
         | 
| 117 | 
            +
                  #
         | 
| 118 | 
            +
                  #       current = self#{is_class_method ? "" : ".class"}
         | 
| 119 | 
            +
                  #
         | 
| 120 | 
            +
                  #     In that case, `self` would always be `Bar`, regardless of whether you were calling
         | 
| 121 | 
            +
                  #     Foo's to_s or Bar's to_s. So you would keep getting Bar's decorated_methods, which
         | 
| 122 | 
            +
                  #     means you would always call Bar's to_s...infinite recursion! Instead, you want to
         | 
| 123 | 
            +
                  #     call Foo's version of decorated_methods. So the line needs to be `current = #{self}`.
         | 
| 134 124 |  | 
| 135 125 | 
             
                  current = self
         | 
| 136 126 | 
             
                  method_reference.make_definition(self) do |*args, &blk|
         | 
| @@ -140,7 +130,7 @@ Here's why: Suppose you have this code: | |
| 140 130 | 
             
                      current = ancestors.shift
         | 
| 141 131 | 
             
                    end
         | 
| 142 132 | 
             
                    if !current.respond_to?(:decorated_methods) || current.decorated_methods.nil?
         | 
| 143 | 
            -
                       | 
| 133 | 
            +
                      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."
         | 
| 144 134 | 
             
                    end
         | 
| 145 135 | 
             
                    methods = current.decorated_methods[method_type][name]
         | 
| 146 136 |  | 
| @@ -151,7 +141,7 @@ Here's why: Suppose you have this code: | |
| 151 141 | 
             
                    i = 0
         | 
| 152 142 | 
             
                    result = nil
         | 
| 153 143 | 
             
                    expected_error = methods[0].failure_exception
         | 
| 154 | 
            -
                     | 
| 144 | 
            +
                    until success
         | 
| 155 145 | 
             
                      method = methods[i]
         | 
| 156 146 | 
             
                      i += 1
         | 
| 157 147 | 
             
                      begin
         | 
| @@ -170,12 +160,10 @@ Here's why: Suppose you have this code: | |
| 170 160 | 
             
                    end
         | 
| 171 161 | 
             
                    result
         | 
| 172 162 | 
             
                  end
         | 
| 173 | 
            -
             | 
| 174 | 
            -
                  method_reference.make_private(self) if is_private
         | 
| 175 163 | 
             
                end
         | 
| 176 164 |  | 
| 177 165 | 
             
                def decorate(klass, *args)
         | 
| 178 | 
            -
                  if  | 
| 166 | 
            +
                  if Support.eigenclass? self
         | 
| 179 167 | 
             
                    return EigenclassWithOwner.lift(self).owner_class.decorate(klass, *args)
         | 
| 180 168 | 
             
                  end
         | 
| 181 169 |  | 
| @@ -189,7 +177,7 @@ Here's why: Suppose you have this code: | |
| 189 177 | 
             
                class << self; attr_accessor :decorators; end
         | 
| 190 178 |  | 
| 191 179 | 
             
                def self.inherited(klass)
         | 
| 192 | 
            -
                  name = klass.name.gsub(/^./) {|m| m.downcase}
         | 
| 180 | 
            +
                  name = klass.name.gsub(/^./) { |m| m.downcase }
         | 
| 193 181 |  | 
| 194 182 | 
             
                  return if name =~ /^[^A-Za-z_]/ || name =~ /[^0-9A-Za-z_]/
         | 
| 195 183 |  | 
    
        data/lib/contracts/eigenclass.rb
    CHANGED
    
    | @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            module Contracts
         | 
| 2 2 | 
             
              module Eigenclass
         | 
| 3 | 
            -
             | 
| 4 3 | 
             
                def self.extended(eigenclass)
         | 
| 5 4 | 
             
                  return if eigenclass.respond_to?(:owner_class=)
         | 
| 6 5 |  | 
| @@ -10,13 +9,11 @@ module Contracts | |
| 10 9 | 
             
                end
         | 
| 11 10 |  | 
| 12 11 | 
             
                def self.lift(base)
         | 
| 13 | 
            -
                  return NullEigenclass if  | 
| 12 | 
            +
                  return NullEigenclass if Support.eigenclass? base
         | 
| 14 13 |  | 
| 15 | 
            -
                  eigenclass = base | 
| 14 | 
            +
                  eigenclass = Support.eigenclass_of base
         | 
| 16 15 |  | 
| 17 | 
            -
                  unless eigenclass.respond_to?(:owner_class=)
         | 
| 18 | 
            -
                    eigenclass.extend(Eigenclass)
         | 
| 19 | 
            -
                  end
         | 
| 16 | 
            +
                  eigenclass.extend(Eigenclass) unless eigenclass.respond_to?(:owner_class=)
         | 
| 20 17 |  | 
| 21 18 | 
             
                  unless eigenclass.respond_to?(:pop_decorators)
         | 
| 22 19 | 
             
                    eigenclass.extend(MethodDecorators)
         | 
| @@ -37,6 +34,5 @@ module Contracts | |
| 37 34 | 
             
                    []
         | 
| 38 35 | 
             
                  end
         | 
| 39 36 | 
             
                end
         | 
| 40 | 
            -
             | 
| 41 37 | 
             
              end
         | 
| 42 38 | 
             
            end
         | 
    
        data/lib/contracts/errors.rb
    CHANGED
    
    
| @@ -0,0 +1,134 @@ | |
| 1 | 
            +
            module Contracts
         | 
| 2 | 
            +
              # A namespace for classes related to formatting.
         | 
| 3 | 
            +
              module Formatters
         | 
| 4 | 
            +
                # Used to format contracts for the `Expected:` field of error output.
         | 
| 5 | 
            +
                class Expected
         | 
| 6 | 
            +
                  # @param full [Boolean] if false only unique `to_s` values will be output,
         | 
| 7 | 
            +
                  #   non unique values become empty string.
         | 
| 8 | 
            +
                  def initialize(contract, full = true)
         | 
| 9 | 
            +
                    @contract, @full = contract, full
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Formats any type of Contract.
         | 
| 13 | 
            +
                  def contract(contract = @contract)
         | 
| 14 | 
            +
                    if contract.is_a?(Hash)
         | 
| 15 | 
            +
                      hash_contract(contract)
         | 
| 16 | 
            +
                    elsif contract.is_a?(Array)
         | 
| 17 | 
            +
                      array_contract(contract)
         | 
| 18 | 
            +
                    else
         | 
| 19 | 
            +
                      InspectWrapper.create(contract, @full)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Formats Hash contracts.
         | 
| 24 | 
            +
                  def hash_contract(hash)
         | 
| 25 | 
            +
                    @full = true # Complex values output completely, overriding @full
         | 
| 26 | 
            +
                    hash.inject({}) do |repr, (k, v)|
         | 
| 27 | 
            +
                      repr.merge(k => InspectWrapper.create(contract(v), @full))
         | 
| 28 | 
            +
                    end.inspect
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  # Formats Array contracts.
         | 
| 32 | 
            +
                  def array_contract(array)
         | 
| 33 | 
            +
                    @full = true
         | 
| 34 | 
            +
                    array.map { |v| InspectWrapper.create(contract(v), @full) }.inspect
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                # A wrapper class to produce correct inspect behaviour for different
         | 
| 39 | 
            +
                # contract values - constants, Class contracts, instance contracts etc.
         | 
| 40 | 
            +
                module InspectWrapper
         | 
| 41 | 
            +
                  # InspectWrapper is a factory, will never be an instance
         | 
| 42 | 
            +
                  # @return [ClassInspectWrapper, ObjectInspectWrapper]
         | 
| 43 | 
            +
                  def self.create(value, full = true)
         | 
| 44 | 
            +
                    if value.class == Class
         | 
| 45 | 
            +
                      ClassInspectWrapper
         | 
| 46 | 
            +
                    else
         | 
| 47 | 
            +
                      ObjectInspectWrapper
         | 
| 48 | 
            +
                    end.new(value, full)
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  # @param full [Boolean] if false only unique `to_s` values will be output,
         | 
| 52 | 
            +
                  #   non unique values become empty string.
         | 
| 53 | 
            +
                  def initialize(value, full)
         | 
| 54 | 
            +
                    @value, @full = value, full
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  # Inspect different types of contract values.
         | 
| 58 | 
            +
                  # Contracts module prefix will be removed from classes.
         | 
| 59 | 
            +
                  # Custom to_s messages will be wrapped in round brackets to differentiate
         | 
| 60 | 
            +
                  # from standard Strings.
         | 
| 61 | 
            +
                  # Primitive values e.g. 42, true, nil will be left alone.
         | 
| 62 | 
            +
                  def inspect
         | 
| 63 | 
            +
                    return "" unless full?
         | 
| 64 | 
            +
                    return @value.inspect if empty_val?
         | 
| 65 | 
            +
                    return @value.to_s if plain?
         | 
| 66 | 
            +
                    return delim(@value.to_s) if useful_to_s?
         | 
| 67 | 
            +
                    useful_inspect
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  def delim(value)
         | 
| 71 | 
            +
                    @full ? "(#{value})" : "#{value}"
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  # Eliminates eronious quotes in output that plain inspect includes.
         | 
| 75 | 
            +
                  def to_s
         | 
| 76 | 
            +
                    inspect
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  private
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  def empty_val?
         | 
| 82 | 
            +
                    @value.nil? || @value == ""
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  def full?
         | 
| 86 | 
            +
                    @full ||
         | 
| 87 | 
            +
                      @value.is_a?(Hash) || @value.is_a?(Array) ||
         | 
| 88 | 
            +
                      (!plain? && useful_to_s?)
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  def plain?
         | 
| 92 | 
            +
                    # Not a type of contract that can have a custom to_s defined
         | 
| 93 | 
            +
                    !@value.is_a?(CallableClass) && @value.class != Class
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  def useful_to_s?
         | 
| 97 | 
            +
                    # Useless to_s value or no custom to_s behavious defined
         | 
| 98 | 
            +
                    !empty_to_s? && custom_to_s?
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  def empty_to_s?
         | 
| 102 | 
            +
                    @value.to_s.empty?
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  def strip_prefix(val)
         | 
| 106 | 
            +
                    val.gsub(/^Contracts::/, "")
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                class ClassInspectWrapper
         | 
| 111 | 
            +
                  include InspectWrapper
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  def custom_to_s?
         | 
| 114 | 
            +
                    @value.to_s != @value.name
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  def useful_inspect
         | 
| 118 | 
            +
                    strip_prefix(empty_to_s? ? @value.name : @value.inspect)
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                class ObjectInspectWrapper
         | 
| 123 | 
            +
                  include InspectWrapper
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                  def custom_to_s?
         | 
| 126 | 
            +
                    !@value.to_s.match(/#\<\w+:.+\>/)
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  def useful_inspect
         | 
| 130 | 
            +
                    strip_prefix(empty_to_s? ? @value.class.name : @value.inspect)
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
              end
         | 
| 134 | 
            +
            end
         | 
    
        data/lib/contracts/invariants.rb
    CHANGED
    
    | @@ -23,7 +23,7 @@ module Contracts | |
| 23 23 | 
             
                end
         | 
| 24 24 |  | 
| 25 25 | 
             
                module InvariantExtension
         | 
| 26 | 
            -
                  def  | 
| 26 | 
            +
                  def invariant(name, &condition)
         | 
| 27 27 | 
             
                    return if ENV["NO_CONTRACTS"]
         | 
| 28 28 |  | 
| 29 29 | 
             
                    invariants << Invariant.new(self, name, &condition)
         | 
| @@ -53,17 +53,16 @@ module Contracts | |
| 53 53 | 
             
                  end
         | 
| 54 54 |  | 
| 55 55 | 
             
                  def self.failure_callback(data)
         | 
| 56 | 
            -
                     | 
| 56 | 
            +
                    fail InvariantError, failure_msg(data)
         | 
| 57 57 | 
             
                  end
         | 
| 58 58 |  | 
| 59 59 | 
             
                  def self.failure_msg(data)
         | 
| 60 | 
            -
            %{Invariant violation:
         | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 60 | 
            +
                    %{Invariant violation:
         | 
| 61 | 
            +
                        Expected: #{data[:expected]}
         | 
| 62 | 
            +
                        Actual: #{data[:actual]}
         | 
| 63 | 
            +
                        Value guarded in: #{data[:target].class}::#{Support.method_name(data[:method])}
         | 
| 64 | 
            +
                        At: #{Support.method_position(data[:method])}}
         | 
| 65 65 | 
             
                  end
         | 
| 66 66 | 
             
                end
         | 
| 67 | 
            -
             | 
| 68 67 | 
             
              end
         | 
| 69 68 | 
             
            end
         | 
| @@ -2,7 +2,6 @@ module Contracts | |
| 2 2 | 
             
              # MethodReference represents original method reference that was
         | 
| 3 3 | 
             
              # decorated by contracts.ruby. Used for instance methods.
         | 
| 4 4 | 
             
              class MethodReference
         | 
| 5 | 
            -
             | 
| 6 5 | 
             
                attr_reader :name
         | 
| 7 6 |  | 
| 8 7 | 
             
                # name - name of the method
         | 
| @@ -19,13 +18,11 @@ module Contracts | |
| 19 18 |  | 
| 20 19 | 
             
                # Makes a method re-definition in proper way
         | 
| 21 20 | 
             
                def make_definition(this, &blk)
         | 
| 21 | 
            +
                  is_private = private?(this)
         | 
| 22 | 
            +
                  is_protected = protected?(this)
         | 
| 22 23 | 
             
                  alias_target(this).send(:define_method, name, &blk)
         | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
                # Makes a method private
         | 
| 26 | 
            -
                def make_private(this)
         | 
| 27 | 
            -
                  original_name = name
         | 
| 28 | 
            -
                  alias_target(this).class_eval { private original_name }
         | 
| 24 | 
            +
                  make_private(this) if is_private
         | 
| 25 | 
            +
                  make_protected(this) if is_protected
         | 
| 29 26 | 
             
                end
         | 
| 30 27 |  | 
| 31 28 | 
             
                # Aliases original method to a special unique name, which is known
         | 
| @@ -48,6 +45,26 @@ module Contracts | |
| 48 45 |  | 
| 49 46 | 
             
                private
         | 
| 50 47 |  | 
| 48 | 
            +
                # Makes a method private
         | 
| 49 | 
            +
                def make_private(this)
         | 
| 50 | 
            +
                  original_name = name
         | 
| 51 | 
            +
                  alias_target(this).class_eval { private original_name }
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def private?(this)
         | 
| 55 | 
            +
                  this.private_instance_methods.map(&:to_sym).include?(name)
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def protected?(this)
         | 
| 59 | 
            +
                  this.protected_instance_methods.map(&:to_sym).include?(name)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                # Makes a method protected
         | 
| 63 | 
            +
                def make_protected(this)
         | 
| 64 | 
            +
                  original_name = name
         | 
| 65 | 
            +
                  alias_target(this).class_eval { protected original_name }
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 51 68 | 
             
                # Returns alias target for instance methods, subject to be
         | 
| 52 69 | 
             
                # overriden in subclasses.
         | 
| 53 70 | 
             
                def alias_target(this)
         | 
| @@ -61,16 +78,23 @@ module Contracts | |
| 61 78 | 
             
                def construct_unique_name
         | 
| 62 79 | 
             
                  :"__contracts_ruby_original_#{name}_#{Support.unique_id}"
         | 
| 63 80 | 
             
                end
         | 
| 64 | 
            -
             | 
| 65 81 | 
             
              end
         | 
| 66 82 |  | 
| 67 83 | 
             
              # The same as MethodReference, but used for singleton methods.
         | 
| 68 84 | 
             
              class SingletonMethodReference < MethodReference
         | 
| 69 85 | 
             
                private
         | 
| 70 86 |  | 
| 87 | 
            +
                def private?(this)
         | 
| 88 | 
            +
                  this.private_methods.map(&:to_sym).include?(name)
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def protected?(this)
         | 
| 92 | 
            +
                  this.protected_methods.map(&:to_sym).include?(name)
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 71 95 | 
             
                # Return alias target for singleton methods.
         | 
| 72 96 | 
             
                def alias_target(this)
         | 
| 73 | 
            -
                  this | 
| 97 | 
            +
                  Support.eigenclass_of this
         | 
| 74 98 | 
             
                end
         | 
| 75 99 | 
             
              end
         | 
| 76 100 | 
             
            end
         | 
    
        data/lib/contracts/support.rb
    CHANGED
    
    | @@ -1,8 +1,7 @@ | |
| 1 1 | 
             
            module Contracts
         | 
| 2 2 | 
             
              module Support
         | 
| 3 | 
            -
             | 
| 4 3 | 
             
                def self.method_position(method)
         | 
| 5 | 
            -
                  return method.method_position if MethodReference | 
| 4 | 
            +
                  return method.method_position if method.is_a?(MethodReference)
         | 
| 6 5 |  | 
| 7 6 | 
             
                  if RUBY_VERSION =~ /^1\.8/
         | 
| 8 7 | 
             
                    if method.respond_to?(:__file__)
         | 
| @@ -26,7 +25,7 @@ module Contracts | |
| 26 25 | 
             
                #    Contracts::Support.unique_id   # => "i53u6tiw5hbo"
         | 
| 27 26 | 
             
                def self.unique_id
         | 
| 28 27 | 
             
                  # Consider using SecureRandom.hex here, and benchmark which one is better
         | 
| 29 | 
            -
                  (Time.now.to_f * 1000).to_i.to_s(36) + rand( | 
| 28 | 
            +
                  (Time.now.to_f * 1000).to_i.to_s(36) + rand(1_000_000).to_s(36)
         | 
| 30 29 | 
             
                end
         | 
| 31 30 |  | 
| 32 31 | 
             
                def self.eigenclass_hierarchy_supported?
         | 
| @@ -34,5 +33,12 @@ module Contracts | |
| 34 33 | 
             
                  RUBY_VERSION.to_f > 1.8
         | 
| 35 34 | 
             
                end
         | 
| 36 35 |  | 
| 36 | 
            +
                def self.eigenclass_of(target)
         | 
| 37 | 
            +
                  class << target; self; end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def self.eigenclass?(target)
         | 
| 41 | 
            +
                  target <= eigenclass_of(Object)
         | 
| 42 | 
            +
                end
         | 
| 37 43 | 
             
              end
         | 
| 38 44 | 
             
            end
         |