neverbounce-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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