pact 0.1.28
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.
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +83 -0
- data/LICENSE.txt +22 -0
- data/README.md +238 -0
- data/Rakefile +33 -0
- data/bin/pact +4 -0
- data/lib/pact/app.rb +32 -0
- data/lib/pact/configuration.rb +54 -0
- data/lib/pact/consumer/app_manager.rb +177 -0
- data/lib/pact/consumer/configuration_dsl.rb +71 -0
- data/lib/pact/consumer/consumer_contract_builder.rb +79 -0
- data/lib/pact/consumer/consumer_contract_builders.rb +10 -0
- data/lib/pact/consumer/dsl.rb +98 -0
- data/lib/pact/consumer/interaction.rb +70 -0
- data/lib/pact/consumer/mock_service.rb +340 -0
- data/lib/pact/consumer/rspec.rb +43 -0
- data/lib/pact/consumer/run_condor.rb +4 -0
- data/lib/pact/consumer/run_mock_contract_service.rb +13 -0
- data/lib/pact/consumer/service_consumer.rb +22 -0
- data/lib/pact/consumer/service_producer.rb +23 -0
- data/lib/pact/consumer.rb +7 -0
- data/lib/pact/consumer_contract.rb +110 -0
- data/lib/pact/json_warning.rb +23 -0
- data/lib/pact/logging.rb +14 -0
- data/lib/pact/matchers/matchers.rb +85 -0
- data/lib/pact/matchers.rb +1 -0
- data/lib/pact/producer/configuration_dsl.rb +62 -0
- data/lib/pact/producer/matchers.rb +22 -0
- data/lib/pact/producer/pact_spec_runner.rb +57 -0
- data/lib/pact/producer/producer_state.rb +81 -0
- data/lib/pact/producer/rspec.rb +127 -0
- data/lib/pact/producer/test_methods.rb +89 -0
- data/lib/pact/producer.rb +1 -0
- data/lib/pact/rake_task.rb +64 -0
- data/lib/pact/reification.rb +26 -0
- data/lib/pact/request.rb +109 -0
- data/lib/pact/term.rb +40 -0
- data/lib/pact/verification_task.rb +57 -0
- data/lib/pact/version.rb +3 -0
- data/lib/pact.rb +5 -0
- data/lib/tasks/pact.rake +6 -0
- data/pact.gemspec +36 -0
- data/scratchpad.txt +36 -0
- data/spec/features/consumption_spec.rb +146 -0
- data/spec/features/producer_states/zebras.rb +28 -0
- data/spec/features/production_spec.rb +160 -0
- data/spec/integration/pact/configuration_spec.rb +65 -0
- data/spec/lib/pact/configuration_spec.rb +35 -0
- data/spec/lib/pact/consumer/app_manager_spec.rb +41 -0
- data/spec/lib/pact/consumer/consumer_contract_builder_spec.rb +87 -0
- data/spec/lib/pact/consumer/dsl_spec.rb +52 -0
- data/spec/lib/pact/consumer/interaction_spec.rb +108 -0
- data/spec/lib/pact/consumer/mock_service_spec.rb +147 -0
- data/spec/lib/pact/consumer/service_consumer_spec.rb +11 -0
- data/spec/lib/pact/consumer_contract_spec.rb +125 -0
- data/spec/lib/pact/matchers/matchers_spec.rb +354 -0
- data/spec/lib/pact/producer/configuration_dsl_spec.rb +101 -0
- data/spec/lib/pact/producer/producer_state_spec.rb +103 -0
- data/spec/lib/pact/producer/rspec_spec.rb +48 -0
- data/spec/lib/pact/reification_spec.rb +43 -0
- data/spec/lib/pact/request_spec.rb +316 -0
- data/spec/lib/pact/term_spec.rb +36 -0
- data/spec/lib/pact/verification_task_spec.rb +64 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/a_consumer-a_producer.json +34 -0
- data/spec/support/pact_rake_support.rb +41 -0
- data/spec/support/test_app_fail.json +22 -0
- data/spec/support/test_app_pass.json +21 -0
- data/tasks/pact-test.rake +19 -0
- metadata +381 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative '../configuration'
|
2
|
+
require_relative '../consumer'
|
3
|
+
require_relative 'dsl'
|
4
|
+
require_relative 'configuration_dsl'
|
5
|
+
|
6
|
+
module Pact
|
7
|
+
module Consumer
|
8
|
+
module RSpec
|
9
|
+
include Pact::Consumer::ConsumerContractBuilders
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.include Pact::Consumer::RSpec, :pact => true
|
16
|
+
|
17
|
+
config.before :all, :pact => true do
|
18
|
+
Pact::Consumer::AppManager.instance.spawn_all
|
19
|
+
FileUtils.mkdir_p Pact.configuration.pact_dir
|
20
|
+
end
|
21
|
+
|
22
|
+
config.before :each, :pact => true do | example |
|
23
|
+
example_description = "#{example.example.example_group.description} #{example.example.description}"
|
24
|
+
Pact.configuration.logger.info "Clearing all expectations"
|
25
|
+
Pact::Consumer::AppManager.instance.ports_of_mock_services.each do | port |
|
26
|
+
Net::HTTP.new("localhost", port).delete("/interactions?example_description=#{URI.encode(example_description)}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
config.after :each, :pact => true do | example |
|
31
|
+
example_description = "#{example.example.example_group.description} #{example.example.description}"
|
32
|
+
Pact.configuration.logger.info "Verifying interactions for #{example_description}"
|
33
|
+
Pact.configuration.producer_verifications.each do | producer_verification |
|
34
|
+
producer_verification.call example_description
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
config.after :suite do
|
39
|
+
Pact.configuration.logger.info "After suite"
|
40
|
+
Pact::Consumer::AppManager.instance.kill_all
|
41
|
+
Pact::Consumer::AppManager.instance.clear_all
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require_relative 'spawn_app'
|
3
|
+
require_relative 'mock_service'
|
4
|
+
|
5
|
+
spawn_app MockService.new, 1234
|
6
|
+
|
7
|
+
RSpec.configure do |c|
|
8
|
+
c.before(:each, :type => :feature) do
|
9
|
+
http = Net::HTTP.new('localhost', 1234)
|
10
|
+
request = Net::HTTP::Delete.new('/interactions')
|
11
|
+
response = http.request(request)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Pact
|
2
|
+
module Consumer
|
3
|
+
class ServiceConsumer
|
4
|
+
attr_accessor :name
|
5
|
+
def initialize options
|
6
|
+
@name = options[:name]
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
name
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_json options = {}
|
14
|
+
{name: name}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_hash obj
|
18
|
+
ServiceConsumer.new(:name => obj['name'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pact
|
2
|
+
module Consumer
|
3
|
+
# This is a crap name, it's really just a object for serializing to JSON
|
4
|
+
class ServiceProducer
|
5
|
+
attr_accessor :name
|
6
|
+
def initialize options
|
7
|
+
@name = options[:name] || '[producer name unknown - please update the pact gem in the consumer project to the latest version and regenerate the pacts]'
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
name
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json options = {}
|
15
|
+
{name: name}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_hash obj
|
19
|
+
ServiceProducer.new(:name => obj['name'])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require_relative 'consumer/interaction'
|
2
|
+
require_relative 'consumer/consumer_contract_builder'
|
3
|
+
require_relative 'consumer/mock_service'
|
4
|
+
require_relative 'consumer/app_manager'
|
5
|
+
require_relative 'term'
|
6
|
+
require_relative 'request'
|
7
|
+
require_relative 'consumer_contract'
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'pact/consumer/service_consumer'
|
2
|
+
require 'pact/consumer/service_producer'
|
3
|
+
require 'pact/consumer/interaction'
|
4
|
+
require 'pact/logging'
|
5
|
+
require 'pact/json_warning'
|
6
|
+
require 'date'
|
7
|
+
|
8
|
+
module Pact
|
9
|
+
class ConsumerContract
|
10
|
+
|
11
|
+
include Pact::Logging
|
12
|
+
include Pact::JsonWarning
|
13
|
+
|
14
|
+
attr_accessor :interactions
|
15
|
+
attr_accessor :consumer
|
16
|
+
attr_accessor :producer
|
17
|
+
|
18
|
+
def initialize(attributes = {})
|
19
|
+
@interactions = attributes[:interactions] || []
|
20
|
+
@consumer = attributes[:consumer]
|
21
|
+
@producer = attributes[:producer]
|
22
|
+
end
|
23
|
+
|
24
|
+
def as_json(options = {})
|
25
|
+
{
|
26
|
+
producer: @producer.as_json,
|
27
|
+
consumer: @consumer.as_json,
|
28
|
+
interactions: @interactions.collect(&:as_json),
|
29
|
+
metadata: {
|
30
|
+
pact_gem: {
|
31
|
+
version: Pact::VERSION
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_json(options = {})
|
38
|
+
as_json(options).to_json(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_hash(obj)
|
42
|
+
new({
|
43
|
+
:interactions => obj['interactions'].collect { |hash| Pact::Consumer::Interaction.from_hash(hash)},
|
44
|
+
:consumer => Pact::Consumer::ServiceConsumer.from_hash(obj['consumer']),
|
45
|
+
:producer => Pact::Consumer::ServiceProducer.from_hash(obj['producer'] || {})
|
46
|
+
})
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.from_json string
|
50
|
+
deserialised_object = JSON.load(string)
|
51
|
+
from_hash(deserialised_object)
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_interaction criteria
|
55
|
+
interactions = find_interactions criteria
|
56
|
+
if interactions.size == 0
|
57
|
+
raise "Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{producer.name}."
|
58
|
+
elsif interactions.size > 1
|
59
|
+
raise "Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{producer.name}."
|
60
|
+
end
|
61
|
+
interactions.first
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_interactions criteria
|
65
|
+
interactions.select{ | interaction| match_criteria? interaction, criteria}
|
66
|
+
end
|
67
|
+
|
68
|
+
def each
|
69
|
+
interactions.each do | interaction |
|
70
|
+
yield interaction
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def match_criteria? interaction, criteria
|
75
|
+
criteria.each do | key, value |
|
76
|
+
unless match_criterion interaction[key.to_s], value
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def match_criterion target, criterion
|
84
|
+
target == criterion || (criterion.is_a?(Regexp) && criterion.match(target))
|
85
|
+
end
|
86
|
+
|
87
|
+
def pact_file_name
|
88
|
+
"#{filenamify(consumer.name)}-#{filenamify(producer.name)}.json"
|
89
|
+
end
|
90
|
+
|
91
|
+
def pactfile_path
|
92
|
+
raise 'You must first specify a consumer and service name' unless (consumer && consumer.name && producer && producer.name)
|
93
|
+
@pactfile_path ||= File.join(Pact.configuration.pact_dir, pact_file_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_pactfile
|
97
|
+
logger.debug "Updating pact file for #{producer.name} at #{pactfile_path}"
|
98
|
+
check_for_active_support_json
|
99
|
+
File.open(pactfile_path, 'w') do |f|
|
100
|
+
f.write JSON.pretty_generate(self)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def filenamify name
|
107
|
+
name.downcase.gsub(/\s/, '_')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'json/add/regexp'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module JsonWarning
|
6
|
+
def check_for_active_support_json
|
7
|
+
# Active support clobbers the as_json methods defined in the json/add directory of the json gem.
|
8
|
+
# These methods are required to serialize and deserialize the Regexp and Symbol classes properly.
|
9
|
+
# You can potentially fix this by making sure the json gem is required AFTER the active_support/json gem
|
10
|
+
# OR if you don't use the json part of activesupport you could only require the parts of active support you really need
|
11
|
+
# OR you can only use strings in your pacts.
|
12
|
+
# Good luck.
|
13
|
+
|
14
|
+
# If someone knows how to make sure the pact gem uses the json gem as_json methods when activesupport/json is used in the calling code,
|
15
|
+
# without breaking the calling code, which may depend on activesupport/json... then please fix this.
|
16
|
+
# Note: we can probably do this in Ruby 2.0 with refinements, but for now, we're all stuck on 1.9 :(
|
17
|
+
|
18
|
+
unless Regexp.new('').as_json.is_a?(Hash)
|
19
|
+
Logger.new($stderr).warn("It appears you are using ActiveSupport json in your project. You are now in rubygems hell. Please see Pact::JsonWarning for more info.")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/pact/logging.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
require 'pact/term'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module Matchers
|
6
|
+
|
7
|
+
NO_DIFF_INDICATOR = 'no difference here!'
|
8
|
+
|
9
|
+
def diff expected, actual, options = {}
|
10
|
+
case expected
|
11
|
+
when Hash then hash_diff(expected, actual, options)
|
12
|
+
when Array then array_diff(expected, actual, options)
|
13
|
+
when Pact::Term then diff(expected.matcher, actual, options)
|
14
|
+
when Regexp then regexp_diff(expected, actual, options)
|
15
|
+
else object_diff(expected, actual, options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def structure_diff expected, actual
|
20
|
+
diff expected, actual, {structure: true}
|
21
|
+
end
|
22
|
+
|
23
|
+
def regexp_diff regexp, actual, options
|
24
|
+
if actual != nil && regexp.match(actual)
|
25
|
+
{}
|
26
|
+
else
|
27
|
+
{expected: regexp, actual: actual}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def array_diff expected, actual, options
|
32
|
+
if actual.is_a? Array
|
33
|
+
if expected.length == actual.length
|
34
|
+
difference = []
|
35
|
+
diff_found = false
|
36
|
+
expected.each_with_index do | item, index|
|
37
|
+
if (item_diff = diff(item, actual[index], options)).any?
|
38
|
+
diff_found = true
|
39
|
+
difference << item_diff
|
40
|
+
else
|
41
|
+
difference << NO_DIFF_INDICATOR
|
42
|
+
end
|
43
|
+
end
|
44
|
+
diff_found ? difference : {}
|
45
|
+
else
|
46
|
+
{expected: expected, actual: actual}
|
47
|
+
end
|
48
|
+
else
|
49
|
+
{expected: expected, actual: actual}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def hash_diff expected, actual, options
|
54
|
+
if actual.is_a? Hash
|
55
|
+
expected.keys.inject({}) do |diff, key|
|
56
|
+
if (diff_at_key = diff(expected[key], actual[key], options)).any?
|
57
|
+
diff[key] = diff_at_key
|
58
|
+
end
|
59
|
+
diff
|
60
|
+
end
|
61
|
+
else
|
62
|
+
{expected: expected, actual: actual}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def class_diff expected, actual
|
67
|
+
if expected.class != actual.class
|
68
|
+
actual_display = actual.nil? ? nil : {:class => actual.class, :value => actual }
|
69
|
+
expected_display = expected.nil? ? nil : {:class => expected.class, eg: expected}
|
70
|
+
{:expected => expected_display, :actual => actual_display}
|
71
|
+
else
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def object_diff expected, actual, options
|
77
|
+
return class_diff(expected, actual) if options[:structure]
|
78
|
+
if expected != actual
|
79
|
+
{:expected => expected, :actual => actual}
|
80
|
+
else
|
81
|
+
{}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'matchers/matchers'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module Producer
|
6
|
+
module ConfigurationDSL
|
7
|
+
|
8
|
+
def producer &block
|
9
|
+
@producer ||= nil
|
10
|
+
if block_given?
|
11
|
+
@producer = ProducerDSL.new(&block).create_producer_config
|
12
|
+
elsif @producer
|
13
|
+
@producer
|
14
|
+
else
|
15
|
+
raise "Please configure your producer. See the Producer section in the README for examples."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ProducerConfig
|
20
|
+
attr_accessor :name
|
21
|
+
|
22
|
+
def initialize name, &app_block
|
23
|
+
@name = name
|
24
|
+
@app_block = app_block
|
25
|
+
end
|
26
|
+
|
27
|
+
def app
|
28
|
+
@app_block.call
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ProducerDSL
|
33
|
+
|
34
|
+
def initialize &block
|
35
|
+
@app = nil
|
36
|
+
@name = nil
|
37
|
+
instance_eval(&block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate
|
41
|
+
raise "Please provide a name for the Producer" unless @name
|
42
|
+
raise "Please configure an app for the Producer" unless @app_block
|
43
|
+
end
|
44
|
+
|
45
|
+
def name name
|
46
|
+
@name = name
|
47
|
+
end
|
48
|
+
|
49
|
+
def app &block
|
50
|
+
@app_block = block
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_producer_config
|
54
|
+
validate
|
55
|
+
ProducerConfig.new(@name, &@app_block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Pact::Configuration.send(:include, Pact::Producer::ConfigurationDSL)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'pact/term'
|
2
|
+
require 'awesome_print'
|
3
|
+
require 'pact/matchers'
|
4
|
+
require 'awesome_print'
|
5
|
+
|
6
|
+
RSpec::Matchers.define :match_term do |expected|
|
7
|
+
include Pact::Matchers
|
8
|
+
|
9
|
+
match do |actual|
|
10
|
+
if (difference = diff(expected, actual)).any?
|
11
|
+
@message = difference
|
12
|
+
false
|
13
|
+
else
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
failure_message_for_should do | actual |
|
19
|
+
@message.ai
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'rspec'
|
3
|
+
require 'rspec/core'
|
4
|
+
require 'rspec/core/formatters/documentation_formatter'
|
5
|
+
require_relative 'rspec'
|
6
|
+
|
7
|
+
module Pact
|
8
|
+
module Producer
|
9
|
+
class PactSpecRunner
|
10
|
+
|
11
|
+
extend Pact::Producer::RSpec::ClassMethods
|
12
|
+
|
13
|
+
def self.run(spec_definitions, options = {})
|
14
|
+
initialize_specs spec_definitions
|
15
|
+
configure_rspec options
|
16
|
+
run_specs
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def self.initialize_specs spec_definitions
|
22
|
+
spec_definitions.each do | spec_definition |
|
23
|
+
require spec_definition[:support_file] if spec_definition[:support_file]
|
24
|
+
options = {consumer: spec_definition[:consumer], save_pactfile_to_tmp: true}
|
25
|
+
honour_pactfile spec_definition[:uri], options
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configure_rspec options
|
30
|
+
config = ::RSpec.configuration
|
31
|
+
config.color = true
|
32
|
+
|
33
|
+
unless options[:silent]
|
34
|
+
config.error_stream = $stderr
|
35
|
+
config.output_stream = $stdout
|
36
|
+
end
|
37
|
+
|
38
|
+
formatter = ::RSpec::Core::Formatters::DocumentationFormatter.new(config.output)
|
39
|
+
reporter = ::RSpec::Core::Reporter.new(formatter)
|
40
|
+
config.instance_variable_set(:@reporter, reporter)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.run_specs
|
44
|
+
config = ::RSpec.configuration
|
45
|
+
world = ::RSpec::world
|
46
|
+
config.reporter.report(world.example_count, nil) do |reporter|
|
47
|
+
begin
|
48
|
+
config.run_hook(:before, :suite)
|
49
|
+
world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : config.failure_exit_code
|
50
|
+
ensure
|
51
|
+
config.run_hook(:after, :suite)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Pact
|
2
|
+
module Producer
|
3
|
+
|
4
|
+
module DSL
|
5
|
+
def producer_state name, &block
|
6
|
+
ProducerState.producer_state(name, &block).register
|
7
|
+
end
|
8
|
+
|
9
|
+
def with_consumer name, &block
|
10
|
+
ProducerState.current_namespaces << name
|
11
|
+
instance_eval(&block)
|
12
|
+
ProducerState.current_namespaces.pop
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ProducerState
|
17
|
+
|
18
|
+
attr_accessor :name
|
19
|
+
attr_accessor :namespace
|
20
|
+
|
21
|
+
def self.producer_state name, &block
|
22
|
+
ProducerState.new(name, current_namespaces.join('.'), &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.register name, producer_state
|
26
|
+
producer_states[name] = producer_state
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.producer_states
|
30
|
+
@@producer_states ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.current_namespaces
|
34
|
+
@@current_namespaces ||= []
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.get name, options = {}
|
38
|
+
fullname = options[:for] ? "#{options[:for]}.#{name}" : name
|
39
|
+
(producer_states[fullname] || producer_states[fullname.to_sym]) || producer_states[name]
|
40
|
+
end
|
41
|
+
|
42
|
+
def register
|
43
|
+
self.class.register(namespaced(name), self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize name, namespace, &block
|
47
|
+
@name = name
|
48
|
+
@namespace = namespace
|
49
|
+
instance_eval(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_up &block
|
53
|
+
if block_given?
|
54
|
+
@set_up_block = block
|
55
|
+
elsif @set_up_block
|
56
|
+
instance_eval &@set_up_block
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def tear_down &block
|
61
|
+
if block_given?
|
62
|
+
@tear_down_block = block
|
63
|
+
elsif @tear_down_block
|
64
|
+
instance_eval &@tear_down_block
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def namespaced(name)
|
71
|
+
if namespace.empty?
|
72
|
+
name
|
73
|
+
else
|
74
|
+
"#{namespace}.#{name}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Pact.send(:extend, Pact::Producer::DSL)
|