emailable 3.0.1 → 3.2.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 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