emailable 3.0.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e7635cc0f814700d29137678bd7b53fd91f07f042ff5435b8777cc269a51fb6
4
- data.tar.gz: 808f71d2c343495ad48143230d1daa11ba59e334d75a242d25df637abd446b51
3
+ metadata.gz: 59b57b0948d78b837e5a3f82a0a0e5c5446742f57e50ebbd2c5b0817f0aabfc5
4
+ data.tar.gz: 58ce6a2ac2a9846e5daa63157014cbe0b557f4c94f30c71d9252db8e169cc12f
5
5
  SHA512:
6
- metadata.gz: 8a78fd0ab06eca710c67e2e8c01142d0a4bfdfa19f7357c9f54b7a8d53eddaf7b29f32ded7ad85145f414e95782a61dd4774d0a39821f2194f0228b9b402b6c4
7
- data.tar.gz: 99f27b66dc848815689fe3a0869b2b47a0f412c3305b910a044488b65598161a8349f690b5772d4e5181e97a4c55fa794f9593d3fa4b60bca5d5ad9a99b84530
6
+ metadata.gz: 033fe8af379957fdee488fca886d3e3ac4818a7f8587b80554424c7ec824bd9dd1ed949268bc246da1460d0afc449df640cdca8e7bf4fcfbef6790c6c5b1fb1a
7
+ data.tar.gz: 9d9ff16f6db49bc2c8791ae46be20dfdc7e5d8e58cc9b603e9e04492b85d97bc0bf48b7933ca46a1362469cba4ca923b35a4444c056699716a15417518454897
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.5
1
+ 2.7.4
data/.travis.yml CHANGED
@@ -3,5 +3,5 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.6.3
7
- before_install: gem install bundler -v 1.17.3
6
+ - 2.7.4
7
+ before_install: gem install bundler -v 2.2.15
data/Gemfile.lock CHANGED
@@ -1,10 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- emailable (3.0.0)
5
- faraday
6
- faraday_middleware
7
- net-http-persistent
4
+ emailable (3.1.1)
8
5
 
9
6
  GEM
10
7
  remote: https://rubygems.org/
@@ -22,14 +19,6 @@ GEM
22
19
  builder (3.2.3)
23
20
  coderay (1.1.2)
24
21
  concurrent-ruby (1.1.7)
25
- connection_pool (2.2.3)
26
- faraday (1.3.0)
27
- faraday-net_http (~> 1.0)
28
- multipart-post (>= 1.2, < 3)
29
- ruby2_keywords
30
- faraday-net_http (1.0.1)
31
- faraday_middleware (1.0.0)
32
- faraday (~> 1.0)
33
22
  i18n (1.8.5)
34
23
  concurrent-ruby (~> 1.0)
35
24
  method_source (0.9.2)
@@ -39,15 +28,11 @@ GEM
39
28
  builder
40
29
  minitest (>= 5.0)
41
30
  ruby-progressbar
42
- multipart-post (2.1.1)
43
- net-http-persistent (4.0.1)
44
- connection_pool (~> 2.2)
45
31
  pry (0.12.2)
46
32
  coderay (~> 1.1.0)
47
33
  method_source (~> 0.9.0)
48
34
  rake (13.0.1)
49
35
  ruby-progressbar (1.10.1)
50
- ruby2_keywords (0.0.4)
51
36
  thread_safe (0.3.6)
52
37
  tzinfo (1.2.8)
53
38
  thread_safe (~> 0.1)
@@ -67,4 +52,4 @@ DEPENDENCIES
67
52
  rake (~> 13.0)
68
53
 
69
54
  BUNDLED WITH
70
- 1.17.3
55
+ 2.1.4
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019 Emailable. https://emailable.com
3
+ Copyright (c) 2021 Emailable. https://emailable.com
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Emailable Ruby Library
2
2
 
3
- [![Build Status](https://travis-ci.com/emailable/emailable-ruby.svg)](https://travis-ci.com/emailable/emailable-ruby)
4
- [![Maintainability](https://api.codeclimate.com/v1/badges/2d74c69a9155109058a7/maintainability)](https://codeclimate.com/github/emailable/emailable-ruby/maintainability)
3
+ [![Build Status](https://app.travis-ci.com/emailable/emailable-ruby.svg)](https://app.travis-ci.com/emailable/emailable-ruby)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/e7eef54e491adec95e6d/maintainability)](https://codeclimate.com/github/emailable/emailable-ruby/maintainability)
5
5
 
6
6
  This is the official ruby wrapper for the Emailable API.
7
7
 
data/bin/console CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "emailable/ruby"
3
+ require 'bundler/setup'
4
+ require 'emailable'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
9
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
10
+ # require 'pry'
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start(__FILE__)
data/emailable.gemspec CHANGED
@@ -27,9 +27,6 @@ Gem::Specification.new do |s|
27
27
  end
28
28
  s.require_paths = ['lib']
29
29
 
30
- s.add_dependency 'faraday'
31
- s.add_dependency 'faraday_middleware'
32
- s.add_dependency 'net-http-persistent'
33
30
  s.add_development_dependency 'bundler'
34
31
  s.add_development_dependency 'rake', '~> 13.0'
35
32
  s.add_development_dependency 'pry'
@@ -4,31 +4,36 @@ module Emailable
4
4
 
5
5
  def initialize(id_or_emails, callback: nil)
6
6
  if id_or_emails.is_a?(Array)
7
+ @id = nil
7
8
  @emails = id_or_emails
8
9
  @callback = callback
9
10
  elsif id_or_emails.is_a?(String)
10
11
  @id = id_or_emails
12
+ @emails = nil
13
+ @callback = nil
11
14
  else
12
15
  raise ArgumentError, 'expected an array of emails or batch id'
13
16
  end
14
17
 
15
18
  @client = Emailable::Client.new
19
+ @status = nil
16
20
  end
17
21
 
18
- def verify
22
+ def verify(simulate: nil)
19
23
  return @id unless @id.nil?
20
24
 
21
- opts = { emails: @emails.join(','), url: @callback }
25
+ opts = { emails: @emails.join(','), url: @callback, simulate: simulate }
22
26
  response = @client.request(:post, 'batch', opts)
23
27
 
24
28
  @id = response.body['id']
25
29
  end
26
30
 
27
- def status
31
+ def status(simulate: nil, partial: nil)
28
32
  return nil unless @id
29
33
  return @status if @status
30
34
 
31
- response = @client.request(:get, 'batch', { id: @id })
35
+ body = { id: @id, simulate: simulate, partial: partial }
36
+ response = @client.request(:get, 'batch', body)
32
37
  bs = BatchStatus.new(response.body)
33
38
  @status = bs if bs.complete?
34
39
 
@@ -36,7 +41,7 @@ module Emailable
36
41
  end
37
42
 
38
43
  def complete?
39
- !status.complete?
44
+ status.complete?
40
45
  end
41
46
 
42
47
  def inspect
@@ -1,28 +1,41 @@
1
1
  module Emailable
2
2
  class Client
3
+ ERRORS = {
4
+ 400 => BadRequestError,
5
+ 401 => UnauthorizedError,
6
+ 402 => PaymentRequiredError,
7
+ 403 => ForbiddenError,
8
+ 404 => NotFoundError,
9
+ 429 => TooManyRequestsError,
10
+ 500 => InternalServerError,
11
+ 503 => ServiceUnavailableError
12
+ }.freeze
3
13
 
4
14
  def initialize
5
- @client = Faraday.new('https://api.emailable.com/v1') do |f|
6
- f.request :url_encoded
7
- f.response :json, content_type: /\bjson$/
8
- f.adapter :net_http_persistent
9
- end
15
+ @base_url = 'https://api.emailable.com/v1'
16
+ @connection = create_connection(URI(@base_url))
10
17
  end
11
18
 
12
- def request(method, endpoint, opts = {})
19
+ def request(method, endpoint, params = {})
13
20
  begin
14
- tries ||= 0
21
+ tries ||= 3
15
22
 
16
- @client.params[:api_key] = Emailable.api_key
23
+ uri = URI("#{@base_url}/#{endpoint}")
24
+ params[:api_key] = Emailable.api_key
17
25
 
18
- response =
26
+ http_response =
19
27
  if method == :get
20
- @client.get(endpoint, opts)
28
+ uri.query = URI.encode_www_form(params)
29
+ @connection.get(uri)
21
30
  elsif method == :post
22
- @client.post(endpoint, opts)
31
+ request = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json')
32
+ request.body = params.to_json
33
+ @connection.request(request)
23
34
  end
35
+
36
+ response = Response.new(http_response)
24
37
  rescue => e
25
- retry if self.class.should_retry?(e, tries)
38
+ retry if (tries -= 1) > 0 && self.class.should_retry?(e, tries)
26
39
 
27
40
  raise e
28
41
  end
@@ -30,41 +43,62 @@ module Emailable
30
43
  status = response.status
31
44
  return response if status.between?(200, 299)
32
45
 
33
- error_attributes = {
34
- message: response.body['message'],
35
- code: status
36
- }
37
- error_map = {
38
- '400' => BadRequestError,
39
- '401' => UnauthorizedError,
40
- '402' => PaymentRequiredError,
41
- '403' => ForbiddenError,
42
- '404' => NotFoundError,
43
- '429' => TooManyRequestsError,
44
- '500' => InternalServerError,
45
- '503' => ServiceUnavailableError
46
- }
47
-
48
- raise error_map[status.to_s].new(error_attributes)
46
+ raise ERRORS[status].new(response.body['message'])
47
+ end
48
+
49
+ private
50
+
51
+ def create_connection(uri)
52
+ connection = Net::HTTP.new(uri.host, uri.port)
53
+
54
+ # Time in seconds within which Net::HTTP will try to reuse an already
55
+ # open connection when issuing a new operation. Outside this window, Ruby
56
+ # will transparently close and re-open the connection without trying to
57
+ # reuse it.
58
+ #
59
+ # Ruby's default of 2 seconds is almost certainly too short. Here I've
60
+ # reused Go's default for `DefaultTransport`.
61
+ connection.keep_alive_timeout = 30
62
+
63
+ connection.open_timeout = Emailable.open_timeout
64
+ connection.read_timeout = Emailable.read_timeout
65
+ if connection.respond_to?(:write_timeout=)
66
+ connection.write_timeout = Emailable.write_timeout
67
+ end
68
+ connection.use_ssl = uri.scheme == "https"
69
+
70
+ connection
49
71
  end
50
72
 
51
73
  def self.should_retry?(error, num_retries)
52
74
  return false if num_retries >= Emailable.max_network_retries
53
75
 
54
- # Retry on timeout-related problems (either on open or read).
55
- return true if error.is_a?(Faraday::TimeoutError)
76
+ case error
77
+ when Net::OpenTimeout, Net::ReadTimeout
78
+ # Retry on timeout-related problems (either on open or read).
79
+ true
80
+ when EOFError, Errno::ECONNREFUSED, Errno::ECONNRESET,
81
+ Errno::EHOSTUNREACH, Errno::ETIMEDOUT, SocketError
82
+ # Destination refused the connection, the connection was reset, or a
83
+ # variety of other connection failures. This could occur from a single
84
+ # saturated server, so retry in case it's intermittent.
85
+ true
86
+ when Net::HTTPError
87
+ # 409 Conflict
88
+ return true if error.http_status == 409
56
89
 
57
- # Destination refused the connection, the connection was reset, or a
58
- # variety of other connection failures. This could occur from a single
59
- # saturated server, so retry in case it's intermittent.
60
- return true if error.is_a?(Faraday::ConnectionFailed)
90
+ # 429 Too Many Requests
91
+ return true if error.http_status == 429
61
92
 
62
- if error.is_a?(Faraday::ClientError) && error.response
63
- # 409 conflict
64
- return true if error.response[:status] == 409
65
- end
93
+ # 500 Internal Server Error
94
+ return true if error.http_status == 500
66
95
 
67
- false
96
+ # 503 Service Unavailable
97
+ error.http_status == 503
98
+ else
99
+ false
100
+ end
68
101
  end
102
+
69
103
  end
70
104
  end
@@ -38,7 +38,7 @@ class EmailValidator < ActiveModel::EachValidator
38
38
 
39
39
  api_options = { timeout: timeout, smtp: smtp }
40
40
  api_options[:accept_all] = true unless accept_all
41
- ev = Emailable.verify(value, api_options)
41
+ ev = Emailable.verify(value, **api_options)
42
42
 
43
43
  result_accessor = "#{attribute}_verification_result"
44
44
  if record.respond_to?(result_accessor)
@@ -0,0 +1,12 @@
1
+ module Emailable
2
+ class Error < StandardError; end
3
+ class BadRequestError < Error; end
4
+ class UnauthorizedError < Error; end
5
+ class PaymentRequiredError < Error; end
6
+ class ForbiddenError < Error; end
7
+ class NotFoundError < Error; end
8
+ class TooManyRequestsError < Error; end
9
+ class InternalServerError < Error; end
10
+ class ServiceUnavailableError < Error; end
11
+ class TimeoutError < Error; end
12
+ end
@@ -7,13 +7,21 @@ module Emailable
7
7
  end
8
8
  end
9
9
 
10
- def inspect
11
- ivars = instance_variables.map do |e|
12
- [e.to_s.delete('@'), instance_variable_get(e)]
10
+ def to_h
11
+ instance_variables.map do |e|
12
+ [e.to_s.delete('@').to_sym, instance_variable_get(e)]
13
13
  end.to_h
14
- fmtted_email = @email ? " #{@email}" : ''
15
- "#<#{self.class}:0x#{(object_id << 1).to_s(16)}#{fmtted_email}> JSON: " +
16
- JSON.pretty_generate(ivars)
14
+ end
15
+
16
+ alias_method :to_hash, :to_h
17
+
18
+ def to_json
19
+ JSON.generate(to_h)
20
+ end
21
+
22
+ def inspect
23
+ "#<#{self.class}:0x#{(object_id << 1).to_s(16)}> JSON: " +
24
+ JSON.pretty_generate(to_h)
17
25
  end
18
26
 
19
27
  end
@@ -4,7 +4,7 @@ module Emailable
4
4
  :processed, :total
5
5
 
6
6
  def complete?
7
- !emails.nil?
7
+ message.include?('completed')
8
8
  end
9
9
  end
10
10
  end
@@ -11,5 +11,10 @@ module Emailable
11
11
  end
12
12
  end
13
13
 
14
+ def inspect
15
+ "#<#{self.class}:0x#{(object_id << 1).to_s(16)}#{@email}> JSON: " +
16
+ JSON.pretty_generate(to_h)
17
+ end
18
+
14
19
  end
15
20
  end
@@ -0,0 +1,12 @@
1
+ module Emailable
2
+ class Response
3
+
4
+ attr_accessor :status, :body
5
+
6
+ def initialize(response)
7
+ @status = response.code.to_i
8
+ @body = JSON.parse(response.body)
9
+ end
10
+
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Emailable
2
- VERSION = '3.0.1'
2
+ VERSION = '3.2.0'
3
3
  end
data/lib/emailable.rb CHANGED
@@ -1,7 +1,10 @@
1
- require 'faraday'
2
- require 'faraday_middleware'
1
+ require 'net/http'
2
+ require 'json'
3
+
3
4
  require 'emailable/version'
5
+ require 'emailable/errors'
4
6
  require 'emailable/client'
7
+ require 'emailable/response'
5
8
  require 'emailable/batch'
6
9
  require 'emailable/resources/api_resource'
7
10
  require 'emailable/resources/account'
@@ -14,9 +17,13 @@ end
14
17
 
15
18
  module Emailable
16
19
  @max_network_retries = 1
20
+ @open_timeout = 30
21
+ @read_timeout = 60
22
+ @write_timeout = 30
17
23
 
18
24
  class << self
19
- attr_accessor :api_key, :max_network_retries
25
+ attr_accessor :api_key, :max_network_retries, :open_timeout, :read_timeout,
26
+ :write_timeout
20
27
  end
21
28
 
22
29
  module_function
@@ -30,9 +37,7 @@ module Emailable
30
37
  response = client.request(:get, 'verify', opts)
31
38
 
32
39
  if response.status == 249
33
- raise Emailable::TimeoutError.new(
34
- code: response.status, message: response.body
35
- )
40
+ raise Emailable::TimeoutError.new(response.body)
36
41
  else
37
42
  Verification.new(response.body)
38
43
  end
@@ -44,22 +49,4 @@ module Emailable
44
49
  Account.new(response.body)
45
50
  end
46
51
 
47
-
48
- class Error < StandardError
49
- attr_accessor :code, :message
50
-
51
- def initialize(code: nil, message: nil)
52
- @code = code
53
- @message = message
54
- end
55
- end
56
- class BadRequestError < Error; end
57
- class UnauthorizedError < Error; end
58
- class PaymentRequiredError < Error; end
59
- class ForbiddenError < Error; end
60
- class NotFoundError < Error; end
61
- class TooManyRequestsError < Error; end
62
- class InternalServerError < Error; end
63
- class ServiceUnavailableError < Error; end
64
- class TimeoutError < Error; end
65
52
  end
@@ -1,3 +1,4 @@
1
+ require 'active_model'
1
2
  require 'test_helper'
2
3
 
3
4
  class EmailValidatorTest < Minitest::Test
@@ -22,8 +22,7 @@ module Emailable
22
22
  end
23
23
 
24
24
  def test_batch_complete
25
- complete = @batch.complete?
26
- assert complete == true || complete == false
25
+ assert @batch.complete?
27
26
  end
28
27
 
29
28
  end
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ module Emailable
4
+ module Resources
5
+ class APIResourceTest < Minitest::Test
6
+ class Example < APIResource
7
+ attr_accessor :foo, :bar, :baz
8
+ end
9
+
10
+ def setup
11
+ @e = Example.new(foo: 'abc', bar: 123, baz: true)
12
+ end
13
+
14
+ def test_init
15
+ assert_equal 'abc', @e.foo
16
+ assert_equal 123, @e.bar
17
+ assert_equal true, @e.baz
18
+ end
19
+
20
+ def test_to_h
21
+ correct = {
22
+ foo: 'abc',
23
+ bar: 123,
24
+ baz: true,
25
+ }
26
+ assert_equal correct, @e.to_h
27
+ assert_equal correct, @e.to_hash
28
+ end
29
+
30
+ def test_to_json
31
+ correct = %q|{"foo":"abc","bar":123,"baz":true}|
32
+ assert_equal correct, @e.to_json
33
+ end
34
+
35
+ def test_inspect
36
+ correct = <<~STR.chomp
37
+ #<> JSON: {
38
+ "foo": "abc",
39
+ "bar": 123,
40
+ "baz": true
41
+ }
42
+ STR
43
+ output = @e.inspect.gsub(/<.*>/, '<>')
44
+ assert_equal correct, output
45
+ end
46
+ end
47
+
48
+ end
49
+ end
data/test/test_helper.rb CHANGED
@@ -1,5 +1,3 @@
1
- $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
- require 'active_model'
3
1
  require 'emailable'
4
2
 
5
3
  require 'pry'
metadata CHANGED
@@ -1,57 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: emailable
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emailable
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-19 00:00:00.000000000 Z
11
+ date: 2022-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: faraday
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: faraday_middleware
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: net-http-persistent
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
13
  - !ruby/object:Gem::Dependency
56
14
  name: bundler
57
15
  requirement: !ruby/object:Gem::Requirement
@@ -176,13 +134,16 @@ files:
176
134
  - lib/emailable/batch.rb
177
135
  - lib/emailable/client.rb
178
136
  - lib/emailable/email_validator.rb
137
+ - lib/emailable/errors.rb
179
138
  - lib/emailable/resources/account.rb
180
139
  - lib/emailable/resources/api_resource.rb
181
140
  - lib/emailable/resources/batch_status.rb
182
141
  - lib/emailable/resources/verification.rb
142
+ - lib/emailable/response.rb
183
143
  - lib/emailable/version.rb
184
144
  - test/email_validator_test.rb
185
145
  - test/emailable/batch_test.rb
146
+ - test/emailable/resources/api_resource_test.rb
186
147
  - test/emailable_test.rb
187
148
  - test/test_helper.rb
188
149
  homepage: https://emailable.com
@@ -192,7 +153,7 @@ metadata:
192
153
  bug_tracker_uri: https://github.com/emailable/emailable-ruby/issues
193
154
  documentation_uri: https://docs.emailable.com/?ruby
194
155
  source_code_uri: https://github.com/emailable/emailable-ruby
195
- post_install_message:
156
+ post_install_message:
196
157
  rdoc_options: []
197
158
  require_paths:
198
159
  - lib
@@ -207,12 +168,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
168
  - !ruby/object:Gem::Version
208
169
  version: '0'
209
170
  requirements: []
210
- rubygems_version: 3.0.9
211
- signing_key:
171
+ rubygems_version: 3.1.6
172
+ signing_key:
212
173
  specification_version: 4
213
174
  summary: Ruby bindings for the Emailable API
214
175
  test_files:
215
176
  - test/email_validator_test.rb
216
177
  - test/emailable/batch_test.rb
178
+ - test/emailable/resources/api_resource_test.rb
217
179
  - test/emailable_test.rb
218
180
  - test/test_helper.rb