neverbounce-api 1.0.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 +7 -0
- data/.codeclimate.yml +8 -0
- data/.gitignore +21 -0
- data/.rspec +4 -0
- data/.rubocop.yml +1161 -0
- data/.travis.yml +27 -0
- data/.yardopts +2 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +48 -0
- data/LICENSE +9 -0
- data/README.md +235 -0
- data/lib/never_bounce/api/client.rb +259 -0
- data/lib/never_bounce/api/error.rb +21 -0
- data/lib/never_bounce/api/feature/basic_initialize.rb +20 -0
- data/lib/never_bounce/api/feature/eigencache.rb +48 -0
- data/lib/never_bounce/api/feature/igetset.rb +41 -0
- data/lib/never_bounce/api/feature/oattrs.rb +100 -0
- data/lib/never_bounce/api/feature/require_attr.rb +27 -0
- data/lib/never_bounce/api/request/account_info.rb +29 -0
- data/lib/never_bounce/api/request/base.rb +102 -0
- data/lib/never_bounce/api/request/jobs_create.rb +90 -0
- data/lib/never_bounce/api/request/jobs_delete.rb +34 -0
- data/lib/never_bounce/api/request/jobs_download.rb +34 -0
- data/lib/never_bounce/api/request/jobs_parse.rb +55 -0
- data/lib/never_bounce/api/request/jobs_results.rb +53 -0
- data/lib/never_bounce/api/request/jobs_search.rb +57 -0
- data/lib/never_bounce/api/request/jobs_start.rb +47 -0
- data/lib/never_bounce/api/request/jobs_status.rb +37 -0
- data/lib/never_bounce/api/request/single_check.rb +67 -0
- data/lib/never_bounce/api/response/account_info.rb +39 -0
- data/lib/never_bounce/api/response/account_info/job_counts.rb +26 -0
- data/lib/never_bounce/api/response/address_info.rb +43 -0
- data/lib/never_bounce/api/response/base.rb +15 -0
- data/lib/never_bounce/api/response/container.rb +126 -0
- data/lib/never_bounce/api/response/credits_info/base.rb +30 -0
- data/lib/never_bounce/api/response/credits_info/monthly.rb +16 -0
- data/lib/never_bounce/api/response/credits_info/paid.rb +20 -0
- data/lib/never_bounce/api/response/error_message.rb +17 -0
- data/lib/never_bounce/api/response/feature/job_status_fields.rb +68 -0
- data/lib/never_bounce/api/response/feature/job_status_fields/total.rb +46 -0
- data/lib/never_bounce/api/response/jobs_create.rb +10 -0
- data/lib/never_bounce/api/response/jobs_delete.rb +8 -0
- data/lib/never_bounce/api/response/jobs_download.rb +19 -0
- data/lib/never_bounce/api/response/jobs_parse.rb +10 -0
- data/lib/never_bounce/api/response/jobs_results.rb +34 -0
- data/lib/never_bounce/api/response/jobs_results/item.rb +65 -0
- data/lib/never_bounce/api/response/jobs_results/query.rb +20 -0
- data/lib/never_bounce/api/response/jobs_search.rb +33 -0
- data/lib/never_bounce/api/response/jobs_search/query.rb +16 -0
- data/lib/never_bounce/api/response/jobs_search/result.rb +13 -0
- data/lib/never_bounce/api/response/jobs_start.rb +10 -0
- data/lib/never_bounce/api/response/jobs_status.rb +11 -0
- data/lib/never_bounce/api/response/message.rb +32 -0
- data/lib/never_bounce/api/response/single_check.rb +68 -0
- data/lib/never_bounce/api/response/status_message.rb +17 -0
- data/lib/never_bounce/api/response/success_message.rb +12 -0
- data/lib/never_bounce/api/session.rb +141 -0
- data/lib/never_bounce/api/version.rb +4 -0
- data/lib/neverbounce-api.rb +4 -0
- data/lib/neverbounce.rb +3 -0
- data/neverbounce-api.gemspec +20 -0
- data/spec/lib/never_bounce/api/client_spec.rb +199 -0
- data/spec/lib/never_bounce/api/feature/basic_initialize_spec.rb +25 -0
- data/spec/lib/never_bounce/api/feature/eigencache_spec.rb +28 -0
- data/spec/lib/never_bounce/api/feature/igetset_spec.rb +45 -0
- data/spec/lib/never_bounce/api/feature/oattrs_spec.rb +72 -0
- data/spec/lib/never_bounce/api/feature/require_attr_spec.rb +25 -0
- data/spec/lib/never_bounce/api/request/account_info_spec.rb +29 -0
- data/spec/lib/never_bounce/api/request/base_spec.rb +6 -0
- data/spec/lib/never_bounce/api/request/jobs_create_spec.rb +89 -0
- data/spec/lib/never_bounce/api/request/jobs_delete_spec.rb +31 -0
- data/spec/lib/never_bounce/api/request/jobs_download_spec.rb +31 -0
- data/spec/lib/never_bounce/api/request/jobs_parse_spec.rb +44 -0
- data/spec/lib/never_bounce/api/request/jobs_results_spec.rb +42 -0
- data/spec/lib/never_bounce/api/request/jobs_search_spec.rb +40 -0
- data/spec/lib/never_bounce/api/request/jobs_start_spec.rb +44 -0
- data/spec/lib/never_bounce/api/request/jobs_status_spec.rb +31 -0
- data/spec/lib/never_bounce/api/request/single_check_spec.rb +44 -0
- data/spec/lib/never_bounce/api/response/account_info/job_counts_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/account_info_spec.rb +9 -0
- data/spec/lib/never_bounce/api/response/address_info_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/base_spec.rb +6 -0
- data/spec/lib/never_bounce/api/response/container_spec.rb +142 -0
- data/spec/lib/never_bounce/api/response/credits_info/base_spec.rb +31 -0
- data/spec/lib/never_bounce/api/response/credits_info/monthly_spec.rb +11 -0
- data/spec/lib/never_bounce/api/response/credits_info/paid_spec.rb +11 -0
- data/spec/lib/never_bounce/api/response/feature/job_status_fields/total_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/feature/job_status_fields_spec.rb +42 -0
- data/spec/lib/never_bounce/api/response/jobs_create_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_delete_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_download_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_parse_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_item_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_query_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_results_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_query_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_result_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_search_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_start_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/jobs_status_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/single_check_spec.rb +7 -0
- data/spec/lib/never_bounce/api/response/spec_helper.rb +8 -0
- data/spec/lib/never_bounce/api/session_spec.rb +140 -0
- data/spec/lib/never_bounce/api_spec.rb +8 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/spec_support/include_dir_context.rb +22 -0
- data/spec/spec_support/simplecov.rb +18 -0
- metadata +210 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
require_relative "success_message"
|
|
3
|
+
|
|
4
|
+
module NeverBounce; module API; module Response
|
|
5
|
+
class JobsResults < SuccessMessage
|
|
6
|
+
require_relative "jobs_results/item"
|
|
7
|
+
require_relative "jobs_results/query"
|
|
8
|
+
|
|
9
|
+
# @!attribute items
|
|
10
|
+
# @return [Array<Item>]
|
|
11
|
+
oattr :items, :writer
|
|
12
|
+
|
|
13
|
+
# @!attribute query
|
|
14
|
+
# @return [Query]
|
|
15
|
+
oattr :query, :writer
|
|
16
|
+
|
|
17
|
+
# @!attribute total_pages
|
|
18
|
+
# @return [Integer]
|
|
19
|
+
oattr :total_pages, :scalar, type: :integer
|
|
20
|
+
|
|
21
|
+
# @!attribute total_results
|
|
22
|
+
# @return [Integer]
|
|
23
|
+
oattr :total_results, :scalar, type: :integer
|
|
24
|
+
|
|
25
|
+
def items
|
|
26
|
+
# NOTE: I take courage to rename original response key since having class `Results::Result` is total ugliness.
|
|
27
|
+
@items ||= body_hash.fetch("results").map { |h| Item.new(body_hash: h) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def query
|
|
31
|
+
@query ||= Query.new(body_hash: body_hash.fetch("query"))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end; end; end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/api/response/address_info"
|
|
3
|
+
require "never_bounce/api/response/container"
|
|
4
|
+
|
|
5
|
+
require_relative "../jobs_results"
|
|
6
|
+
|
|
7
|
+
module NeverBounce; module API; module Response; class JobsResults
|
|
8
|
+
class Item < Container
|
|
9
|
+
# @!attribute address_info
|
|
10
|
+
# @return [AddressInfo]
|
|
11
|
+
oattr :address_info, :writer
|
|
12
|
+
|
|
13
|
+
# @!attribute data
|
|
14
|
+
# @return [Hash]
|
|
15
|
+
oattr :data, :writer
|
|
16
|
+
|
|
17
|
+
# @!attribute email
|
|
18
|
+
# @return [String]
|
|
19
|
+
oattr :email, :writer
|
|
20
|
+
|
|
21
|
+
# @!attribute flags
|
|
22
|
+
# @return [Array<String>]
|
|
23
|
+
oattr :flags, :writer
|
|
24
|
+
|
|
25
|
+
# @!attribute result
|
|
26
|
+
# @return [String]
|
|
27
|
+
oattr :result, :writer
|
|
28
|
+
|
|
29
|
+
# @!attribute suggested_correction
|
|
30
|
+
# @return [String]
|
|
31
|
+
oattr :suggested_correction, :writer
|
|
32
|
+
|
|
33
|
+
def address_info
|
|
34
|
+
@address_info = AddressInfo.new(body_hash: body_hash.fetch("verification").fetch("address_info"))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def email
|
|
38
|
+
@email ||= data["email"]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def flags
|
|
42
|
+
@flags ||= body_hash.fetch("verification").fetch("flags")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def data
|
|
46
|
+
@data ||= body_hash.fetch("data")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def result
|
|
50
|
+
@result ||= body_hash.fetch("verification").fetch("result")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def suggested_correction
|
|
54
|
+
@suggested_correction ||= body_hash.fetch("verification").fetch("suggested_correction")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end; end; end; end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# Implementation notes:
|
|
61
|
+
#
|
|
62
|
+
# * I use a more streamlined structure here, not reflecting API JSON reply structure.
|
|
63
|
+
# I skip `verification` key entirely, since it has zero value to client code.
|
|
64
|
+
# * According to API spec, `data` has varying keys based on originally uploaded CSV.
|
|
65
|
+
# * We extract some important keys like `email` from `data`. `data` stays intact.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/api/response/container"
|
|
3
|
+
|
|
4
|
+
require_relative "../jobs_results"
|
|
5
|
+
|
|
6
|
+
module NeverBounce; module API; module Response; class JobsResults
|
|
7
|
+
class Query < Container
|
|
8
|
+
# @!attribute items_per_page
|
|
9
|
+
# @return [Integer]
|
|
10
|
+
oattr :items_per_page, :scalar, type: :integer
|
|
11
|
+
|
|
12
|
+
# @!attribute job_id
|
|
13
|
+
# @return [Integer]
|
|
14
|
+
oattr :job_id, :scalar, type: :integer
|
|
15
|
+
|
|
16
|
+
# @!attribute page
|
|
17
|
+
# @return [Integer]
|
|
18
|
+
oattr :page, :scalar, type: :integer
|
|
19
|
+
end
|
|
20
|
+
end; end; end; end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
require_relative "success_message"
|
|
3
|
+
|
|
4
|
+
module NeverBounce; module API; module Response
|
|
5
|
+
class JobsSearch < SuccessMessage
|
|
6
|
+
require_relative "jobs_search/result"
|
|
7
|
+
require_relative "jobs_search/query"
|
|
8
|
+
|
|
9
|
+
# @!attribute query
|
|
10
|
+
# @return [Query]
|
|
11
|
+
oattr :query, :writer
|
|
12
|
+
|
|
13
|
+
# @!attribute results
|
|
14
|
+
# @return [Array<Result>]
|
|
15
|
+
oattr :results, :writer
|
|
16
|
+
|
|
17
|
+
# @!attribute total_pages
|
|
18
|
+
# @return [Integer]
|
|
19
|
+
oattr :total_pages, :scalar, type: :integer
|
|
20
|
+
|
|
21
|
+
# @!attribute total_results
|
|
22
|
+
# @return [Integer]
|
|
23
|
+
oattr :total_results, :scalar, type: :integer
|
|
24
|
+
|
|
25
|
+
def query
|
|
26
|
+
@query ||= Query.new(body_hash: body_hash.fetch("query"))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def results
|
|
30
|
+
@results ||= body_hash.fetch("results").map { |h| Result.new(body_hash: h) }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end; end; end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/api/response/container"
|
|
3
|
+
|
|
4
|
+
require_relative "../jobs_search"
|
|
5
|
+
|
|
6
|
+
module NeverBounce; module API; module Response; class JobsSearch
|
|
7
|
+
class Query < Container
|
|
8
|
+
# @!attribute items_per_page
|
|
9
|
+
# @return [Integer]
|
|
10
|
+
oattr :items_per_page, :scalar, type: :integer
|
|
11
|
+
|
|
12
|
+
# @!attribute page
|
|
13
|
+
# @return [Integer]
|
|
14
|
+
oattr :page, :scalar, type: :integer
|
|
15
|
+
end
|
|
16
|
+
end; end; end; end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/api/response/container"
|
|
3
|
+
require "never_bounce/api/response/feature/job_status_fields"
|
|
4
|
+
|
|
5
|
+
require_relative "../jobs_search"
|
|
6
|
+
|
|
7
|
+
module NeverBounce; module API; module Response; class JobsSearch
|
|
8
|
+
# Bulk job status.
|
|
9
|
+
# @see Response::Feature::JobStatusFields
|
|
10
|
+
class Result < Container
|
|
11
|
+
Response::Feature::JobStatusFields.load(self)
|
|
12
|
+
end
|
|
13
|
+
end; end; end; end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/api/response/feature/job_status_fields"
|
|
3
|
+
|
|
4
|
+
require_relative "success_message"
|
|
5
|
+
|
|
6
|
+
module NeverBounce; module API; module Response
|
|
7
|
+
# @see Feature::JobStatusFields
|
|
8
|
+
class JobsStatus < SuccessMessage
|
|
9
|
+
Response::Feature::JobStatusFields.load(self)
|
|
10
|
+
end
|
|
11
|
+
end; end; end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
require_relative "container"
|
|
3
|
+
|
|
4
|
+
module NeverBounce; module API; module Response
|
|
5
|
+
# A response message from the server.
|
|
6
|
+
# A top-level container with a few extra features.
|
|
7
|
+
class Message < Container
|
|
8
|
+
# <tt>true</tt> if this an error message.
|
|
9
|
+
# @see #success?
|
|
10
|
+
def error?
|
|
11
|
+
!success?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# An alias to {#success?}.
|
|
15
|
+
def ok?
|
|
16
|
+
success?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# <tt>true</tt> if this is a success message.
|
|
20
|
+
# @abstract
|
|
21
|
+
def success?
|
|
22
|
+
raise NotImplementedError, "Redefine `success?` in your class: #{self.class}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end; end; end
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Implementation notes:
|
|
29
|
+
#
|
|
30
|
+
# * `ErrorMessage` and `SuccessMessage` are ancestors of this class.
|
|
31
|
+
# I don't go for more correct `Message::Error` since I want to keep this sub-hierarchy flat.
|
|
32
|
+
# There aren't going to be a big family of `*Message`, so simplified suffix grouping should be okay.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/api/response/address_info"
|
|
3
|
+
require "never_bounce/api/response/credits_info/monthly"
|
|
4
|
+
require "never_bounce/api/response/credits_info/paid"
|
|
5
|
+
|
|
6
|
+
require_relative "success_message"
|
|
7
|
+
|
|
8
|
+
module NeverBounce; module API; module Response;
|
|
9
|
+
class SingleCheck < SuccessMessage
|
|
10
|
+
# @!attribute flags
|
|
11
|
+
# @return [String]
|
|
12
|
+
oattr :flags, :scalar
|
|
13
|
+
|
|
14
|
+
# @!attribute result
|
|
15
|
+
# @return [String]
|
|
16
|
+
oattr :result, :scalar
|
|
17
|
+
|
|
18
|
+
# @!attribute suggested_correction
|
|
19
|
+
# @return [String]
|
|
20
|
+
oattr :suggested_correction, :scalar
|
|
21
|
+
|
|
22
|
+
# @!attribute address_info
|
|
23
|
+
# @return [AddressInfo]
|
|
24
|
+
# @return [nil]
|
|
25
|
+
oattr :address_info, :writer
|
|
26
|
+
|
|
27
|
+
# @!attribute credits_info
|
|
28
|
+
# @return [CreditsInfo::Monthly]
|
|
29
|
+
# @return [CreditsInfo::Paid]
|
|
30
|
+
# @return [nil]
|
|
31
|
+
oattr :credits_info, :writer
|
|
32
|
+
|
|
33
|
+
def address_info
|
|
34
|
+
igetset(:address_info) do
|
|
35
|
+
if body_hash.has_key?(k = "address_info")
|
|
36
|
+
Response::AddressInfo.new(body_hash: body_hash.fetch(k))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# <tt>true</tt> if {#address_info} is present.
|
|
42
|
+
def address_info?
|
|
43
|
+
!address_info.nil?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def credits_info
|
|
47
|
+
igetset(:credits_info) do
|
|
48
|
+
if (body_hash.has_key?(k = "credits_info"))
|
|
49
|
+
h = body_hash.fetch(k)
|
|
50
|
+
klass = if h.has_key? "monthly_api_usage"
|
|
51
|
+
CreditsInfo::Monthly
|
|
52
|
+
elsif h.has_key? "paid_credits_remaining"
|
|
53
|
+
CreditsInfo::Paid
|
|
54
|
+
else
|
|
55
|
+
raise "Unknown `credits_info`: #{h.inspect}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
klass.new(h)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# <tt>true</tt> if {#credits_info} is present.
|
|
64
|
+
def credits_info?
|
|
65
|
+
!credits_info.nil?
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end; end; end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
require_relative "message"
|
|
3
|
+
|
|
4
|
+
module NeverBounce; module API; module Response
|
|
5
|
+
# A message having <tt>status</tt> and <tt>execution_time</tt>.
|
|
6
|
+
class StatusMessage < Message
|
|
7
|
+
# @!attribute execution_time
|
|
8
|
+
# Request execution time in milliseconds.
|
|
9
|
+
# @return [Integer]
|
|
10
|
+
oattr :execution_time, :scalar, type: :integer
|
|
11
|
+
|
|
12
|
+
# @!attribute status
|
|
13
|
+
# Status mnemo as returned by the server.
|
|
14
|
+
# @return [String]
|
|
15
|
+
oattr :status, :scalar
|
|
16
|
+
end
|
|
17
|
+
end; end; end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
|
|
2
|
+
require "httparty"
|
|
3
|
+
|
|
4
|
+
require "never_bounce/api/feature/basic_initialize"
|
|
5
|
+
require "never_bounce/api/feature/igetset"
|
|
6
|
+
require "never_bounce/api/feature/require_attr"
|
|
7
|
+
|
|
8
|
+
module NeverBounce; module API
|
|
9
|
+
# A single request-response session (server dialog).
|
|
10
|
+
# @see API::Feature::BasicInitialize
|
|
11
|
+
# @see API::Feature::Igetset
|
|
12
|
+
# @see API::Feature::RequireAttr
|
|
13
|
+
class Session
|
|
14
|
+
API::Feature::BasicInitialize.load(self)
|
|
15
|
+
API::Feature::Igetset.load(self)
|
|
16
|
+
API::Feature::RequireAttr.load(self)
|
|
17
|
+
|
|
18
|
+
attr_writer :httparty, :response, :robj_hash_preview, :robj_klass_and_attrs, :server_content_type, :server_obj, :server_ok, :server_raw
|
|
19
|
+
|
|
20
|
+
# An instance of <tt>Request::Base</tt> successor.
|
|
21
|
+
# @return [Object]
|
|
22
|
+
attr_accessor :request
|
|
23
|
+
|
|
24
|
+
# HTTParty module. Default is <tt>HTTParty</tt>.
|
|
25
|
+
# @!attribute httparty
|
|
26
|
+
# @return [Module]
|
|
27
|
+
def httparty
|
|
28
|
+
@httparty ||= HTTParty
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Meaningful response object.
|
|
32
|
+
# @!attribute response
|
|
33
|
+
# @return [Response::Message]
|
|
34
|
+
# @return [Response::ErrorMessage]
|
|
35
|
+
def response
|
|
36
|
+
@response ||= begin
|
|
37
|
+
klass, attrs = require_attr(:robj_klass_and_attrs)
|
|
38
|
+
klass.new(attrs)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Render response object class and constructor attributes based on peeking in the data.
|
|
43
|
+
# @!attribute robj_klass_and_attrs
|
|
44
|
+
# @return [Array<Class,Hash>]
|
|
45
|
+
def robj_klass_and_attrs
|
|
46
|
+
@robj_klass_and_attrs ||= begin
|
|
47
|
+
if (h = robj_hash_preview).is_a? Hash
|
|
48
|
+
# Determine response class based on API convention.
|
|
49
|
+
begin
|
|
50
|
+
[
|
|
51
|
+
h.fetch("status") == "success" ? request.class.response_klass : Response::ErrorMessage,
|
|
52
|
+
body_hash: robj_hash_preview,
|
|
53
|
+
]
|
|
54
|
+
rescue KeyError
|
|
55
|
+
raise FormatError, "Key 'status' not found: #{h.inspect}"
|
|
56
|
+
end
|
|
57
|
+
elsif robj_hash_preview == false
|
|
58
|
+
# Let response class handle it on its own.
|
|
59
|
+
[
|
|
60
|
+
request.class.response_klass,
|
|
61
|
+
raw: server_raw,
|
|
62
|
+
]
|
|
63
|
+
else
|
|
64
|
+
raise AttributeError, "Unknown `robj_hash_preview`: #{robj_hash_preview.inspect}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Response body, opportunistically JSON-parsed based on <tt>server_content_type</tt>.
|
|
70
|
+
# @return [Hash]
|
|
71
|
+
# @return [false] If <tt>server_content_type</tt> isn't a JSON.
|
|
72
|
+
def robj_hash_preview
|
|
73
|
+
igetset(:robj_hash_preview) do
|
|
74
|
+
case require_attr(:server_content_type)
|
|
75
|
+
when "application/json"
|
|
76
|
+
begin
|
|
77
|
+
JSON.parse(server_raw)
|
|
78
|
+
rescue JSON::ParserError => e
|
|
79
|
+
raise FormatError, "#{e.class}: #{e.message}"
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Server response code. Default is <tt>server_obj.code</tt>.
|
|
88
|
+
# @!attribute server_code
|
|
89
|
+
# @return [Integer]
|
|
90
|
+
def server_code
|
|
91
|
+
@server_code ||= require_attr(:server_obj).code
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Server response content type. Default is <tt>server_obj.content_type</tt>.
|
|
95
|
+
# @!attribute server_content_type
|
|
96
|
+
# @return [String]
|
|
97
|
+
def server_content_type
|
|
98
|
+
@server_content_type ||= begin
|
|
99
|
+
require_attr(:server_obj).content_type
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Make a request, return server response object from HTTParty.
|
|
104
|
+
# @return [Object] An <tt>HTTParty::Response</tt>.
|
|
105
|
+
def server_obj
|
|
106
|
+
@server_obj ||= begin
|
|
107
|
+
httparty.send(*require_attr(:request).to_httparty)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# <tt>true</tt> if response is an OK response.
|
|
112
|
+
# @!attribute server_ok
|
|
113
|
+
# @return [Boolean]
|
|
114
|
+
def server_ok
|
|
115
|
+
igetset(:server_ok) { require_attr(:server_obj).ok? }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
alias_method :server_ok?, :server_ok
|
|
119
|
+
|
|
120
|
+
# Raw server response body. Default is <tt>server_obj.body</tt>.
|
|
121
|
+
# @!attribute server_raw
|
|
122
|
+
# @return [String]
|
|
123
|
+
def server_raw
|
|
124
|
+
@server_raw ||= begin
|
|
125
|
+
raise RequestError, "Code not OK: #{server_code}" if not server_ok?
|
|
126
|
+
server_obj.body
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end; end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Implementation notes:
|
|
134
|
+
#
|
|
135
|
+
# * `server_*` methods deal with stage 1, received directly from the server via HTTParty.
|
|
136
|
+
# `robj_*` methods deal with stage 2, converting raw data into response object.
|
|
137
|
+
# Everything `response*` relates to meaningful response object.
|
|
138
|
+
# * Response classes can natively parse their raw content to JSON, and that is right.
|
|
139
|
+
# BUT we also use JSON parsing here, for the purpose of detecting success/error response class.
|
|
140
|
+
# * We inherit response "OK?" condition from HTTParty, which inherits it from `net/http`.
|
|
141
|
+
# `ok?` maps to `Net::HTTPOK` class equality check.
|