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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +8 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +1161 -0
  6. data/.travis.yml +27 -0
  7. data/.yardopts +2 -0
  8. data/Gemfile +17 -0
  9. data/Gemfile.lock +48 -0
  10. data/LICENSE +9 -0
  11. data/README.md +235 -0
  12. data/lib/never_bounce/api/client.rb +259 -0
  13. data/lib/never_bounce/api/error.rb +21 -0
  14. data/lib/never_bounce/api/feature/basic_initialize.rb +20 -0
  15. data/lib/never_bounce/api/feature/eigencache.rb +48 -0
  16. data/lib/never_bounce/api/feature/igetset.rb +41 -0
  17. data/lib/never_bounce/api/feature/oattrs.rb +100 -0
  18. data/lib/never_bounce/api/feature/require_attr.rb +27 -0
  19. data/lib/never_bounce/api/request/account_info.rb +29 -0
  20. data/lib/never_bounce/api/request/base.rb +102 -0
  21. data/lib/never_bounce/api/request/jobs_create.rb +90 -0
  22. data/lib/never_bounce/api/request/jobs_delete.rb +34 -0
  23. data/lib/never_bounce/api/request/jobs_download.rb +34 -0
  24. data/lib/never_bounce/api/request/jobs_parse.rb +55 -0
  25. data/lib/never_bounce/api/request/jobs_results.rb +53 -0
  26. data/lib/never_bounce/api/request/jobs_search.rb +57 -0
  27. data/lib/never_bounce/api/request/jobs_start.rb +47 -0
  28. data/lib/never_bounce/api/request/jobs_status.rb +37 -0
  29. data/lib/never_bounce/api/request/single_check.rb +67 -0
  30. data/lib/never_bounce/api/response/account_info.rb +39 -0
  31. data/lib/never_bounce/api/response/account_info/job_counts.rb +26 -0
  32. data/lib/never_bounce/api/response/address_info.rb +43 -0
  33. data/lib/never_bounce/api/response/base.rb +15 -0
  34. data/lib/never_bounce/api/response/container.rb +126 -0
  35. data/lib/never_bounce/api/response/credits_info/base.rb +30 -0
  36. data/lib/never_bounce/api/response/credits_info/monthly.rb +16 -0
  37. data/lib/never_bounce/api/response/credits_info/paid.rb +20 -0
  38. data/lib/never_bounce/api/response/error_message.rb +17 -0
  39. data/lib/never_bounce/api/response/feature/job_status_fields.rb +68 -0
  40. data/lib/never_bounce/api/response/feature/job_status_fields/total.rb +46 -0
  41. data/lib/never_bounce/api/response/jobs_create.rb +10 -0
  42. data/lib/never_bounce/api/response/jobs_delete.rb +8 -0
  43. data/lib/never_bounce/api/response/jobs_download.rb +19 -0
  44. data/lib/never_bounce/api/response/jobs_parse.rb +10 -0
  45. data/lib/never_bounce/api/response/jobs_results.rb +34 -0
  46. data/lib/never_bounce/api/response/jobs_results/item.rb +65 -0
  47. data/lib/never_bounce/api/response/jobs_results/query.rb +20 -0
  48. data/lib/never_bounce/api/response/jobs_search.rb +33 -0
  49. data/lib/never_bounce/api/response/jobs_search/query.rb +16 -0
  50. data/lib/never_bounce/api/response/jobs_search/result.rb +13 -0
  51. data/lib/never_bounce/api/response/jobs_start.rb +10 -0
  52. data/lib/never_bounce/api/response/jobs_status.rb +11 -0
  53. data/lib/never_bounce/api/response/message.rb +32 -0
  54. data/lib/never_bounce/api/response/single_check.rb +68 -0
  55. data/lib/never_bounce/api/response/status_message.rb +17 -0
  56. data/lib/never_bounce/api/response/success_message.rb +12 -0
  57. data/lib/never_bounce/api/session.rb +141 -0
  58. data/lib/never_bounce/api/version.rb +4 -0
  59. data/lib/neverbounce-api.rb +4 -0
  60. data/lib/neverbounce.rb +3 -0
  61. data/neverbounce-api.gemspec +20 -0
  62. data/spec/lib/never_bounce/api/client_spec.rb +199 -0
  63. data/spec/lib/never_bounce/api/feature/basic_initialize_spec.rb +25 -0
  64. data/spec/lib/never_bounce/api/feature/eigencache_spec.rb +28 -0
  65. data/spec/lib/never_bounce/api/feature/igetset_spec.rb +45 -0
  66. data/spec/lib/never_bounce/api/feature/oattrs_spec.rb +72 -0
  67. data/spec/lib/never_bounce/api/feature/require_attr_spec.rb +25 -0
  68. data/spec/lib/never_bounce/api/request/account_info_spec.rb +29 -0
  69. data/spec/lib/never_bounce/api/request/base_spec.rb +6 -0
  70. data/spec/lib/never_bounce/api/request/jobs_create_spec.rb +89 -0
  71. data/spec/lib/never_bounce/api/request/jobs_delete_spec.rb +31 -0
  72. data/spec/lib/never_bounce/api/request/jobs_download_spec.rb +31 -0
  73. data/spec/lib/never_bounce/api/request/jobs_parse_spec.rb +44 -0
  74. data/spec/lib/never_bounce/api/request/jobs_results_spec.rb +42 -0
  75. data/spec/lib/never_bounce/api/request/jobs_search_spec.rb +40 -0
  76. data/spec/lib/never_bounce/api/request/jobs_start_spec.rb +44 -0
  77. data/spec/lib/never_bounce/api/request/jobs_status_spec.rb +31 -0
  78. data/spec/lib/never_bounce/api/request/single_check_spec.rb +44 -0
  79. data/spec/lib/never_bounce/api/response/account_info/job_counts_spec.rb +7 -0
  80. data/spec/lib/never_bounce/api/response/account_info_spec.rb +9 -0
  81. data/spec/lib/never_bounce/api/response/address_info_spec.rb +7 -0
  82. data/spec/lib/never_bounce/api/response/base_spec.rb +6 -0
  83. data/spec/lib/never_bounce/api/response/container_spec.rb +142 -0
  84. data/spec/lib/never_bounce/api/response/credits_info/base_spec.rb +31 -0
  85. data/spec/lib/never_bounce/api/response/credits_info/monthly_spec.rb +11 -0
  86. data/spec/lib/never_bounce/api/response/credits_info/paid_spec.rb +11 -0
  87. data/spec/lib/never_bounce/api/response/feature/job_status_fields/total_spec.rb +7 -0
  88. data/spec/lib/never_bounce/api/response/feature/job_status_fields_spec.rb +42 -0
  89. data/spec/lib/never_bounce/api/response/jobs_create_spec.rb +7 -0
  90. data/spec/lib/never_bounce/api/response/jobs_delete_spec.rb +7 -0
  91. data/spec/lib/never_bounce/api/response/jobs_download_spec.rb +7 -0
  92. data/spec/lib/never_bounce/api/response/jobs_parse_spec.rb +7 -0
  93. data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_item_spec.rb +7 -0
  94. data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_query_spec.rb +7 -0
  95. data/spec/lib/never_bounce/api/response/jobs_results_spec.rb +7 -0
  96. data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_query_spec.rb +7 -0
  97. data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_result_spec.rb +7 -0
  98. data/spec/lib/never_bounce/api/response/jobs_search_spec.rb +7 -0
  99. data/spec/lib/never_bounce/api/response/jobs_start_spec.rb +7 -0
  100. data/spec/lib/never_bounce/api/response/jobs_status_spec.rb +7 -0
  101. data/spec/lib/never_bounce/api/response/single_check_spec.rb +7 -0
  102. data/spec/lib/never_bounce/api/response/spec_helper.rb +8 -0
  103. data/spec/lib/never_bounce/api/session_spec.rb +140 -0
  104. data/spec/lib/never_bounce/api_spec.rb +8 -0
  105. data/spec/spec_helper.rb +49 -0
  106. data/spec/spec_support/include_dir_context.rb +22 -0
  107. data/spec/spec_support/simplecov.rb +18 -0
  108. metadata +210 -0
@@ -0,0 +1,4 @@
1
+
2
+ module NeverBounce; module API
3
+ VERSION = "1.0.0"
4
+ end; end
@@ -0,0 +1,4 @@
1
+
2
+ # Main gem require. Load all libraries.
3
+ # Upon a convention, the require named same as gem does all loading job.
4
+ Dir[File.join(__dir__, "**/*.rb")].each { |fn| require fn }
@@ -0,0 +1,3 @@
1
+
2
+ # A "convenience" require for doc examples etc.
3
+ require_relative "neverbounce-api"
@@ -0,0 +1,20 @@
1
+
2
+ require_relative "lib/never_bounce/api/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "neverbounce-api"
6
+ s.summary = "The official NeverBounce API library for Ruby"
7
+
8
+ s.authors = ["NeverBounce"]
9
+ s.email = ["support@neverbounce.com"]
10
+ s.homepage = "https://neverbounce.com"
11
+ s.license = "MIT"
12
+ s.version = NeverBounce::API::VERSION
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.required_ruby_version = ">= 2.0.0"
16
+ s.require_paths = ["lib"]
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+
19
+ s.add_dependency("httparty", "~> 0.15")
20
+ end
@@ -0,0 +1,199 @@
1
+
2
+ module NeverBounce; module API
3
+ describe Client do
4
+ include_dir_context __dir__
5
+
6
+ it_behaves_like "instantiatable"
7
+
8
+ describe "attributes" do
9
+ describe "#api_key" do
10
+ it "generally works" do
11
+ r = newo
12
+ expect { r.api_key }.to raise_error(AttributeError, "Attribute must be set: api_key")
13
+ r.api_key = "api_key"
14
+ expect(r.api_key).to eq "api_key"
15
+ end
16
+ end
17
+ end # describe "attributes"
18
+
19
+ describe "requests" do
20
+ let(:klass) do
21
+ Class.new(described_class) do
22
+ def response_to(request)
23
+ # Short-circuit with a signature. The signature ensures we get control here.
24
+ [:response_to, request]
25
+ end
26
+ end
27
+ end
28
+
29
+ # Build a "good" object with typically enough attributes to work.
30
+ def goodo(attrs = {})
31
+ klass.new({
32
+ api_key: "api_key",
33
+ }.merge(attrs))
34
+ end
35
+
36
+ describe "#account_info" do
37
+ it "generally works" do
38
+ sc = goodo.account_info
39
+ expect(sc).to be_a Array
40
+ signature, request = sc
41
+ expect(signature).to eq :response_to
42
+ expect(request).to be_a Request::AccountInfo
43
+ expect(request.to_httparty).to be_a Array
44
+ end
45
+ end
46
+
47
+ describe "#jobs_create" do
48
+ let(:m) { :jobs_create }
49
+ it "generally works" do
50
+ client = goodo
51
+ expect { client.send(m) }.to raise_error(ArgumentError, "Input not given, use `remote_input` or `supplied_input`")
52
+
53
+ client = goodo
54
+ expect { client.send(m, remote_input: "remote", supplied_input: "supplied") }.to raise_error(ArgumentError, "`remote_input` and `supplied_input` can't both be given")
55
+
56
+ # NOTE: A few scenarios, redundantly testing as much attributes as possible.
57
+
58
+ sc = client.send(m, remote_input: "remote_input")
59
+ expect(sc).to be_a Array
60
+ signature, request = sc
61
+ expect(signature).to eq :response_to
62
+ expect(request).to be_a Request::JobsCreate
63
+ expect(request.to_httparty).to be_a Array
64
+ expect(request.input).to eq "remote_input"
65
+ expect(request.input_location).to eq "remote_url"
66
+ expect(request.filename).to match /^\d{8}-\d{6}\.csv$/
67
+
68
+ sc = client.send(m, supplied_input: [["email", "name"]])
69
+ expect(sc).to be_a Array
70
+ signature, request = sc
71
+ expect(signature).to eq :response_to
72
+ expect(request).to be_a Request::JobsCreate
73
+ expect(request.to_httparty).to be_a Array
74
+ expect(request.input).to eq [["email", "name"]]
75
+ expect(request.input_location).to eq "supplied"
76
+ expect(request.filename).to match /^\d{8}-\d{6}\.csv$/
77
+
78
+ sc = client.send(m, auto_parse: true, auto_start: true, filename: "filename", run_sample: true, supplied_input: [["email", "name"]])
79
+ expect(sc).to be_a Array
80
+ signature, request = sc
81
+ expect(signature).to eq :response_to
82
+ expect(request).to be_a Request::JobsCreate
83
+ expect(request.to_httparty).to be_a Array
84
+ expect(request.auto_parse).to be true
85
+ expect(request.auto_start).to be true
86
+ expect(request.input).to eq [["email", "name"]]
87
+ expect(request.input_location).to eq "supplied"
88
+ expect(request.filename).to eq "filename"
89
+ expect(request.run_sample).to be true
90
+ end
91
+ end
92
+
93
+ describe "#jobs_delete" do
94
+ it "generally works" do
95
+ sc = goodo.jobs_delete(job_id: 12)
96
+ expect(sc).to be_a Array
97
+ signature, request = sc
98
+ expect(signature).to eq :response_to
99
+ expect(request).to be_a Request::JobsDelete
100
+ expect(request.to_httparty).to be_a Array
101
+ expect(request.job_id).to eq 12
102
+ end
103
+ end
104
+
105
+ describe "#jobs_download" do
106
+ it "generally works" do
107
+ sc = goodo.jobs_download(job_id: 12)
108
+ expect(sc).to be_a Array
109
+ signature, request = sc
110
+ expect(signature).to eq :response_to
111
+ expect(request).to be_a Request::JobsDownload
112
+ expect(request.to_httparty).to be_a Array
113
+ expect(request.job_id).to eq 12
114
+ end
115
+ end
116
+
117
+ describe "#jobs_parse" do
118
+ it "generally works" do
119
+ sc = goodo.jobs_parse(auto_start: true, job_id: 12)
120
+ expect(sc).to be_a Array
121
+ signature, request = sc
122
+ expect(signature).to eq :response_to
123
+ expect(request).to be_a Request::JobsParse
124
+ expect(request.to_httparty).to be_a Array
125
+ expect(request.auto_start).to be true
126
+ expect(request.job_id).to eq 12
127
+ end
128
+ end
129
+
130
+ describe "#jobs_results" do
131
+ it "generally works" do
132
+ sc = goodo.jobs_results(job_id: 12, page: 34, per_page: 56)
133
+ expect(sc).to be_a Array
134
+ signature, request = sc
135
+ expect(signature).to eq :response_to
136
+ expect(request).to be_a Request::JobsResults
137
+ expect(request.to_httparty).to be_a Array
138
+ expect(request.job_id).to eq 12
139
+ expect(request.page).to eq 34
140
+ expect(request.per_page).to eq 56
141
+ end
142
+ end
143
+
144
+ describe "#jobs_search" do
145
+ it "generally works" do
146
+ sc = goodo.jobs_search(job_id: 12, page: 34, per_page: 56)
147
+ expect(sc).to be_a Array
148
+ signature, request = sc
149
+ expect(signature).to eq :response_to
150
+ expect(request).to be_a Request::JobsSearch
151
+ expect(request.to_httparty).to be_a Array
152
+ expect(request.job_id).to eq 12
153
+ expect(request.page).to eq 34
154
+ expect(request.per_page).to eq 56
155
+ end
156
+ end
157
+
158
+ describe "#jobs_start" do
159
+ it "generally works" do
160
+ sc = goodo.jobs_start(job_id: 12, run_sample: true)
161
+ expect(sc).to be_a Array
162
+ signature, request = sc
163
+ expect(signature).to eq :response_to
164
+ expect(request).to be_a Request::JobsStart
165
+ expect(request.to_httparty).to be_a Array
166
+ expect(request.job_id).to eq 12
167
+ expect(request.run_sample).to be true
168
+ end
169
+ end
170
+
171
+ describe "#jobs_status" do
172
+ it "generally works" do
173
+ sc = goodo.jobs_status(job_id: 12)
174
+ expect(sc).to be_a Array
175
+ signature, request = sc
176
+ expect(signature).to eq :response_to
177
+ expect(request).to be_a Request::JobsStatus
178
+ expect(request.to_httparty).to be_a Array
179
+ expect(request.job_id).to eq 12
180
+ end
181
+ end
182
+
183
+ describe "#single_check" do
184
+ it "generally works" do
185
+ sc = goodo.single_check(address_info: true, credits_info: true, email: "alice@isp.com", timeout: 12)
186
+ expect(sc).to be_a Array
187
+ signature, request = sc
188
+ expect(signature).to eq :response_to
189
+ expect(request).to be_a Request::SingleCheck
190
+ expect(request.to_httparty).to be_a Array
191
+ expect(request.address_info).to be true
192
+ expect(request.credits_info).to be true
193
+ expect(request.email).to eq "alice@isp.com"
194
+ expect(request.timeout).to eq 12
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end; end
@@ -0,0 +1,25 @@
1
+
2
+ describe NeverBounce::API::Feature::BasicInitialize do
3
+ let(:klass) do
4
+ feature = described_class
5
+ Class.new do
6
+ feature.load(self)
7
+
8
+ attr_accessor :a, :b
9
+
10
+ private
11
+
12
+ def c=(value)
13
+ @c = value
14
+ end
15
+ end
16
+ end
17
+
18
+ it "generally works" do
19
+ r = klass.new(a: 12, b: "34")
20
+ expect(r.a).to eq 12
21
+ expect(r.b).to eq "34"
22
+
23
+ expect { klass.new(c: 12) }.to raise_error(NoMethodError, /\bprivate method `c='/)
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+
2
+ describe NeverBounce::API::Feature::Eigencache do
3
+ let(:klass) do
4
+ feature = described_class
5
+
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ def a
10
+ _cache[:a] ||= "a"
11
+ end
12
+
13
+ def a=(value)
14
+ _cache[:a] = value
15
+ end
16
+ end
17
+ end
18
+
19
+ it "generally works" do
20
+ r = klass.new
21
+ expect(r.a).to eq "a"
22
+ expect(r.inspect).not_to match "@a"
23
+
24
+ r.a = "a+"
25
+ expect(r.a).to eq "a+"
26
+ expect(r.inspect).not_to match "@a"
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+
2
+ describe NeverBounce::API::Feature::Igetset do
3
+ let(:klass) do
4
+ feature = described_class
5
+
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ attr_writer :is_a, :is_b
10
+
11
+ def is_a
12
+ igetset(:is_a) do
13
+ seq << :is_a_block
14
+ false
15
+ end
16
+ end
17
+
18
+ def is_b
19
+ igetset(:is_a) do
20
+ seq << :is_b_block
21
+ nil
22
+ end
23
+ end
24
+
25
+ # Store call sequence here.
26
+ def seq
27
+ @log ||= []
28
+ end
29
+ end
30
+ end
31
+
32
+ it "generally works" do
33
+ r = klass.new
34
+ expect(r.seq).to eq []
35
+ expect(r.is_a).to be false
36
+ expect(r.is_a).to be false
37
+ expect(r.seq).to eq [:is_a_block] # Block evaluation is logged once.
38
+
39
+ r = klass.new
40
+ expect(r.seq).to eq []
41
+ expect(r.is_b).to be nil
42
+ expect(r.is_b).to be nil
43
+ expect(r.seq).to eq [:is_b_block]
44
+ end
45
+ end
@@ -0,0 +1,72 @@
1
+
2
+ describe NeverBounce::API::Feature::Oattrs do
3
+ let(:parent_klass) do
4
+ feature = described_class
5
+
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ oattr :parent_a, :writer
10
+
11
+ def parent_a
12
+ @parent_a ||= "parent_a"
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:klass) do
18
+ feature = described_class
19
+
20
+ Class.new(parent_klass) do
21
+ feature.load(self)
22
+
23
+ oattr :a, :writer
24
+ oattr :b, :custom
25
+
26
+ def a
27
+ @a ||= "a"
28
+ end
29
+
30
+ def b
31
+ @b ||= "b"
32
+ end
33
+
34
+ def b=(value)
35
+ @b = value
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "attribute access" do
41
+ include_dir_context __dir__
42
+
43
+ it "generally works" do
44
+ r = parent_klass.new
45
+ expect(r.parent_a).to eq "parent_a"
46
+ r.parent_a = "yo"
47
+ expect(r.parent_a).to eq "yo"
48
+
49
+ r = klass.new
50
+ expect(r.b).to eq "b"
51
+ r.b = "yo"
52
+ expect(r.b).to eq "yo"
53
+ end
54
+ end
55
+
56
+ describe ".attrs" do
57
+ it "generally works" do
58
+ expect(parent_klass.oattrs).to eq [:parent_a]
59
+ expect(klass.oattrs).to eq [:parent_a, :a, :b]
60
+ end
61
+ end
62
+
63
+ describe "#touch" do
64
+ it "generally works" do
65
+ r = klass.new
66
+ expect(r.touch).to eq r
67
+ expect(r.inspect).to include '@parent_a="parent_a"'
68
+ expect(r.inspect).to include '@a="a"'
69
+ expect(r.inspect).to include '@b="b"'
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module NeverBounce; module API; module Feature
3
+ describe RequireAttr do
4
+ let(:klass) do
5
+ feature = described_class
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ attr_accessor :a
10
+
11
+ def b
12
+ v = require_attr :a
13
+ [v, "b"]
14
+ end
15
+ end
16
+ end
17
+
18
+ it "generally works" do
19
+ r = klass.new
20
+ expect { r.b }.to raise_error(AttributeError, "Attribute must be set: a")
21
+ r.a = "a"
22
+ expect(r.b).to eq ["a", "b"]
23
+ end
24
+ end
25
+ end; end; end
@@ -0,0 +1,29 @@
1
+
2
+ module NeverBounce; module API; module Request
3
+ describe AccountInfo do
4
+ include_dir_context __dir__
5
+
6
+ it_behaves_like "instantiatable"
7
+
8
+ describe ".response_klass" do
9
+ it { expect(described_class.response_klass).to eq Response::AccountInfo }
10
+ end
11
+
12
+ describe "#to_httparty" do
13
+ it "generally works" do
14
+ r = newo
15
+ expect { r.to_httparty }.to raise_error(AttributeError, "Attribute must be set: api_key")
16
+ r.api_key = "api_key"
17
+
18
+ res = r.to_httparty
19
+ expect(res).to be_a Array
20
+ method, url, data = res
21
+ expect(method).to eq :get
22
+ expect(url).to eq "https://api.neverbounce.com/v4/account/info"
23
+ expect(data).to include(:body, :headers)
24
+ expect(data.fetch(:body)).to eq "{\"key\":\"api_key\"}"
25
+ expect(data.fetch(:headers)).to include("Content-Type", "User-Agent")
26
+ end
27
+ end
28
+ end
29
+ end; end; end