parliament-ruby 0.5.19 → 0.6.0
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/.rubocop.yml +1 -3
- data/.travis.yml +0 -1
- data/Gemfile +2 -0
- data/Makefile +9 -4
- data/README.md +185 -11
- data/Rakefile +1 -1
- data/lib/parliament/client_error.rb +21 -0
- data/lib/parliament/decorator/constituency_area.rb +27 -0
- data/lib/parliament/{decorators → decorator}/constituency_group.rb +29 -1
- data/lib/parliament/decorator/contact_point.rb +48 -0
- data/lib/parliament/decorator/gender.rb +13 -0
- data/lib/parliament/decorator/gender_identity.rb +13 -0
- data/lib/parliament/decorator/house.rb +41 -0
- data/lib/parliament/decorator/house_incumbency.rb +50 -0
- data/lib/parliament/decorator/house_seat.rb +27 -0
- data/lib/parliament/decorator/incumbency.rb +57 -0
- data/lib/parliament/decorator/party.rb +27 -0
- data/lib/parliament/decorator/party_membership.rb +36 -0
- data/lib/parliament/decorator/person.rb +224 -0
- data/lib/parliament/{decorators → decorator}/postal_address.rb +5 -1
- data/lib/parliament/decorator/seat_incumbency.rb +64 -0
- data/lib/parliament/decorator.rb +7 -0
- data/lib/parliament/network_error.rb +13 -0
- data/lib/parliament/no_content_response_error.rb +19 -0
- data/lib/parliament/request.rb +112 -33
- data/lib/parliament/response.rb +76 -9
- data/lib/parliament/server_error.rb +21 -0
- data/lib/parliament/utils.rb +113 -13
- data/lib/parliament/version.rb +1 -1
- data/lib/parliament.rb +8 -4
- data/parliament-ruby.gemspec +6 -6
- metadata +32 -28
- data/lib/parliament/decorators/constituency_area.rb +0 -17
- data/lib/parliament/decorators/contact_point.rb +0 -29
- data/lib/parliament/decorators/gender.rb +0 -9
- data/lib/parliament/decorators/gender_identity.rb +0 -9
- data/lib/parliament/decorators/house.rb +0 -28
- data/lib/parliament/decorators/house_incumbency.rb +0 -31
- data/lib/parliament/decorators/house_seat.rb +0 -17
- data/lib/parliament/decorators/incumbency.rb +0 -35
- data/lib/parliament/decorators/party.rb +0 -17
- data/lib/parliament/decorators/party_membership.rb +0 -23
- data/lib/parliament/decorators/person.rb +0 -152
- data/lib/parliament/decorators/seat_incumbency.rb +0 -39
- data/lib/parliament/no_content_error.rb +0 -9
@@ -1,6 +1,10 @@
|
|
1
1
|
module Parliament
|
2
|
-
module
|
2
|
+
module Decorator
|
3
|
+
# Decorator namespace for Grom::Node instances with type: http://id.ukpds.org/schema/PostalAddress
|
3
4
|
module PostalAddress
|
5
|
+
# Builds a full address using the lines of the address and the postcode.
|
6
|
+
#
|
7
|
+
# @return [String, String] the full address of the Grom::Node or an empty string.
|
4
8
|
def full_address
|
5
9
|
address_array.join(', ')
|
6
10
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Parliament
|
2
|
+
module Decorator
|
3
|
+
# Decorator namespace for Grom::Node instances with type: http://id.ukpds.org/schema/SeatIncumbency
|
4
|
+
module SeatIncumbency
|
5
|
+
# Alias incumbencyStartDate with fallback.
|
6
|
+
#
|
7
|
+
# @return [DateTime, nil] the start date of the Grom::Node or nil.
|
8
|
+
def start_date
|
9
|
+
respond_to?(:incumbencyStartDate) ? DateTime.parse(incumbencyStartDate) : nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# Alias incumbencyEndDate with fallback.
|
13
|
+
#
|
14
|
+
# @return [DateTime, nil] the end date of the Grom::Node or nil.
|
15
|
+
def end_date
|
16
|
+
respond_to?(:incumbencyEndDate) ? DateTime.parse(incumbencyEndDate) : nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# Alias seatIncumbencyHasHouseSeat with fallback.
|
20
|
+
#
|
21
|
+
# @return [Grom::Node, nil] the seat of the Grom::Node or nil.
|
22
|
+
def seat
|
23
|
+
respond_to?(:seatIncumbencyHasHouseSeat) ? seatIncumbencyHasHouseSeat.first : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Checks if Grom::Node has an end date.
|
27
|
+
#
|
28
|
+
# @return [Boolean] a boolean depending on whether or not the Grom::Node has an end date.
|
29
|
+
def current?
|
30
|
+
has_end_date = respond_to?(:incumbencyEndDate)
|
31
|
+
|
32
|
+
!has_end_date
|
33
|
+
end
|
34
|
+
|
35
|
+
# Alias houseSeatHasHouse with fallback.
|
36
|
+
#
|
37
|
+
# @return [Grom::Node, nil] the house of the Grom::Node or nil.
|
38
|
+
def house
|
39
|
+
seat.nil? ? nil : seat.house
|
40
|
+
end
|
41
|
+
|
42
|
+
# Alias houseSeatHasConstituencyGroup with fallback.
|
43
|
+
#
|
44
|
+
# @return [Grom::Node, nil] the constituency of the Grom::Node or nil.
|
45
|
+
def constituency
|
46
|
+
seat.nil? ? nil : seat.constituency
|
47
|
+
end
|
48
|
+
|
49
|
+
# Alias incumbencyHasContactPoint with fallback.
|
50
|
+
#
|
51
|
+
# @return [Array, Array] the contact points of the Grom::Node or an empty array.
|
52
|
+
def contact_points
|
53
|
+
respond_to?(:incumbencyHasContactPoint) ? incumbencyHasContactPoint : []
|
54
|
+
end
|
55
|
+
|
56
|
+
# Alias incumbencyHasMember with fallback.
|
57
|
+
#
|
58
|
+
# @return [Grom::Node, nil] the member connected to the Grom::Node or nil.
|
59
|
+
def member
|
60
|
+
respond_to?(:incumbencyHasMember) ? incumbencyHasMember.first : nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Parliament
|
2
|
+
# A parent class that standardises the error message generated for network errors.
|
3
|
+
#
|
4
|
+
# @see Parliament::ClientError
|
5
|
+
# @see Parliament::ServerError
|
6
|
+
#
|
7
|
+
# @since 0.6.0
|
8
|
+
class NetworkError < StandardError
|
9
|
+
def initialize(url, response)
|
10
|
+
super("#{response.code} HTTP status code received from: #{url} - #{response.message}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Parliament
|
2
|
+
# An error raised when a 204 status code is returned by Net::HTTP inside of Parliament::Request.
|
3
|
+
#
|
4
|
+
# @since 0.6.0
|
5
|
+
class NoContentResponseError < Parliament::NetworkError
|
6
|
+
# @param [String] url the url that caused the Parliament::ClientError
|
7
|
+
# @param [Net::HTTPResponse] response the Net:HTTPResponse that caused the Parliament::NoContentResponseError
|
8
|
+
#
|
9
|
+
# @example Creating a Parliament::NoContentResponseError
|
10
|
+
# url = 'http://localhost:3030/foo/bar'
|
11
|
+
#
|
12
|
+
# response = Net::HTTP.get_response(URI(url))
|
13
|
+
#
|
14
|
+
# raise Parliament::NoContentResponseError.new(url, response) if response.code == '204'
|
15
|
+
def initialize(url, response)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/parliament/request.rb
CHANGED
@@ -1,26 +1,109 @@
|
|
1
1
|
module Parliament
|
2
|
+
# API request object, allowing the user to build a request to a graph-based API, download n-triple data and create
|
3
|
+
# ruby objects from that data.
|
4
|
+
#
|
5
|
+
# @since 0.1.0
|
6
|
+
#
|
7
|
+
# @attr_reader [String] base_url the base url of our api. (expected: http://example.com - without the trailing slash).
|
2
8
|
class Request
|
3
9
|
attr_reader :base_url
|
4
10
|
|
11
|
+
# Creates a new instance of Parliament::Request.
|
12
|
+
#
|
13
|
+
# An interesting note for #initialize is that setting base_url on the class, or using the environment variable
|
14
|
+
# PARLIAMENT_BASE_URL means you don't need to pass in a base_url. You can pass one anyway to override the
|
15
|
+
# environment variable or class parameter.
|
16
|
+
#
|
17
|
+
# @example Setting the base_url on the class
|
18
|
+
# Parliament::Request.base_url = 'http://example.com'
|
19
|
+
#
|
20
|
+
# Parliament::Request.new.base_url #=> 'http://example.com'
|
21
|
+
#
|
22
|
+
# @example Setting the base_url via environment variable
|
23
|
+
# ENV['PARLIAMENT_BASE_URL'] #=> 'http://test.com'
|
24
|
+
#
|
25
|
+
# Parliament::Request.new.base_url #=> 'http://test.com'
|
26
|
+
#
|
27
|
+
# @example Setting the base_url via a parameter
|
28
|
+
# Parliament::Request.base_url #=> nil
|
29
|
+
# ENV['PARLIAMENT_BASE_URL'] #=> nil
|
30
|
+
#
|
31
|
+
# Parliament::Request.new(base_url: 'http://example.com').base_url #=> 'http://example.com'
|
32
|
+
#
|
33
|
+
# @example Overriding the base_url via a parameter
|
34
|
+
# ENV['PARLIAMENT_BASE_URL'] #=> 'http://test.com'
|
35
|
+
#
|
36
|
+
# Parliament::Request.new(base_url: 'http://example.com').base_url #=> 'http://example.com'
|
37
|
+
#
|
38
|
+
# @param [String] base_url the base url of our api. (expected: http://example.com - without the trailing slash).
|
5
39
|
def initialize(base_url: nil)
|
6
40
|
@endpoint_parts = []
|
7
41
|
@base_url = base_url || self.class.base_url || ENV['PARLIAMENT_BASE_URL']
|
8
42
|
end
|
9
43
|
|
44
|
+
# Overrides ruby's method_missing to allow creation of URLs through method calls.
|
45
|
+
#
|
46
|
+
# @example Adding a simple URL part
|
47
|
+
# request = Parliament::Request.new(base_url: 'http://example.com')
|
48
|
+
#
|
49
|
+
# # url: http://example.com/people
|
50
|
+
# request.people
|
51
|
+
#
|
52
|
+
# @example Adding a simple URL part with parameters
|
53
|
+
# request = Parliament::Request.new(base_url: 'http://example.com')
|
54
|
+
#
|
55
|
+
# # url: http://example.com/people/123456
|
56
|
+
# request.people('123456')
|
57
|
+
#
|
58
|
+
# @example Chaining URL parts and using hyphens
|
59
|
+
# request = Parliament::Request.new(base_url: 'http://example.com')
|
60
|
+
#
|
61
|
+
# # url: http://example.com/people/123456/foo/bar/hello-world/7890
|
62
|
+
# request.people('123456').foo.bar('hello-world', '7890')
|
63
|
+
#
|
64
|
+
# @param [Symbol] method the 'method' (url part) we are processing.
|
65
|
+
# @param [Array<Object>] params parameters passed to the specified method (url part).
|
66
|
+
# @param [Block] block additional block (kept for compatibility with method_missing API).
|
67
|
+
#
|
68
|
+
# @return [Parliament::Request] self.
|
10
69
|
def method_missing(method, *params, &block)
|
11
|
-
# TODO: Fix this smell
|
12
|
-
super if method == :base_url=
|
13
|
-
|
14
70
|
@endpoint_parts << method.to_s
|
15
71
|
@endpoint_parts << params
|
16
72
|
@endpoint_parts = @endpoint_parts.flatten!
|
17
|
-
|
73
|
+
|
74
|
+
block&.call
|
75
|
+
|
76
|
+
self || super
|
18
77
|
end
|
19
78
|
|
20
|
-
|
21
|
-
|
79
|
+
# This class always responds to method calls, even those missing. Therefore, respond_to_missing? always returns true.
|
80
|
+
#
|
81
|
+
# @return [Boolean] always returns true.
|
82
|
+
def respond_to_missing?(_, _ = false)
|
83
|
+
true # responds to everything, always
|
22
84
|
end
|
23
85
|
|
86
|
+
# Using our url built via #method_missing, make a HTTP GET request and process results into a response.
|
87
|
+
#
|
88
|
+
# @example HTTP GET request
|
89
|
+
# request = Parliament::Request.new(base_url: 'http://example.com')
|
90
|
+
#
|
91
|
+
# # url: http://example.com/people/123456
|
92
|
+
# response = request.people('123456').get #=> #<Parliament::Response ...>
|
93
|
+
#
|
94
|
+
# @example HTTP GET request with URI encoded form values
|
95
|
+
# request = Parliament::Request.new(base_url: 'http://example.com')
|
96
|
+
#
|
97
|
+
# # url: http://example.com/people/current?limit=10&page=4&lang=en-gb
|
98
|
+
# response = request.people.current.get({ limit: 10, page: 4, lang: 'en-gb' }) #=> #<Parliament::Response ...>
|
99
|
+
#
|
100
|
+
# @raise [Parliament::ServerError] when the server responds with a 5xx status code.
|
101
|
+
# @raise [Parliament::ClientError] when the server responds with a 4xx status code.
|
102
|
+
# @raise [Parliament::NoContentResponseError] when the server responds with a 204 status code.
|
103
|
+
#
|
104
|
+
# @param [Hash] params (optional) additional URI encoded form values to be added to the URI.
|
105
|
+
#
|
106
|
+
# @return [Parliament::Response] a Parliament::Response object containing all of the nodes returned from the URL.
|
24
107
|
def get(params: nil)
|
25
108
|
endpoint_uri = URI.parse(api_endpoint)
|
26
109
|
endpoint_uri.query = URI.encode_www_form(params.to_a) unless params.nil?
|
@@ -32,29 +115,35 @@ module Parliament
|
|
32
115
|
build_parliament_response(net_response)
|
33
116
|
end
|
34
117
|
|
35
|
-
|
36
|
-
objects = Grom::Reader.new(response.body).objects
|
37
|
-
objects.map { |object| assign_decorator(object) }
|
118
|
+
private
|
38
119
|
|
39
|
-
|
120
|
+
# @attr [String] base_url the base url of our api. (expected: http://example.com - without the trailing slash).
|
121
|
+
class << self
|
122
|
+
attr_accessor :base_url
|
40
123
|
end
|
41
124
|
|
42
|
-
def
|
43
|
-
|
44
|
-
handle_server_error(response)
|
45
|
-
handle_no_content_error(response)
|
125
|
+
def api_endpoint
|
126
|
+
[@base_url, @endpoint_parts].join('/')
|
46
127
|
end
|
47
128
|
|
48
|
-
def
|
49
|
-
|
129
|
+
def handle_errors(response)
|
130
|
+
case response
|
131
|
+
when Net::HTTPSuccess # 2xx Status
|
132
|
+
exception_class = Parliament::NoContentResponseError if response.code == '204'
|
133
|
+
when Net::HTTPClientError # 4xx Status
|
134
|
+
exception_class = Parliament::ClientError
|
135
|
+
when Net::HTTPServerError # 5xx Status
|
136
|
+
exception_class = Parliament::ServerError
|
137
|
+
end
|
138
|
+
|
139
|
+
raise exception_class.new(api_endpoint, response) if exception_class
|
50
140
|
end
|
51
141
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
142
|
+
def build_parliament_response(response)
|
143
|
+
objects = Grom::Reader.new(response.body).objects
|
144
|
+
objects.map { |object| assign_decorator(object) }
|
55
145
|
|
56
|
-
|
57
|
-
raise Parliament::NoContentError if response.code == '204'
|
146
|
+
Parliament::Response.new(objects)
|
58
147
|
end
|
59
148
|
|
60
149
|
def assign_decorator(object)
|
@@ -62,20 +151,10 @@ module Parliament
|
|
62
151
|
|
63
152
|
object_type = Grom::Helper.get_id(object.type)
|
64
153
|
|
65
|
-
return object unless Parliament::
|
154
|
+
return object unless Parliament::Decorator.constants.include?(object_type.to_sym)
|
66
155
|
|
67
|
-
decorator_module = Object.const_get("Parliament::
|
156
|
+
decorator_module = Object.const_get("Parliament::Decorator::#{object_type}")
|
68
157
|
object.extend(decorator_module)
|
69
158
|
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
class << self
|
74
|
-
attr_accessor :base_url
|
75
|
-
end
|
76
|
-
|
77
|
-
def api_endpoint
|
78
|
-
[@base_url, @endpoint_parts].join('/')
|
79
|
-
end
|
80
159
|
end
|
81
160
|
end
|
data/lib/parliament/response.rb
CHANGED
@@ -1,16 +1,65 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
3
|
module Parliament
|
4
|
+
# API response object that wraps an Array of Grom::Node objects with common helper methods.
|
5
|
+
#
|
6
|
+
# Delegates a number of common methods to the array of Grom::Nodes including, but not limited to, :size, :each, :map, :count etc.
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
#
|
10
|
+
# @attr_reader [Array<Grom::Node>] nodes Graph nodes.
|
4
11
|
class Response
|
5
12
|
include Enumerable
|
6
13
|
extend Forwardable
|
7
14
|
attr_reader :nodes
|
8
15
|
def_delegators :@nodes, :size, :each, :select, :map, :select!, :map!, :count, :length, :[], :empty?
|
9
16
|
|
17
|
+
# @param [Array<Grom::Node>] nodes An array of nodes the response should wrap
|
10
18
|
def initialize(nodes)
|
11
19
|
@nodes = nodes
|
12
20
|
end
|
13
21
|
|
22
|
+
# Given our array of Grom::Nodes, filter them into arrays of 'types' of nodes.
|
23
|
+
#
|
24
|
+
# Note: this method assumes all of your nodes include a #type attribute.
|
25
|
+
#
|
26
|
+
# @since 0.2.0
|
27
|
+
#
|
28
|
+
# @example Filtering for a single type
|
29
|
+
# node_1 = Grom::Node.new
|
30
|
+
# node_1.instance_variable_set(:type, 'type_1')
|
31
|
+
# node_2 = Grom::Node.new
|
32
|
+
# node_2.instance_variable_set(:type, 'type_3')
|
33
|
+
# node_3 = Grom::Node.new
|
34
|
+
# node_3.instance_variable_set(:type, 'type_1')
|
35
|
+
# node_4 = Grom::Node.new
|
36
|
+
# node_4.instance_variable_set(:type, 'type_2')
|
37
|
+
# nodes = [node_1, node_2, node_3, node_4]
|
38
|
+
#
|
39
|
+
# response = Parliament::Response.new(nodes)
|
40
|
+
# response.filter('type_2') #=> [#<Grom::Node @type='type_2'>]
|
41
|
+
#
|
42
|
+
# @example Filtering for multiple types
|
43
|
+
# node_1 = Grom::Node.new
|
44
|
+
# node_1.instance_variable_set(:type, 'type_1')
|
45
|
+
# node_2 = Grom::Node.new
|
46
|
+
# node_2.instance_variable_set(:type, 'type_3')
|
47
|
+
# node_3 = Grom::Node.new
|
48
|
+
# node_3.instance_variable_set(:type, 'type_1')
|
49
|
+
# node_4 = Grom::Node.new
|
50
|
+
# node_4.instance_variable_set(:type, 'type_2')
|
51
|
+
# nodes = [node_1, node_2, node_3, node_4]
|
52
|
+
#
|
53
|
+
# response = Parliament::Response.new(nodes)
|
54
|
+
# response.filter('type_2', 'type_1') #=> [[#<Grom::Node @type='type_2'>], [#<Grom::Node @type='type_1'>, #<Grom::Node @type='type_1'>]]
|
55
|
+
#
|
56
|
+
# # Also consider
|
57
|
+
# type_2, type_1 = response.filter('type_2', 'type_1')
|
58
|
+
# type_2 #=> [#<Grom::Node @type='type_2'>]
|
59
|
+
# type_1 #=> [#<Grom::Node @type='type_1'>, #<Grom::Node @type='type_1'>]
|
60
|
+
#
|
61
|
+
# @param [Array<String>] types An array of type strings that you are looking for.
|
62
|
+
# @return [Array<Grom::Node> || Array<*Array<Grom::Node>>] If you pass one type, this returns an Array of Grom::Node objects. If you pass multiple, it returns an array, of arrays of Grom::Node objects.
|
14
63
|
def filter(*types)
|
15
64
|
filtered_objects = Array.new(types.size) { [] }
|
16
65
|
|
@@ -26,15 +75,14 @@ module Parliament
|
|
26
75
|
types.size == 1 ? result.first : result
|
27
76
|
end
|
28
77
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
78
|
+
# Sort the Parliament::Response nodes in ascending order by a set of attributes on each node.
|
79
|
+
#
|
80
|
+
# @see Parliament::Utils.sort_by
|
81
|
+
#
|
82
|
+
# @since 0.5.0
|
83
|
+
#
|
84
|
+
# @param [Array<Symbol>] parameters Attributes to sort on - left to right.
|
85
|
+
# @return [Array<Grom::Node>] A sorted array of nodes.
|
38
86
|
def sort_by(*parameters)
|
39
87
|
Parliament::Utils.sort_by({
|
40
88
|
list: @nodes,
|
@@ -42,11 +90,30 @@ module Parliament
|
|
42
90
|
})
|
43
91
|
end
|
44
92
|
|
93
|
+
# Sort the Parliament::Response nodes in descending order by a set of attributes on each node.
|
94
|
+
#
|
95
|
+
# @see Parliament::Utils.reverse_sort_by
|
96
|
+
#
|
97
|
+
# @since 0.5.0
|
98
|
+
#
|
99
|
+
# @param [Array<Symbol>] parameters Attributes to sort on - left to right.
|
100
|
+
# @return [Array<Grom::Node>] A sorted array of nodes.
|
45
101
|
def reverse_sort_by(*parameters)
|
46
102
|
Parliament::Utils.reverse_sort_by({
|
47
103
|
list: @nodes,
|
48
104
|
parameters: parameters
|
49
105
|
})
|
50
106
|
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def build_responses(filtered_objects)
|
111
|
+
result = []
|
112
|
+
|
113
|
+
filtered_objects.each do |objects|
|
114
|
+
result << Parliament::Response.new(objects)
|
115
|
+
end
|
116
|
+
result
|
117
|
+
end
|
51
118
|
end
|
52
119
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Parliament
|
2
|
+
# An error raised when a 5xx status code is returned by Net::HTTP inside of Parliament::Request.
|
3
|
+
#
|
4
|
+
# @see Parliament::ClientError
|
5
|
+
#
|
6
|
+
# @since 0.6.0
|
7
|
+
class ServerError < Parliament::NetworkError
|
8
|
+
# @param [String] url the url that caused the Parliament::ServerError
|
9
|
+
# @param [Net::HTTPServerError] response the Net:HTTPServerError that caused the Parliament::ServerError
|
10
|
+
#
|
11
|
+
# @example Creating a Parliament::ServerError
|
12
|
+
# url = 'http://localhost:3030/foo/bar'
|
13
|
+
#
|
14
|
+
# response = Net::HTTP.get_response(URI(url))
|
15
|
+
#
|
16
|
+
# raise Parliament::ServerError.new(url, response) if response.is_a?(Net::HTTPServerError)
|
17
|
+
def initialize(url, response)
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|