stub_requests 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -2
- data/.reek.yml +1 -0
- data/.rubocop.yml +33 -35
- data/.simplecov +14 -8
- data/.yardopts +1 -1
- data/CHANGELOG.md +4 -1
- data/README.md +19 -11
- data/Rakefile +1 -3
- data/bin/update_docs.sh +16 -0
- data/gemfiles/webmock_2.3.gemfile.lock +1 -1
- data/gemfiles/webmock_3.5.gemfile.lock +1 -1
- data/gemfiles/webmock_develop.gemfile.lock +1 -1
- data/lib/rspec/subject_as_lambda.rb +138 -0
- data/lib/stub_requests/api.rb +42 -8
- data/lib/stub_requests/argument_validation.rb +17 -6
- data/lib/stub_requests/configuration.rb +21 -0
- data/lib/stub_requests/core_ext/all.rb +4 -0
- data/lib/stub_requests/core_ext/array/extract_options.rb +32 -0
- data/lib/stub_requests/core_ext/class/attribute.rb +61 -0
- data/lib/stub_requests/core_ext/kernel/singleton_class.rb +12 -0
- data/lib/stub_requests/core_ext/module/redefine_method.rb +44 -0
- data/lib/stub_requests/core_ext/object/blank.rb +99 -155
- data/lib/stub_requests/endpoint.rb +28 -30
- data/lib/stub_requests/endpoint_registry.rb +18 -16
- data/lib/stub_requests/exceptions.rb +84 -0
- data/lib/stub_requests/hash_util.rb +2 -0
- data/lib/stub_requests/metrics/endpoint_stat.rb +97 -0
- data/lib/stub_requests/metrics/registry.rb +132 -0
- data/lib/stub_requests/metrics/stub_stat.rb +80 -0
- data/lib/stub_requests/metrics.rb +32 -0
- data/lib/stub_requests/property/validator.rb +136 -0
- data/lib/stub_requests/property.rb +99 -0
- data/lib/stub_requests/service.rb +12 -53
- data/lib/stub_requests/service_registry.rb +8 -9
- data/lib/stub_requests/stub_requests.rb +23 -51
- data/lib/stub_requests/uri/builder.rb +5 -1
- data/lib/stub_requests/uri/scheme.rb +3 -1
- data/lib/stub_requests/uri/suffix.rb +5 -0
- data/lib/stub_requests/uri/validator.rb +3 -1
- data/lib/stub_requests/uri.rb +34 -0
- data/lib/stub_requests/version.rb +1 -1
- data/lib/stub_requests/webmock/builder.rb +116 -0
- data/lib/stub_requests/webmock/stub_registry_extension.rb +46 -0
- data/lib/stub_requests.rb +21 -3
- metadata +19 -49
- data/docs/.gitkeep +0 -0
- data/docs/Array.html +0 -137
- data/docs/FalseClass.html +0 -232
- data/docs/Hash.html +0 -137
- data/docs/NilClass.html +0 -232
- data/docs/Numeric.html +0 -233
- data/docs/Object.html +0 -396
- data/docs/String.html +0 -298
- data/docs/StubRequests/API.html +0 -651
- data/docs/StubRequests/ArgumentValidation.html +0 -309
- data/docs/StubRequests/Endpoint.html +0 -1187
- data/docs/StubRequests/EndpointNotFound.html +0 -157
- data/docs/StubRequests/EndpointRegistry.html +0 -1527
- data/docs/StubRequests/Error.html +0 -153
- data/docs/StubRequests/HashUtil.html +0 -304
- data/docs/StubRequests/InvalidType.html +0 -252
- data/docs/StubRequests/InvalidUri.html +0 -252
- data/docs/StubRequests/Service.html +0 -1307
- data/docs/StubRequests/ServiceHaveEndpoints.html +0 -244
- data/docs/StubRequests/ServiceNotFound.html +0 -252
- data/docs/StubRequests/ServiceRegistry.html +0 -1031
- data/docs/StubRequests/URI/Builder.html +0 -1194
- data/docs/StubRequests/URI/Scheme.html +0 -315
- data/docs/StubRequests/URI/Suffix.html +0 -315
- data/docs/StubRequests/URI/Validator.html +0 -770
- data/docs/StubRequests/URI.html +0 -144
- data/docs/StubRequests/UriSegmentMismatch.html +0 -157
- data/docs/StubRequests/WebMockBuilder.html +0 -887
- data/docs/StubRequests.html +0 -452
- data/docs/Time.html +0 -232
- data/docs/TrueClass.html +0 -232
- data/docs/_config.yml +0 -1
- data/docs/_index.html +0 -391
- data/docs/class_list.html +0 -51
- data/docs/css/common.css +0 -1
- data/docs/css/full_list.css +0 -58
- data/docs/css/style.css +0 -496
- data/docs/file.README.html +0 -225
- data/docs/file_list.html +0 -56
- data/docs/frames.html +0 -17
- data/docs/index.html +0 -225
- data/docs/js/app.js +0 -292
- data/docs/js/full_list.js +0 -216
- data/docs/js/jquery.js +0 -4
- data/docs/method_list.html +0 -707
- data/docs/top-level-namespace.html +0 -112
- data/lib/stub_requests/webmock_builder.rb +0 -108
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Abstraction over WebMock to reduce duplication
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
9
|
+
module StubRequests
|
10
|
+
#
|
11
|
+
# Module Metrics contains logic for collecting metrics about {EndpointStat} and {StubStat}
|
12
|
+
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
# @since 0.1.2
|
15
|
+
#
|
16
|
+
# :reek:TooManyInstanceVariables
|
17
|
+
module Metrics
|
18
|
+
#
|
19
|
+
# Class EndpointStat provides metrics for stubbed endpoints
|
20
|
+
#
|
21
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
22
|
+
# @since 0.1.2
|
23
|
+
#
|
24
|
+
class EndpointStat
|
25
|
+
# includes "Enumerable"
|
26
|
+
# @!parse include Enumerable
|
27
|
+
include Enumerable
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
include Property
|
31
|
+
|
32
|
+
#
|
33
|
+
# @!attribute [r] service_id
|
34
|
+
# @return [Symbol] the id of a {Service}
|
35
|
+
property :service_id, type: Symbol
|
36
|
+
#
|
37
|
+
# @!attribute [r] endpoint_id
|
38
|
+
# @return [Symbol] the id of an endpoint
|
39
|
+
property :endpoint_id, type: Symbol
|
40
|
+
#
|
41
|
+
# @!attribute [r] verb
|
42
|
+
# @return [String] the HTTP verb/method for this endpoint
|
43
|
+
property :verb, type: Symbol
|
44
|
+
#
|
45
|
+
# @!attribute [r] uri_template
|
46
|
+
# @return [String] the full URI template for the endpoint
|
47
|
+
property :uri_template, type: Symbol
|
48
|
+
#
|
49
|
+
# @!attribute [r] stats
|
50
|
+
# @return [Array] an array with recorded request_stubs
|
51
|
+
attr_reader :stats
|
52
|
+
|
53
|
+
#
|
54
|
+
# Initializes a new EndpointStat
|
55
|
+
#
|
56
|
+
# @param [Service] service a service
|
57
|
+
# @param [Endpoint] endpoint an endpoint
|
58
|
+
#
|
59
|
+
def initialize(service, endpoint)
|
60
|
+
@service_id = service.id
|
61
|
+
@endpoint_id = endpoint.id
|
62
|
+
@verb = endpoint.verb
|
63
|
+
@uri_template = [service.uri, endpoint.uri_template].join("/")
|
64
|
+
@stats = Concurrent::Array.new
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_by(attribute:, value:)
|
68
|
+
find { |stat| stat.send(attribute) == value }
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Required by Enumerable
|
73
|
+
#
|
74
|
+
#
|
75
|
+
# @return [Concurrent::Map<Symbol, Service>] an map with services
|
76
|
+
#
|
77
|
+
# @yield used by Enumerable
|
78
|
+
#
|
79
|
+
def each(&block)
|
80
|
+
stats.each(&block)
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Records a WebMock::RequestStub as stubbed
|
85
|
+
#
|
86
|
+
# @param [WebMock::RequestStub] request_stub <description>
|
87
|
+
#
|
88
|
+
# @return [Record]
|
89
|
+
#
|
90
|
+
def record(request_stub)
|
91
|
+
stat = StubStat.new(self, request_stub)
|
92
|
+
stats.push(stat)
|
93
|
+
stat
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Abstraction over WebMock to reduce duplication
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
9
|
+
module StubRequests
|
10
|
+
#
|
11
|
+
# Module Metrics contains logic for collecting metrics about {EndpointStat} and {StubStat}
|
12
|
+
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
# @since 0.1.2
|
15
|
+
#
|
16
|
+
# :reek:DataClump
|
17
|
+
module Metrics
|
18
|
+
#
|
19
|
+
# Class Registry maintains a registry of stubbed endpoints.
|
20
|
+
# Also allows provides querying capabilities for said entities.
|
21
|
+
#
|
22
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
23
|
+
# @since 0.1.2
|
24
|
+
#
|
25
|
+
class Registry
|
26
|
+
# includes "Singleton"
|
27
|
+
# @!parse include Singleton
|
28
|
+
include Singleton
|
29
|
+
# includes "Enumerable"
|
30
|
+
# @!parse include Enumerable
|
31
|
+
include Enumerable
|
32
|
+
|
33
|
+
#
|
34
|
+
# @!attribute [rw] services
|
35
|
+
# @return [Concurrent::Array<EndpointStat>] a map with stubbed endpoints
|
36
|
+
attr_reader :stats
|
37
|
+
|
38
|
+
#
|
39
|
+
# Initialize a new registry
|
40
|
+
#
|
41
|
+
#
|
42
|
+
def initialize
|
43
|
+
@stats = Concurrent::Array.new
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Resets the map with stubbed endpoints
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
def reset
|
52
|
+
stats.clear
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Required by Enumerable
|
57
|
+
#
|
58
|
+
#
|
59
|
+
# @return [Concurrent::Array<EndpointStat>] an array with stubbed endpoints
|
60
|
+
#
|
61
|
+
# @yield used by Enumerable
|
62
|
+
#
|
63
|
+
def each(&block)
|
64
|
+
stats.each(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Records metrics about stubbed endpoints
|
69
|
+
#
|
70
|
+
#
|
71
|
+
# @param [Service] service a symbolic id of the service
|
72
|
+
# @param [Endpoint] endpoint a string with a base_uri to the service
|
73
|
+
# @param [WebMock::RequestStub] request_stub the stubbed request
|
74
|
+
#
|
75
|
+
# @return [Service] the service that was just registered
|
76
|
+
#
|
77
|
+
def record(service, endpoint, request_stub)
|
78
|
+
stat = find_or_initialize_stat(service, endpoint)
|
79
|
+
stat.record(request_stub)
|
80
|
+
|
81
|
+
stats.push(stat)
|
82
|
+
stat
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Mark a {StubStat} as having responded
|
87
|
+
#
|
88
|
+
# @note Called when webmock responds successfully
|
89
|
+
#
|
90
|
+
# @param [WebMock::RequestStub] request_stub the stubbed webmock request
|
91
|
+
#
|
92
|
+
# @return [void]
|
93
|
+
#
|
94
|
+
def mark_as_responded(request_stub)
|
95
|
+
return unless (stat = find_stub_stat(request_stub))
|
96
|
+
|
97
|
+
stat.mark_as_responded
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Finds a {StubStat} amongst the endpoint stubs
|
102
|
+
#
|
103
|
+
#
|
104
|
+
# @param [WebMock::RequestStub] request_stub a stubbed webmock response
|
105
|
+
#
|
106
|
+
# @return [StubStat] the stub_stat matching the request stub
|
107
|
+
#
|
108
|
+
def find_stub_stat(request_stub)
|
109
|
+
map do |endpoint|
|
110
|
+
endpoint.find_by(attribute: :request_stub, value: request_stub)
|
111
|
+
end.compact.first
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def find_or_initialize_stat(service, endpoint)
|
117
|
+
find_stat(service, endpoint) || initialize_stat(service, endpoint)
|
118
|
+
end
|
119
|
+
|
120
|
+
# :reek:UtilityFunction
|
121
|
+
# :reek:FeatureEnvy
|
122
|
+
def find_stat(service, endpoint)
|
123
|
+
find { |stat| stat.service_id == service.id && stat.endpoint_id == endpoint.id }
|
124
|
+
end
|
125
|
+
|
126
|
+
# :reek:UtilityFunction
|
127
|
+
def initialize_stat(service, endpoint)
|
128
|
+
Metrics::EndpointStat.new(service, endpoint)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Abstraction over WebMock to reduce duplication
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
9
|
+
module StubRequests
|
10
|
+
#
|
11
|
+
# Module Metrics contains logic for collecting metrics about {EndpointStat} and {StubStat}
|
12
|
+
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
# @since 0.1.2
|
15
|
+
#
|
16
|
+
module Metrics
|
17
|
+
#
|
18
|
+
# Class StubStat tracks the WebMock::RequestStub life cycle
|
19
|
+
#
|
20
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
21
|
+
# @since 0.1.2
|
22
|
+
#
|
23
|
+
# :reek:TooManyInstanceVariables
|
24
|
+
class StubStat
|
25
|
+
include Property
|
26
|
+
#
|
27
|
+
# @!attribute [r] verb
|
28
|
+
# @return [Symbol] a HTTP verb/method
|
29
|
+
property :verb, type: Symbol
|
30
|
+
#
|
31
|
+
# @!attribute [r] uri
|
32
|
+
# @return [String] the full URI for this endpoint
|
33
|
+
property :uri, type: String
|
34
|
+
#
|
35
|
+
# @!attribute [r] request_stub
|
36
|
+
# @return [WebMock::RequestStub] a webmock stubbed request
|
37
|
+
property :request_stub, type: WebMock::RequestStub
|
38
|
+
#
|
39
|
+
# @!attribute [r] recorded_at
|
40
|
+
# @return [Time] the time this record was recorded
|
41
|
+
property :recorded_at, type: Time
|
42
|
+
#
|
43
|
+
# @!attribute [r] recorded_from
|
44
|
+
# @return [String] the relative path to the spec that recorded it
|
45
|
+
property :recorded_from, type: String
|
46
|
+
#
|
47
|
+
# @!attribute [r] responded_at
|
48
|
+
# @return [Time] the time this stubs response was used
|
49
|
+
property :responded_at, type: Time
|
50
|
+
|
51
|
+
#
|
52
|
+
# Initialize a new Record
|
53
|
+
#
|
54
|
+
#
|
55
|
+
# @param [EndpointStat] endpoint_stat a stubbed endpoint
|
56
|
+
# @param [WebMock::RequestStub] request_stub the stubbed webmock request
|
57
|
+
#
|
58
|
+
def initialize(endpoint_stat, request_stub)
|
59
|
+
request_pattern = request_stub.request_pattern
|
60
|
+
@endpoint_stat = endpoint_stat
|
61
|
+
@verb = request_pattern.method_pattern.to_s.to_sym
|
62
|
+
@uri = request_pattern.uri_pattern.to_s
|
63
|
+
@request_stub = request_stub
|
64
|
+
@recorded_at = Time.now
|
65
|
+
@recorded_from = RSpec.current_example.metadata[:location]
|
66
|
+
@responded_at = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Marks this record as having responded
|
71
|
+
#
|
72
|
+
#
|
73
|
+
# @return [Time] the time it was marked responded
|
74
|
+
#
|
75
|
+
def mark_as_responded
|
76
|
+
@responded_at = Time.now
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Abstraction over WebMock to reduce duplication
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
9
|
+
module StubRequests
|
10
|
+
#
|
11
|
+
# Module Metrics contains logic for collecting metrics about {EndpointStat} and {StubStat}
|
12
|
+
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
# @since 0.1.2
|
15
|
+
#
|
16
|
+
module Metrics
|
17
|
+
#
|
18
|
+
# Records metrics about stubbed endpoints
|
19
|
+
#
|
20
|
+
# @param [Service] service a Service
|
21
|
+
# @param [Endpoint] endpoint an Endpoint
|
22
|
+
# @param [WebMock::RequestStub] endpoint_stub the stubbed webmock request
|
23
|
+
#
|
24
|
+
# @return [EndpointStat] the stat that was recorded
|
25
|
+
#
|
26
|
+
def self.record(service, endpoint, endpoint_stub)
|
27
|
+
return unless StubRequests.config.record_metrics?
|
28
|
+
|
29
|
+
Registry.instance.record(service, endpoint, endpoint_stub)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Abstraction over WebMock to reduce duplication
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
9
|
+
module StubRequests
|
10
|
+
#
|
11
|
+
# Module Property provides type checked attribute definition with default value
|
12
|
+
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
# @since 0.1.2
|
15
|
+
#
|
16
|
+
module Property
|
17
|
+
#
|
18
|
+
# Class Validator provides validation for adding properties
|
19
|
+
#
|
20
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
21
|
+
# @since 0.1.2
|
22
|
+
#
|
23
|
+
class Validator
|
24
|
+
include ArgumentValidation
|
25
|
+
|
26
|
+
#
|
27
|
+
# Validates that the property can be added to the class
|
28
|
+
#
|
29
|
+
#
|
30
|
+
# @param [Symbol] name the name of the property
|
31
|
+
# @param [Class, Module] type the type of the property
|
32
|
+
# @param [Object] default the default value of the property
|
33
|
+
# @param [Hash] properties the list of currently defined properties
|
34
|
+
#
|
35
|
+
# @raise [InvalidArgumentType] when name is not a Symbol
|
36
|
+
# @raise [InvalidArgumentType] when default does not match type
|
37
|
+
# @raise [PropertyDefined] when property has already been defined
|
38
|
+
#
|
39
|
+
# @return [void]
|
40
|
+
#
|
41
|
+
# :reek:LongParameterList
|
42
|
+
def self.call(name, type, default, properties)
|
43
|
+
new(name, type, default, properties).run_validations
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# @!attribute [r] name
|
48
|
+
# @return [Symbol] the name of the property
|
49
|
+
attr_reader :name
|
50
|
+
#
|
51
|
+
# @!attribute [r] type
|
52
|
+
# @return [Class, Module] the type of the property
|
53
|
+
attr_reader :type
|
54
|
+
#
|
55
|
+
# @!attribute [r] default
|
56
|
+
# @return [Object] the default value of the property
|
57
|
+
attr_reader :default
|
58
|
+
#
|
59
|
+
# @!attribute [r] properties
|
60
|
+
# @return [Hash] the list of currently defined properties
|
61
|
+
attr_reader :properties
|
62
|
+
|
63
|
+
# Initializes a new {Validator}
|
64
|
+
#
|
65
|
+
# @param [Symbol] name the name of the property
|
66
|
+
# @param [Class, Module] type the type of the property
|
67
|
+
# @param [Object] default the default value of the property
|
68
|
+
# @param [Hash] properties the list of currently defined properties
|
69
|
+
#
|
70
|
+
# :reek:LongParameterList
|
71
|
+
def initialize(name, type, default, properties)
|
72
|
+
@name = name
|
73
|
+
@type = Array(type).flatten
|
74
|
+
@default = default
|
75
|
+
@properties = properties
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Performs all validations
|
80
|
+
#
|
81
|
+
#
|
82
|
+
# @raise [InvalidArgumentType] when name is not a Symbol
|
83
|
+
# @raise [InvalidArgumentType] when default does not match type
|
84
|
+
# @raise [PropertyDefined] when property has already been defined
|
85
|
+
#
|
86
|
+
# @return [void]
|
87
|
+
#
|
88
|
+
def run_validations
|
89
|
+
validate_undefined
|
90
|
+
validate_name
|
91
|
+
validate_default
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
#
|
97
|
+
# Validates that the name is of type Symbol
|
98
|
+
#
|
99
|
+
# @raise [InvalidArgumentType] when name is not a Symbol
|
100
|
+
#
|
101
|
+
# @return [void]
|
102
|
+
#
|
103
|
+
def validate_name
|
104
|
+
validate! :name, name, is_a: Symbol
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Validate that the default value matches the type
|
109
|
+
#
|
110
|
+
#
|
111
|
+
# @raise [InvalidArgumentType] when default does not match type
|
112
|
+
#
|
113
|
+
# @return [void]
|
114
|
+
#
|
115
|
+
def validate_default
|
116
|
+
return unless default || default.is_a?(FalseClass)
|
117
|
+
|
118
|
+
validate! :default, default, is_a: type
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Validate that the property has not been defined
|
123
|
+
#
|
124
|
+
#
|
125
|
+
# @raise [PropertyDefined] when property has already been defined
|
126
|
+
#
|
127
|
+
# @return [void]
|
128
|
+
#
|
129
|
+
def validate_undefined
|
130
|
+
return unless (prop = properties[name])
|
131
|
+
|
132
|
+
raise PropertyDefined, name: name, type: prop[:type], default: prop[:default]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Abstraction over WebMock to reduce duplication
|
5
|
+
#
|
6
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
9
|
+
module StubRequests
|
10
|
+
#
|
11
|
+
# Module Property provides type checked attribute definition with default value
|
12
|
+
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
# @since 0.1.2
|
15
|
+
#
|
16
|
+
module Property
|
17
|
+
include ArgumentValidation
|
18
|
+
|
19
|
+
#
|
20
|
+
# Extends the base class with the ClassMethods module
|
21
|
+
#
|
22
|
+
# @param [Class,Module] base the class where this module is included
|
23
|
+
#
|
24
|
+
# @return [void]
|
25
|
+
#
|
26
|
+
def self.included(base)
|
27
|
+
base.class_attribute :properties, default: {}
|
28
|
+
base.send(:extend, ClassMethods)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Module ClassMethods provides class methods for {Property}
|
33
|
+
#
|
34
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
35
|
+
#
|
36
|
+
# :reek:DataClump
|
37
|
+
module ClassMethods
|
38
|
+
#
|
39
|
+
# Define property methods for the name
|
40
|
+
#
|
41
|
+
# @param [Symbol] name the name of the property
|
42
|
+
# @param [Object] type: the expected type of the property
|
43
|
+
# @param [Hash<Symbol>] **options a hash with options
|
44
|
+
# @option options [Object] :default a default value for the property
|
45
|
+
#
|
46
|
+
# @return [Object] the whatever
|
47
|
+
#
|
48
|
+
def property(name, type:, **options)
|
49
|
+
type = normalize_type(type, options)
|
50
|
+
default = options[:default]
|
51
|
+
Property::Validator.call(name, type, default, properties)
|
52
|
+
|
53
|
+
Docile.dsl_eval(self) do
|
54
|
+
define_property(name, type, default)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def normalize_type(type, **options)
|
59
|
+
type_array = Array(type)
|
60
|
+
return type_array unless (default = options[:default])
|
61
|
+
|
62
|
+
type_array.concat([default.class]).flatten.uniq
|
63
|
+
end
|
64
|
+
|
65
|
+
def define_property(name, type, default)
|
66
|
+
property_reader(name)
|
67
|
+
property_predicate(name)
|
68
|
+
property_writer(name, type)
|
69
|
+
|
70
|
+
set_property_defined(name, type, default)
|
71
|
+
end
|
72
|
+
|
73
|
+
def property_reader(name)
|
74
|
+
silence_redefinition_of_method(name.to_s)
|
75
|
+
redefine_method(name) do
|
76
|
+
instance_variable_get("@#{name}") || properties.dig(name, :default)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def property_predicate(name)
|
81
|
+
silence_redefinition_of_method("#{name}?")
|
82
|
+
redefine_method("#{name}?") do
|
83
|
+
!!public_send(name) # rubocop:disable Style/DoubleNegation
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def property_writer(name, type)
|
88
|
+
redefine_method("#{name}=") do |obj|
|
89
|
+
validate! name, obj, is_a: type
|
90
|
+
instance_variable_set("@#{name}", obj)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_property_defined(name, type, default)
|
95
|
+
properties[name] = { type: type, default: default }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -10,21 +10,23 @@ module StubRequests
|
|
10
10
|
#
|
11
11
|
# Class Service provides details for a registered service
|
12
12
|
#
|
13
|
+
# @author Mikael Henriksson <mikael@zoolutions.se>
|
14
|
+
#
|
13
15
|
class Service
|
14
|
-
include ArgumentValidation
|
15
16
|
include Comparable
|
17
|
+
include Property
|
16
18
|
|
17
19
|
# @!attribute [rw] id
|
18
20
|
# @return [EndpointRegistry] the id of the service
|
19
|
-
|
21
|
+
property :id, type: Symbol
|
20
22
|
|
21
23
|
# @!attribute [rw] uri
|
22
24
|
# @return [EndpointRegistry] the base uri to the service
|
23
|
-
|
25
|
+
property :uri, type: String
|
24
26
|
|
25
|
-
# @!attribute [rw]
|
27
|
+
# @!attribute [rw] endpoints
|
26
28
|
# @return [EndpointRegistry] a list with defined endpoints
|
27
|
-
attr_reader :
|
29
|
+
attr_reader :endpoints
|
28
30
|
|
29
31
|
#
|
30
32
|
# Initializes a new instance of a Service
|
@@ -33,28 +35,9 @@ module StubRequests
|
|
33
35
|
# @param [String] service_uri the base uri to reach the service
|
34
36
|
#
|
35
37
|
def initialize(service_id, service_uri)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@id = service_id
|
40
|
-
@uri = service_uri
|
41
|
-
@endpoint_registry = EndpointRegistry.new
|
42
|
-
end
|
43
|
-
|
44
|
-
#
|
45
|
-
# Registers a new endpoint or updates an existing one
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# @param [Symbol] endpoint_id the id of this Endpoint
|
49
|
-
# @param [Symbol] verb a HTTP verb
|
50
|
-
# @param [String] uri_template the URI to reach the endpoint
|
51
|
-
# @param [optional, Hash<Symbol>] default_options default options
|
52
|
-
#
|
53
|
-
# @return [Endpoint] either the new endpoint or the updated one
|
54
|
-
#
|
55
|
-
# :reek:LongParameterList { max_params: 5 }
|
56
|
-
def register_endpoint(endpoint_id, verb, uri_template, default_options = {})
|
57
|
-
endpoint_registry.register(endpoint_id, verb, uri_template, default_options)
|
38
|
+
self.id = service_id
|
39
|
+
self.uri = service_uri
|
40
|
+
@endpoints = EndpointRegistry.new
|
58
41
|
end
|
59
42
|
|
60
43
|
#
|
@@ -63,31 +46,7 @@ module StubRequests
|
|
63
46
|
# @return [true,false]
|
64
47
|
#
|
65
48
|
def endpoints?
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
#
|
70
|
-
# Gets an endpoint from the {#endpoint_registry} collection
|
71
|
-
#
|
72
|
-
# @param [Symbol] endpoint_id the id of the endpoint
|
73
|
-
#
|
74
|
-
# @raise [EndpointNotFound] when the endpoint couldn't be found
|
75
|
-
#
|
76
|
-
# @return [Endpoint]
|
77
|
-
#
|
78
|
-
def get_endpoint!(endpoint_id)
|
79
|
-
endpoint_registry.get!(endpoint_id)
|
80
|
-
end
|
81
|
-
|
82
|
-
#
|
83
|
-
# Gets an endpoint from the {#endpoint_registry} collection
|
84
|
-
#
|
85
|
-
# @param [Symbol] endpoint_id the id of the endpoint
|
86
|
-
#
|
87
|
-
# @return [Endpoint, nil]
|
88
|
-
#
|
89
|
-
def get_endpoint(endpoint_id)
|
90
|
-
endpoint_registry.get(endpoint_id)
|
49
|
+
endpoints.any?
|
91
50
|
end
|
92
51
|
|
93
52
|
#
|
@@ -100,7 +59,7 @@ module StubRequests
|
|
100
59
|
+"#<#{self.class}",
|
101
60
|
+" id=#{id}",
|
102
61
|
+" uri=#{uri}",
|
103
|
-
+" endpoints=#{
|
62
|
+
+" endpoints=#{endpoints.endpoints_string}",
|
104
63
|
+">",
|
105
64
|
].join("")
|
106
65
|
end
|