shokkenki-provider 0.0.2

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.
@@ -0,0 +1,17 @@
1
+ require_relative '../model/rack_http_client'
2
+
3
+ module Shokkenki
4
+ module Provider
5
+ module Configuration
6
+ module Provider
7
+ def run app
8
+ self.http_client = Shokkenki::Provider::Model::RackHttpClient.new app
9
+ end
10
+
11
+ def given match, &block
12
+ add_fixture match, block
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../model/provider'
2
+
3
+ module Shokkenki
4
+ module Provider
5
+ module Configuration
6
+ module Session
7
+
8
+ def configure &block
9
+ instance_eval &block
10
+ end
11
+
12
+ def tickets location
13
+ self.ticket_location = location
14
+ end
15
+
16
+ def provider name, &block
17
+ provider = providers[name] || Shokkenki::Provider::Model::Provider.new(name)
18
+ provider.instance_eval &block if block
19
+ add_provider provider
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module Model
4
+ class Fixture
5
+
6
+ def initialize name_pattern, establisher
7
+ @name_pattern = name_pattern
8
+ @establisher = establisher
9
+ validate!
10
+ end
11
+
12
+ def establish required_fixture
13
+ match = @name_pattern.match required_fixture.name
14
+ if match
15
+ arguments = {
16
+ :arguments => required_fixture.arguments,
17
+ :match => match
18
+ }
19
+ @establisher.call arguments
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def validate!
26
+ message = "Fixture establisher for name pattern /#{@name_pattern}/ must only accept zero or one argument."
27
+ raise message unless @establisher.arity <= 1
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
3
+ module Shokkenki
4
+ module Provider
5
+ module Model
6
+ class FixtureRequirement
7
+
8
+ attr_reader :name, :arguments
9
+
10
+ def initialize name, arguments
11
+ @name = name
12
+ @arguments = arguments
13
+ end
14
+
15
+ def self.from_hash hash
16
+ symbolized_hash = hash.deep_symbolize_keys
17
+ new symbolized_hash[:name], symbolized_hash[:arguments]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ require 'shokkenki/term/term_factory'
2
+ require_relative 'fixture_requirement'
3
+
4
+ module Shokkenki
5
+ module Provider
6
+ module Model
7
+ class Interaction
8
+ attr_reader :label, :response, :request, :required_fixtures
9
+
10
+ def initialize label, request, response, required_fixtures
11
+ @label = label
12
+ @request = request
13
+ @response = response
14
+ @required_fixtures = required_fixtures
15
+ end
16
+
17
+ def self.from_hash hash
18
+ new(
19
+ hash[:label],
20
+ Shokkenki::Term::TermFactory.from_json(hash[:request]),
21
+ Shokkenki::Term::TermFactory.from_json(hash[:response]),
22
+ hash[:fixtures].map {|f| FixtureRequirement.from_hash f }
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ require_relative '../configuration/provider'
2
+ require_relative 'fixture'
3
+
4
+ module Shokkenki
5
+ module Provider
6
+ module Model
7
+ class Provider
8
+ include Shokkenki::Provider::Configuration::Provider
9
+ attr_reader :name, :fixtures
10
+ attr_accessor :http_client
11
+
12
+ def initialize name
13
+ @name = name
14
+ @fixtures = []
15
+ end
16
+
17
+ def add_fixture name_pattern, fixture
18
+ @fixtures << Fixture.new(name_pattern, fixture)
19
+ end
20
+
21
+ def establish required_fixtures
22
+ required_fixtures.each do |required_fixture|
23
+ @fixtures.each { |fixture| fixture.establish required_fixture }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ require 'rack/test'
2
+ require 'faraday'
3
+
4
+ module Shokkenki
5
+ module Provider
6
+ module Model
7
+ class RackHttpClient
8
+
9
+ def initialize app
10
+ @connection = Faraday.new do |f|
11
+ f.adapter :rack, app
12
+ end
13
+ end
14
+
15
+ def response_for request_term
16
+ request = request_term.example
17
+ response = @connection.send(request.delete(:method).to_sym) do |r|
18
+ request.each do |key, value|
19
+ writer = (key == :query) ? :params= : "#{key}=".to_sym
20
+ r.send writer, value
21
+ end
22
+ end
23
+
24
+ as_shokkenki_response response
25
+ end
26
+
27
+ private
28
+
29
+ def as_shokkenki_response response
30
+ {
31
+ :status => response.status,
32
+ :headers => response.headers,
33
+ :body => response.body
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module Model
4
+ class Role
5
+
6
+ attr_reader :name, :label
7
+
8
+ def self.from_hash hash
9
+ new(hash[:name], hash[:label])
10
+ end
11
+
12
+ def initialize name, label
13
+ @name = name
14
+ @label = label
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require_relative 'role'
3
+ require_relative 'interaction'
4
+
5
+ module Shokkenki
6
+ module Provider
7
+ module Model
8
+ class Ticket
9
+
10
+ attr_reader :provider, :interactions, :consumer
11
+
12
+ def initialize provider, consumer, interactions
13
+ @provider = provider
14
+ @consumer = consumer
15
+ @interactions = interactions
16
+ end
17
+
18
+ def self.from_json json
19
+ from_hash(JSON.parse(json).symbolize_keys)
20
+ end
21
+
22
+ def self.from_hash hash
23
+ new(
24
+ Role.from_hash(hash[:provider].symbolize_keys),
25
+ Role.from_hash(hash[:consumer].symbolize_keys),
26
+ hash[:interactions].map{ |h| Interaction.from_hash h.symbolize_keys }
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'session'
2
+
3
+ module Shokkenki
4
+
5
+ def self.provider
6
+ @provider_session ||= Shokkenki::Provider::Session.new
7
+ end
8
+
9
+ end
@@ -0,0 +1,22 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module RSpec
4
+ module Interaction
5
+
6
+ def verify_within context
7
+ interaction = self
8
+ context.describe label do
9
+ before(:all) do
10
+ @provider.establish interaction.required_fixtures
11
+
12
+ @http_response = @provider.http_client.response_for(interaction.request)
13
+ end
14
+ before(:each) { @actual_values = [@http_response] }
15
+ interaction.response.verify_within self
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module RSpec
4
+ module Term
5
+ module HashTerm
6
+ def verify_within context
7
+ value.each do |name, term|
8
+ context.describe name do
9
+ before { @actual_values = @actual_values.map{ |value| value[name] } }
10
+
11
+ term.verify_within self
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ require 'jsonpath'
2
+
3
+ module Shokkenki
4
+ module Provider
5
+ module RSpec
6
+ module Term
7
+ module JsonPathsTerm
8
+ def verify_within context
9
+ term = self
10
+ context.describe 'json value' do
11
+ term.value.each do |json_path, term|
12
+ describe json_path do
13
+
14
+ before(:each) do
15
+ @actual_values = @actual_values.map do |value|
16
+ JsonPath.on(value, json_path)
17
+ end.flatten
18
+ end
19
+
20
+ term.verify_within self
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module RSpec
4
+ module Term
5
+ module NumberTerm
6
+ def verify_within context
7
+ term_value = value
8
+ context.it %Q{is #{term_value}} do
9
+ @actual_values.each { |value| expect(value).to(eq(term_value)) }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module RSpec
4
+ module Term
5
+ module RegexpTerm
6
+ def verify_within context
7
+ term_value = value
8
+ context.it %Q{matches /#{term_value}/} do
9
+ @actual_values.each{ |value| expect(value.to_s).to(match(term_value)) }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module RSpec
4
+ module Term
5
+ module StringTerm
6
+ def verify_within context
7
+ term_value = value
8
+ context.it %Q{is "#{term_value}"} do
9
+ @actual_values.each { |value| expect(value.to_s).to(eq(term_value)) }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ require 'rspec/core/dsl'
2
+
3
+ module Shokkenki
4
+ module Provider
5
+ module RSpec
6
+ module Ticket
7
+ include ::RSpec::Core::DSL
8
+
9
+ def verify_with provider
10
+ ticket = self
11
+ describe consumer.label, :shokkenki_provider => true do
12
+ before(:all) { @provider = provider }
13
+ ticket.interactions.each { |i| i.verify_within self }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'provider'
2
+
3
+ require_relative 'model/ticket'
4
+ require_relative 'model/interaction'
5
+ require 'shokkenki/term/hash_term'
6
+ require 'shokkenki/term/string_term'
7
+ require 'shokkenki/term/number_term'
8
+ require 'shokkenki/term/regexp_term'
9
+ require 'shokkenki/term/json_paths_term'
10
+
11
+ require_relative 'rspec/ticket'
12
+ require_relative 'rspec/interaction'
13
+ require_relative 'rspec/term/hash_term'
14
+ require_relative 'rspec/term/string_term'
15
+ require_relative 'rspec/term/number_term'
16
+ require_relative 'rspec/term/regexp_term'
17
+ require_relative 'rspec/term/json_paths_term'
18
+
19
+ Shokkenki::Provider::Model::Ticket.send :include, Shokkenki::Provider::RSpec::Ticket
20
+ Shokkenki::Provider::Model::Interaction.send :include, Shokkenki::Provider::RSpec::Interaction
21
+
22
+ Shokkenki::Term::HashTerm.send :include, Shokkenki::Provider::RSpec::Term::HashTerm
23
+ Shokkenki::Term::StringTerm.send :include, Shokkenki::Provider::RSpec::Term::StringTerm
24
+ Shokkenki::Term::NumberTerm.send :include, Shokkenki::Provider::RSpec::Term::NumberTerm
25
+ Shokkenki::Term::RegexpTerm.send :include, Shokkenki::Provider::RSpec::Term::RegexpTerm
26
+ Shokkenki::Term::JsonPathsTerm.send :include, Shokkenki::Provider::RSpec::Term::JsonPathsTerm
@@ -0,0 +1,32 @@
1
+ require_relative 'ticket_reader'
2
+ require_relative 'configuration/session'
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+
5
+ module Shokkenki
6
+ module Provider
7
+ class Session
8
+ include Shokkenki::Provider::Configuration::Session
9
+
10
+ attr_accessor :ticket_location
11
+ attr_reader :providers
12
+
13
+ def initialize
14
+ @ticket_reader = TicketReader.new
15
+ @ticket_location = 'tickets'
16
+ @providers = {}.with_indifferent_access
17
+ end
18
+
19
+ def add_provider provider
20
+ @providers[provider.name] = provider
21
+ end
22
+
23
+ def redeem_tickets
24
+ @ticket_reader.read_from(ticket_location).each do |ticket|
25
+ provider = providers[ticket.provider.name]
26
+ raise "No provided named '#{ticket.provider.name}' was found. Did you register one?" unless provider
27
+ ticket.verify_with provider
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'model/ticket'
2
+ require 'uri'
3
+ require 'open-uri'
4
+ require 'json'
5
+
6
+ module Shokkenki
7
+ module Provider
8
+ class TicketReader
9
+
10
+ def read_from location
11
+ location.respond_to?(:call) ? location.call : parse_from(location)
12
+ end
13
+
14
+ private
15
+
16
+ def parse_from resource
17
+ resource.match(URI::regexp) ? parse_from_remote(resource) : parse_from_local(resource)
18
+ end
19
+
20
+ def parse_from_remote resource
21
+ open(resource) do |f|
22
+ JSON.parse(f.read).map { |h| Shokkenki::Provider::Model::Ticket.from_hash h}
23
+ end
24
+ end
25
+
26
+ def parse_from_local resource
27
+ Dir.exists?(resource) ? parse_from_directory(resource) : parse_from_file(resource)
28
+ end
29
+
30
+ def parse_from_directory directory
31
+ Dir.glob("#{directory}/**/*.json").map do |json_file|
32
+ Shokkenki::Provider::Model::Ticket.from_json File.read(json_file)
33
+ end
34
+ end
35
+
36
+ def parse_from_file file
37
+ [Shokkenki::Provider::Model::Ticket.from_json(File.read(file))]
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ module Shokkenki
2
+ module Provider
3
+ module Version
4
+ STRING = '0.0.2'
5
+ end
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shokkenki-provider
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brent Snook
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: faraday
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rack-test
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: jsonpath
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: shokkenki-support
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 0.0.2
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 0.0.2
110
+ - !ruby/object:Gem::Dependency
111
+ name: rake
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 10.0.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: 10.0.0
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 2.14.0
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 2.14.0
142
+ description: Example-driven consumer-driven contracts. For providers.
143
+ email: brent@fuglylogic.com
144
+ executables: []
145
+ extensions: []
146
+ extra_rdoc_files: []
147
+ files:
148
+ - lib/shokkenki/provider/configuration/provider.rb
149
+ - lib/shokkenki/provider/configuration/session.rb
150
+ - lib/shokkenki/provider/model/fixture.rb
151
+ - lib/shokkenki/provider/model/fixture_requirement.rb
152
+ - lib/shokkenki/provider/model/interaction.rb
153
+ - lib/shokkenki/provider/model/provider.rb
154
+ - lib/shokkenki/provider/model/rack_http_client.rb
155
+ - lib/shokkenki/provider/model/role.rb
156
+ - lib/shokkenki/provider/model/ticket.rb
157
+ - lib/shokkenki/provider/provider.rb
158
+ - lib/shokkenki/provider/rspec.rb
159
+ - lib/shokkenki/provider/rspec/interaction.rb
160
+ - lib/shokkenki/provider/rspec/term/hash_term.rb
161
+ - lib/shokkenki/provider/rspec/term/json_paths_term.rb
162
+ - lib/shokkenki/provider/rspec/term/number_term.rb
163
+ - lib/shokkenki/provider/rspec/term/regexp_term.rb
164
+ - lib/shokkenki/provider/rspec/term/string_term.rb
165
+ - lib/shokkenki/provider/rspec/ticket.rb
166
+ - lib/shokkenki/provider/session.rb
167
+ - lib/shokkenki/provider/ticket_reader.rb
168
+ - lib/shokkenki/provider/version.rb
169
+ homepage: http://github.com/brentsnook/shokkenki-provider
170
+ licenses:
171
+ - GPL2
172
+ post_install_message:
173
+ rdoc_options:
174
+ - --charset=UTF-8
175
+ require_paths:
176
+ - lib
177
+ required_ruby_version: !ruby/object:Gem::Requirement
178
+ none: false
179
+ requirements:
180
+ - - ! '>='
181
+ - !ruby/object:Gem::Version
182
+ version: '1.9'
183
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
+ none: false
185
+ requirements:
186
+ - - ! '>='
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ segments:
190
+ - 0
191
+ hash: -3549391990526076045
192
+ requirements: []
193
+ rubyforge_project:
194
+ rubygems_version: 1.8.24
195
+ signing_key:
196
+ specification_version: 3
197
+ summary: shokkenki-0.0.2
198
+ test_files: []