lawyer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e99be433808f11cb424f36b57b3ac474f2cd810e
4
+ data.tar.gz: 9eeebb6c71f45d45e054aa3a66a310409de86587
5
+ SHA512:
6
+ metadata.gz: 6c2f4dd3603a8dd235adfd3efb6c7ad08a075f2410cb7b835e7c9b78a876e0194ed00c9bb2c6c9359b64d3fbc1764d982bc1e962c081a852d08452b93630f4df
7
+ data.tar.gz: 681467aa0759581e4410d1524ce7c67e6c0fc1148cb736417d057478d2b28910dbc7a50c850e6caa9bbf8efcfb881267724361aabf23369286494a660d5d107f
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --require "spec_helper"
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lawyer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 John Cinnamond
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # Lawyer
2
+
3
+ Strong Duck Typing for Ruby.
4
+
5
+ Lawyer allows you to create contracts that specify how an object behaves.
6
+
7
+ require 'lawyer'
8
+
9
+ class Pingable < Lawyer::Contract
10
+ confirm :ping
11
+ end
12
+
13
+ You can then ensure that your class implements the contract:
14
+
15
+ require 'pingable'
16
+
17
+ class Foo
18
+ def ping
19
+ puts "ping"
20
+ end
21
+
22
+ def pong
23
+ puts "pong"
24
+ end
25
+ end
26
+
27
+ Foo.implements(Pingable)
28
+
29
+ ...but this works best when you write loosely coupled objects and then define the
30
+ interfaces between them. You can then write specs to check that a class implements
31
+ a particular contract:
32
+
33
+ require 'foo'
34
+ require 'pingable'
35
+
36
+ describe Foo do
37
+ it { should implement(Pingable) }
38
+ end
39
+
40
+ ...and use mocks to test methods that expect to receive objects that conform to
41
+ a particular contract:
42
+
43
+ describe Pinger do
44
+ let(:pingable) { contract_double(Pingable) }
45
+ subject(:pinger) { Pinger.new(pingable) }
46
+
47
+ it "pings the pingable" do
48
+ subject.run
49
+ expect(:pingable).to have_received(:ping)
50
+ end
51
+
52
+ it "can't call methods that aren't part of the contract" do
53
+ expect { pingable.pong }.to raise_error(NoMethodError)
54
+ end
55
+ end
56
+
57
+ ...all based off a single definition of the contract.
58
+
59
+ This helps you to write loosely coupled code that relies on well defined interfaces.
60
+ By declaring the contract up front and then using that in your tests you can ensure
61
+ that any mismatches between the expected interface and the actual impelmentations are
62
+ caught as you modify your codebase.
63
+
64
+ ## Installation
65
+
66
+ Add this line to your application's Gemfile:
67
+
68
+ gem 'lawyer'
69
+
70
+ And then execute:
71
+
72
+ $ bundle
73
+
74
+ Or install it yourself as:
75
+
76
+ $ gem install laywer
77
+
78
+ ## Usage
79
+
80
+ First up, create a contract that specifies the methods available in an interface:
81
+
82
+ require 'lawyer'
83
+
84
+ module Contracts
85
+ class Person < Lawyer::Contract
86
+ check :name # check that the method exists
87
+ check :name= => 1 # check the method arity
88
+ check :rename => [:firstname, :lastname] # check required named parameters (ruby 2.1 only)
89
+ end
90
+ end
91
+
92
+
93
+ Add Laywer to your spec_helper:
94
+
95
+ require 'lawyer/rspec'
96
+
97
+ RSpec.configure do |config|
98
+ config.include Lawyer::RSpec::Matchers
99
+ config.include Lawyer::RSpec::ContractDouble
100
+
101
+ # ...
102
+ end
103
+
104
+ Test an implementation:
105
+
106
+ require 'contracts/person'
107
+ require 'person_record'
108
+
109
+ describe PersonRecord do
110
+ it { should implement(Contracts::Person) }
111
+
112
+ # test the implementation
113
+ end
114
+
115
+ And test a receiver:
116
+
117
+ require 'contracts/person'
118
+ require 'namer'
119
+
120
+ describe Namer do
121
+ let(:person) { contract_double(Contracts::Person) }
122
+ subject(:namer) { Namer.new(person) }
123
+
124
+ it "sets the name" do
125
+ expect(person).to receive(:name=).with("John Smith")
126
+ namer.set_name("John Smith")
127
+ end
128
+ end
129
+
130
+ ## Credits
131
+
132
+ Many thanks to Jakub Oboza (http://lambdacu.be) for suggesting an original
133
+ implementation of this idea and for discussing the motivations behind it.
134
+
135
+ ## Contributing
136
+
137
+ 1. Fork it ( http://github.com/jcinnamond/lawyer/fork )
138
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
139
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
140
+ 4. Push to the branch (`git push origin my-new-feature`)
141
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ # If you want to make this the default task
8
+ task :default => :spec
data/lawyer.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lawyer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lawyer"
8
+ spec.version = Lawyer::VERSION
9
+ spec.authors = ["John Cinnamond"]
10
+ spec.email = ["jc@panagile.com"]
11
+ spec.summary = %q{Strong Duck Typing for Ruby}
12
+ spec.homepage = "http://github.com/jcinnamond/lawyer"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(spec)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec", "~> 2.14.1"
23
+ end
@@ -0,0 +1,38 @@
1
+ module Lawyer
2
+ class BrokenContract < StandardError
3
+ def initialize(subject, contract, violations)
4
+ @subject = subject.name
5
+ @contract = contract.name
6
+ @method_missing_violations = violations.select { |v| v.is_a?(MethodMissingViolation) }
7
+ @wrong_arity_violations = violations.select { |v| v.is_a?(WrongArityViolation) }
8
+ @wrong_signature_violations = violations.select { |v| v.is_a?(WrongSignatureViolation) }
9
+ end
10
+
11
+ def to_s
12
+ str = "#{@subject} does not implement <#{@contract}>\n"
13
+ str << explain_violations(@method_missing_violations, "missing")
14
+ str << "\n" if @method_missing_violations &&
15
+ (@wrong_arity_violations || @wrong_signature_violations)
16
+ str << explain_violations(@wrong_arity_violations, "with the wrong arity")
17
+ str << "\n" if (@method_missing_violations || @wrong_arity_violations) &&
18
+ @wrong_signature_violations
19
+ str << explain_violations(@wrong_signature_violations, "with the wrong signature")
20
+ str
21
+ end
22
+
23
+ def explain_violations(violations, type)
24
+ str = ""
25
+ if violations.any?
26
+ count = violations.count
27
+ str << "\t(#{count} #{methods(count)} #{type})\n"
28
+ str << violations.map(&:to_s).join("\n")
29
+ str << "\n"
30
+ end
31
+ str
32
+ end
33
+
34
+ def methods(count)
35
+ count == 1 ? "method" : "methods"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,72 @@
1
+ require 'lawyer/method_missing_violation'
2
+ require 'lawyer/wrong_arity_violation'
3
+ require 'lawyer/wrong_signature_violation'
4
+
5
+ module Lawyer
6
+ class Clause
7
+ attr_reader :name
8
+
9
+ def initialize(params)
10
+ @arity = nil
11
+ @signature = nil
12
+
13
+ if params.is_a?(Hash)
14
+ (@name, details) = params.first
15
+ case details
16
+ when Fixnum
17
+ @arity = details
18
+ when Array
19
+ @signature = details
20
+ end
21
+ else
22
+ @name = params.to_sym
23
+ end
24
+ end
25
+
26
+ def check(subject)
27
+ if missing_method?(subject)
28
+ Lawyer::MethodMissingViolation.new(@name)
29
+ elsif wrong_arity?(subject)
30
+ Lawyer::WrongArityViolation.new(@name, expected: @arity, actual: actual_arity(subject))
31
+ elsif wrong_signature?(subject)
32
+ Lawyer::WrongSignatureViolation.new(@name,
33
+ missing: missing_parameters(subject),
34
+ extra: extra_parameters(subject))
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ def missing_method?(subject)
41
+ !subject.instance_methods.include?(@name)
42
+ end
43
+
44
+ def wrong_arity?(subject)
45
+ @arity && @arity != actual_arity(subject)
46
+ end
47
+
48
+ def actual_arity(subject)
49
+ method_from(subject).arity
50
+ end
51
+
52
+ def wrong_signature?(subject)
53
+ @signature && (missing_parameters(subject).any? || extra_parameters(subject).any?)
54
+ end
55
+
56
+ def missing_parameters(subject)
57
+ @signature - actual_signature(subject)
58
+ end
59
+
60
+ def extra_parameters(subject)
61
+ actual_signature(subject) - @signature
62
+ end
63
+
64
+ def actual_signature(subject)
65
+ method_from(subject).parameters.select { |p| p[0] == :keyreq }.map(&:last) || []
66
+ end
67
+
68
+ def method_from(subject)
69
+ subject.instance_method(@name)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,28 @@
1
+ require 'lawyer/clause'
2
+
3
+ module Lawyer
4
+ class Contract
5
+ class << self
6
+ def clauses
7
+ @clauses ||= []
8
+ end
9
+
10
+ def confirm(clause)
11
+ self.clauses << Lawyer::Clause.new(clause)
12
+ end
13
+
14
+ def check!(subject)
15
+ klass = subject.is_a?(Class) ? subject : subject.class
16
+
17
+ violations = self.clauses.map do |clause|
18
+ clause.check(klass)
19
+ end
20
+ violations.compact!
21
+
22
+ if violations.any?
23
+ raise Lawyer::BrokenContract.new(klass, self, violations)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module Lawyer
2
+ class MethodMissingViolation
3
+ def initialize(name)
4
+ @name = name
5
+ end
6
+
7
+ def to_s
8
+ "\t[missing] #{@name}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Lawyer
2
+ module RSpec
3
+ module ContractDouble
4
+ def contract_double(contract)
5
+ methods = {}
6
+ contract.clauses.each do |clause|
7
+ methods[clause.name] = true
8
+ end
9
+ double(contract.name, methods)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'rspec/expectations'
2
+
3
+ module Lawyer
4
+ module RSpec
5
+ module Matchers
6
+ extend ::RSpec::Matchers::DSL
7
+
8
+ matcher :implement do |contract|
9
+ match do |implementation|
10
+ begin
11
+ contract.check!(implementation)
12
+ true
13
+ rescue Lawyer::BrokenContract => e
14
+ @exception = e
15
+ false
16
+ end
17
+ end
18
+
19
+ failure_message_for_should do |actual|
20
+ @expectation.to_s
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'lawyer/rspec/matchers'
2
+ require 'lawyer/rspec/contract_double'
@@ -0,0 +1,3 @@
1
+ module Lawyer
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,13 @@
1
+ module Lawyer
2
+ class WrongArityViolation
3
+ def initialize(name, expected:, actual:)
4
+ @name = name
5
+ @expected = expected
6
+ @actual = actual
7
+ end
8
+
9
+ def to_s
10
+ "\t[wrong arity] #{@name} (takes #{@actual}, requires #{@expected})"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Lawyer
2
+ class WrongSignatureViolation
3
+ def initialize(name, missing:, extra:)
4
+ @name = name
5
+ @missing = missing
6
+ @extra = extra
7
+ end
8
+
9
+ def to_s
10
+ details = []
11
+ details << "missing #{@missing}" if @missing.any?
12
+ details << "extra #{@extra}" if @extra.any?
13
+ "\t[wrong signature] #{@name} (#{details.join(', ')})"
14
+ end
15
+ end
16
+ end
data/lib/lawyer.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'lawyer/version'
2
+ require 'lawyer/contract'
3
+ require 'lawyer/broken_contract'
4
+
5
+ require 'monkey_patch/module'
6
+
7
+ module Lawyer
8
+ end
@@ -0,0 +1,5 @@
1
+ class Module
2
+ def implements(contract)
3
+ contract.check!(self)
4
+ end
5
+ end
@@ -0,0 +1,73 @@
1
+ class TestContract < Lawyer::Contract
2
+ confirm :ping
3
+ confirm :pong => 2
4
+ confirm :pung => [:name, :size]
5
+ end
6
+
7
+ describe TestContract do
8
+ before :each do
9
+ # Stop modifications to the TestObject from leaking into other specs.
10
+ Object.send(:remove_const, :TestObject) if Object.const_defined?(:TestObject)
11
+ class TestObject;
12
+ def pong(a1); end
13
+ def pung(name:, hats:); end
14
+ end
15
+ end
16
+
17
+ describe "#check!" do
18
+ context "with a compliant object" do
19
+ before :each do
20
+ # Reopen the class and define the required methods
21
+ class TestObject;
22
+ def ping; end
23
+ def pong(a1, a2); end
24
+ def pung(name:, size:); end
25
+ end
26
+ end
27
+
28
+ it "does not raise an exception when checking a class" do
29
+ expect { TestContract.check!(TestObject) }.not_to raise_error
30
+ end
31
+
32
+ it "does not raise an exception when checking an object" do
33
+ expect { TestContract.check!(TestObject.new) }.not_to raise_error
34
+ end
35
+ end
36
+
37
+ context "with a non-compliant object" do
38
+ it "raises a BrokenContract exception when checking a class" do
39
+ expect { TestContract.check!(TestObject) }.to raise_error(Lawyer::BrokenContract)
40
+ end
41
+
42
+ it "raises a BrokenContract exception when checking an object" do
43
+ expect { TestContract.check!(TestObject.new) }.to raise_error(Lawyer::BrokenContract)
44
+ end
45
+
46
+ describe "the exception" do
47
+ before :each do
48
+ begin
49
+ TestContract.check!(TestObject)
50
+ rescue Lawyer::BrokenContract => e
51
+ @exception = e
52
+ end
53
+ end
54
+
55
+ it "includes the offending class and contract" do
56
+ expect(@exception.to_s).to include("TestObject does not implement <TestContract>\n")
57
+ end
58
+
59
+ it "includes the missing method details in the exception" do
60
+ expect(@exception.to_s).to include("\t(1 method missing)\n\t[missing] ping\n")
61
+ end
62
+
63
+ it "includes the incorrect arity details in the exception" do
64
+ expect(@exception.to_s).to include("\t(1 method with the wrong arity)\n\t[wrong arity] pong (takes 1, requires 2)\n")
65
+ end
66
+
67
+ it "includes required argument mismatch details in the exception" do
68
+ expect(@exception.to_s).to include("\t(1 method with the wrong signature)\n\t[wrong signature] pung (missing [:size], extra [:hats])\n")
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,15 @@
1
+ require 'lawyer/rspec'
2
+
3
+ class DoublableContract < Lawyer::Contract
4
+ confirm :ping
5
+ end
6
+
7
+ describe Lawyer::RSpec::ContractDouble do
8
+ include Lawyer::RSpec::ContractDouble
9
+
10
+ subject { contract_double(DoublableContract) }
11
+
12
+ it { should be_a(RSpec::Mocks::Mock) }
13
+ it { should respond_to(:ping) }
14
+ it { should_not respond_to(:pong) }
15
+ end
@@ -0,0 +1,28 @@
1
+ require 'lawyer/rspec'
2
+
3
+ describe Lawyer::RSpec::Matchers do
4
+ include Lawyer::RSpec::Matchers
5
+ let(:subject) { double("Module", name: "Module") }
6
+ let(:contract) { double("contract", check!: true, name: "Contract") }
7
+
8
+ it "defines 'implement'" do
9
+ expect(subject).to implement(contract)
10
+ end
11
+
12
+ it "calls checks the subject against the contract" do
13
+ expect(contract).to receive(:check!).with(subject)
14
+ expect(subject).to implement(contract)
15
+ end
16
+
17
+ context "with violations" do
18
+ let(:broken_contract) { Lawyer::BrokenContract.new(subject, contract, []) }
19
+
20
+ before :each do
21
+ allow(contract).to receive(:check!).and_raise(broken_contract)
22
+ end
23
+
24
+ it "fails the matcher" do
25
+ expect(subject).not_to implement(contract)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ describe Module do
2
+ describe "#implements" do
3
+ it "checks the module against the contract" do
4
+ class A; end
5
+
6
+ contract = double("contract")
7
+ expect(contract).to receive(:check!).with(A)
8
+ A.implements(contract)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require 'lawyer'
2
+
3
+ RSpec.configure do |config|
4
+ config.treat_symbols_as_metadata_keys_with_true_values = true
5
+ config.run_all_when_everything_filtered = true
6
+ config.filter_run :focus
7
+
8
+ config.order = 'random'
9
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lawyer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Cinnamond
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.14.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.14.1
55
+ description:
56
+ email:
57
+ - jc@panagile.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lawyer.gemspec
69
+ - lib/lawyer.rb
70
+ - lib/lawyer/broken_contract.rb
71
+ - lib/lawyer/clause.rb
72
+ - lib/lawyer/contract.rb
73
+ - lib/lawyer/method_missing_violation.rb
74
+ - lib/lawyer/rspec.rb
75
+ - lib/lawyer/rspec/contract_double.rb
76
+ - lib/lawyer/rspec/matchers.rb
77
+ - lib/lawyer/version.rb
78
+ - lib/lawyer/wrong_arity_violation.rb
79
+ - lib/lawyer/wrong_signature_violation.rb
80
+ - lib/monkey_patch/module.rb
81
+ - spec/lib/lawyer/contract_spec.rb
82
+ - spec/lib/lawyer/rspec/contract_double_spec.rb
83
+ - spec/lib/lawyer/rspec/matchers_spec.rb
84
+ - spec/module_spec.rb
85
+ - spec/spec_helper.rb
86
+ homepage: http://github.com/jcinnamond/lawyer
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.2.0
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Strong Duck Typing for Ruby
110
+ test_files:
111
+ - spec/lib/lawyer/contract_spec.rb
112
+ - spec/lib/lawyer/rspec/contract_double_spec.rb
113
+ - spec/lib/lawyer/rspec/matchers_spec.rb
114
+ - spec/module_spec.rb
115
+ - spec/spec_helper.rb