bitly 1.1.1 → 2.0.1
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 +5 -5
- data/.gitignore +36 -3
- data/.rspec +3 -0
- data/.travis.yml +6 -2
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -2
- data/History.txt +32 -1
- data/LICENSE.md +1 -1
- data/README.md +151 -58
- data/Rakefile +6 -9
- data/bitly.gemspec +36 -32
- data/config/env.yml.example +5 -0
- data/lib/bitly.rb +9 -7
- data/lib/bitly/api.rb +19 -0
- data/lib/bitly/api/base.rb +23 -0
- data/lib/bitly/api/bitlink.rb +342 -0
- data/lib/bitly/api/bitlink/clicks_summary.rb +35 -0
- data/lib/bitly/api/bitlink/deeplink.rb +29 -0
- data/lib/bitly/api/bitlink/link_click.rb +75 -0
- data/lib/bitly/api/bitlink/paginated_list.rb +52 -0
- data/lib/bitly/api/bsd.rb +24 -0
- data/lib/bitly/api/click_metric.rb +186 -0
- data/lib/bitly/api/client.rb +588 -0
- data/lib/bitly/api/group.rb +232 -0
- data/lib/bitly/api/group/preferences.rb +73 -0
- data/lib/bitly/api/list.rb +22 -0
- data/lib/bitly/api/oauth_app.rb +26 -0
- data/lib/bitly/api/organization.rb +104 -0
- data/lib/bitly/api/shorten_counts.rb +61 -0
- data/lib/bitly/api/user.rb +107 -0
- data/lib/bitly/error.rb +33 -0
- data/lib/bitly/http.rb +10 -0
- data/lib/bitly/http/adapters.rb +9 -0
- data/lib/bitly/http/adapters/net_http.rb +27 -0
- data/lib/bitly/http/client.rb +33 -0
- data/lib/bitly/http/request.rb +118 -0
- data/lib/bitly/http/response.rb +66 -0
- data/lib/bitly/oauth.rb +109 -0
- data/lib/bitly/version.rb +3 -1
- metadata +82 -111
- data/Manifest +0 -37
- data/lib/bitly/client.rb +0 -145
- data/lib/bitly/config.rb +0 -29
- data/lib/bitly/url.rb +0 -103
- data/lib/bitly/utils.rb +0 -57
- data/lib/bitly/v3.rb +0 -14
- data/lib/bitly/v3/bitly.rb +0 -7
- data/lib/bitly/v3/client.rb +0 -207
- data/lib/bitly/v3/country.rb +0 -13
- data/lib/bitly/v3/day.rb +0 -13
- data/lib/bitly/v3/missing_url.rb +0 -15
- data/lib/bitly/v3/oauth.rb +0 -41
- data/lib/bitly/v3/realtime_link.rb +0 -18
- data/lib/bitly/v3/referrer.rb +0 -13
- data/lib/bitly/v3/url.rb +0 -154
- data/lib/bitly/v3/user.rb +0 -135
- data/test/bitly/test_client.rb +0 -266
- data/test/bitly/test_config.rb +0 -28
- data/test/bitly/test_url.rb +0 -167
- data/test/bitly/test_utils.rb +0 -79
- data/test/fixtures/cnn.json +0 -1
- data/test/fixtures/cnn_and_google.json +0 -1
- data/test/fixtures/expand_cnn.json +0 -1
- data/test/fixtures/expand_cnn_and_google.json +0 -1
- data/test/fixtures/google_and_cnn_info.json +0 -1
- data/test/fixtures/google_info.json +0 -1
- data/test/fixtures/google_stats.json +0 -1
- data/test/fixtures/shorten_error.json +0 -1
- data/test/test_helper.rb +0 -39
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./base"
|
3
|
+
|
4
|
+
module Bitly
|
5
|
+
module API
|
6
|
+
class ShortenCounts
|
7
|
+
include Base
|
8
|
+
|
9
|
+
def self.attributes
|
10
|
+
[:units, :facet, :unit_reference, :unit]
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader(*attributes)
|
14
|
+
attr_reader :metrics
|
15
|
+
|
16
|
+
Metric = Struct.new(:key, :value)
|
17
|
+
|
18
|
+
##
|
19
|
+
# Shorten counts by group
|
20
|
+
# [`GET /v4/groups/{group_guid}/shorten_counts`](https://dev.bitly.com/v4/#operation/getGroupShortenCounts)
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# shorten_counts = Bitly::API::ShortenCounts.by_group(client: client, group_guid: group_guid)
|
24
|
+
#
|
25
|
+
# @param client [Bitly::API::Client] An authorized API client
|
26
|
+
# @param group_guid [String] The guid of the group for which you want
|
27
|
+
# shorten counts
|
28
|
+
#
|
29
|
+
# @return [Bitly::API::ShortenCounts]
|
30
|
+
def self.by_group(client:, group_guid:)
|
31
|
+
response = client.request(path: "/groups/#{group_guid}/shorten_counts")
|
32
|
+
new(data: response.body, response: response)
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Shorten counts by organization
|
37
|
+
# [`GET /v4/organizations/{organization_guid}/shorten_counts`](https://dev.bitly.com/v4/#operation/getOrganizationShortenCounts)
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# shorten_counts = Bitly::API::ShortenCounts.by_organization(client: client, organization_guid: organization_guid)
|
41
|
+
#
|
42
|
+
# @param client [Bitly::API::Client] An authorized API client
|
43
|
+
# @param organization_guid [String] The guid of the organization for which
|
44
|
+
# you want shorten counts
|
45
|
+
#
|
46
|
+
# @return [Bitly::API::ShortenCounts]
|
47
|
+
def self.by_organization(client:, organization_guid:)
|
48
|
+
response = client.request(path: "/organizations/#{organization_guid}/shorten_counts")
|
49
|
+
new(data: response.body, response: response)
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(data:, response: nil)
|
53
|
+
assign_attributes(data)
|
54
|
+
@metrics = data["metrics"].map do |metric|
|
55
|
+
Metric.new(metric["key"], metric["value"])
|
56
|
+
end
|
57
|
+
@response = response
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./base"
|
3
|
+
|
4
|
+
module Bitly
|
5
|
+
module API
|
6
|
+
##
|
7
|
+
# A User represents the authorized user
|
8
|
+
class User
|
9
|
+
class Email
|
10
|
+
attr_reader :email, :is_verified, :is_primary
|
11
|
+
def initialize(data)
|
12
|
+
@email = data["email"]
|
13
|
+
@is_verified = data["is_verified"]
|
14
|
+
@is_primary = data["is_primary"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
include Base
|
19
|
+
##
|
20
|
+
# Gets the authorized user from the API.
|
21
|
+
# [`GET /v4/user`](https://dev.bitly.com/v4/#operation/getUser)
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# user = Bitly::API::User.fetch(client: client)
|
25
|
+
#
|
26
|
+
# @param client [Bitly::API::Client] The authorized API client
|
27
|
+
#
|
28
|
+
# @return [Bitly::API::User]
|
29
|
+
def self.fetch(client:)
|
30
|
+
response = client.request(path: "/user")
|
31
|
+
new(data: response.body, client: client, response: response)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Array<Symbol>] The attributes the API returns for a user
|
35
|
+
def self.attributes
|
36
|
+
[:login, :is_active, :is_2fa_enabled, :name, :is_sso_user, :default_group_guid]
|
37
|
+
end
|
38
|
+
# @return [Array<Symbol>] The attributes the API returns that need to be
|
39
|
+
# converted to `Time` objects.
|
40
|
+
def self.time_attributes
|
41
|
+
[:created, :modified]
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader(*(attributes + time_attributes))
|
45
|
+
attr_reader :emails
|
46
|
+
|
47
|
+
##
|
48
|
+
# Creates a Bitly::API::User object.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# user = Bitly::API::User.new(data: user_data, client: client)
|
52
|
+
#
|
53
|
+
# @param data [Hash<String, String | Boolean>] The user data from the API
|
54
|
+
# @param client [Bitly::API::Client] The authorized API client
|
55
|
+
# @param response [Bitly::HTTP::Response] The original HTTP response
|
56
|
+
#
|
57
|
+
# @return [Bitly::API::User]
|
58
|
+
def initialize(data:, client:, response: nil)
|
59
|
+
assign_attributes(data)
|
60
|
+
@client = client
|
61
|
+
@response = response
|
62
|
+
if data["emails"]
|
63
|
+
@emails = data["emails"].map { |e| Email.new(e) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Returns the default group for the user from the default group guid
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# user.default_group
|
72
|
+
#
|
73
|
+
# @returns [Bitly::API::Group]
|
74
|
+
def default_group
|
75
|
+
@default_group ||= Group.fetch(client: @client, guid: default_group_guid)
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Allows you to update the authorized user's name or default group guid.
|
80
|
+
# If you update the default group ID and have already loaded the default
|
81
|
+
# group, it is nilled out so it can be reloaded with the correct ID.
|
82
|
+
# [`PATCH /v4/user`](https://dev.bitly.com/v4/#operation/updateUser)]
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# user.update(name: "New Name", default_group_guid: "aaabbb")
|
86
|
+
#
|
87
|
+
# @param name [String] A new name
|
88
|
+
# @param default_group_guid [String] A new default guid
|
89
|
+
#
|
90
|
+
# @return [Bitly::API::User]
|
91
|
+
def update(name: nil, default_group_guid: nil)
|
92
|
+
params = { "name" => name }
|
93
|
+
if default_group_guid
|
94
|
+
params["default_group_guid"] = default_group_guid
|
95
|
+
@default_group = nil
|
96
|
+
end
|
97
|
+
@response = @client.request(
|
98
|
+
path: "/user",
|
99
|
+
method: "PATCH",
|
100
|
+
params: params
|
101
|
+
)
|
102
|
+
assign_attributes(@response.body)
|
103
|
+
self
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/bitly/error.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitly
|
4
|
+
##
|
5
|
+
# An error class that covers all potential errors from the Bitly API. In an
|
6
|
+
# error scenario, the API is only guaranteed to return a status_code and
|
7
|
+
# status_txt: https://dev.bitly.com/formats.html
|
8
|
+
class Error < StandardError
|
9
|
+
##
|
10
|
+
# @return [String] The status code of the failed request
|
11
|
+
attr_reader :status_code
|
12
|
+
|
13
|
+
##
|
14
|
+
# @return [String] The description of the failed request
|
15
|
+
attr_reader :description
|
16
|
+
|
17
|
+
##
|
18
|
+
# @return [Bitly::HTTP::Response] The response that caused the error
|
19
|
+
attr_reader :response
|
20
|
+
|
21
|
+
##
|
22
|
+
# Creates a new Bitly::Error object
|
23
|
+
#
|
24
|
+
# @param [Bitly::HTTP::Response] response The parsed response to the HTTP request
|
25
|
+
def initialize(response)
|
26
|
+
@response = response
|
27
|
+
@status_code = response.status
|
28
|
+
@description = response.body["description"]
|
29
|
+
@message = "[#{@status_code}] #{response.body["message"]}"
|
30
|
+
super(@message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/bitly/http.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitly
|
4
|
+
module HTTP
|
5
|
+
autoload :Adapters, File.join(File.dirname(__FILE__), "http/adapters.rb")
|
6
|
+
autoload :Response, File.join(File.dirname(__FILE__), "http/response.rb")
|
7
|
+
autoload :Request, File.join(File.dirname(__FILE__), "http/request.rb")
|
8
|
+
autoload :Client, File.join(File.dirname(__FILE__), "http/client.rb")
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module Bitly
|
6
|
+
module HTTP
|
7
|
+
module Adapters
|
8
|
+
class NetHTTP
|
9
|
+
def request(request)
|
10
|
+
Net::HTTP.start(request.uri.host, request.uri.port, use_ssl: true) do |http|
|
11
|
+
method = Object.const_get("Net::HTTP::#{request.method.capitalize}")
|
12
|
+
full_path = request.uri.path
|
13
|
+
full_path += "?#{request.uri.query}" if request.uri.query
|
14
|
+
http_request = method.new full_path
|
15
|
+
http_request.body = request.body
|
16
|
+
request.headers.each do |header, value|
|
17
|
+
http_request[header] = value
|
18
|
+
end
|
19
|
+
response = http.request http_request
|
20
|
+
success = response.kind_of? Net::HTTPSuccess
|
21
|
+
return [response.code, response.body, response.to_hash, success]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bitly
|
4
|
+
module HTTP
|
5
|
+
class Client
|
6
|
+
def initialize(adapter=Bitly::HTTP::Adapters::NetHTTP.new)
|
7
|
+
@adapter = adapter
|
8
|
+
raise ArgumentError, "Adapter must have a request method." unless @adapter.respond_to?(:request)
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# The main method for the HTTP client. It receives a Bitly::HTTP::Request
|
13
|
+
# object, makes the request described and returns a Bitly::HTTP::Response.
|
14
|
+
#
|
15
|
+
# @param [Bitly::HTTP::Request] request The request that should be made
|
16
|
+
#
|
17
|
+
# @return [Bitly::HTTP::Response] The response from the request.
|
18
|
+
#
|
19
|
+
# @raise [Bitly::Error] If the response is not a successful response
|
20
|
+
# in the 2xx range, then we raise an error with the response passed as
|
21
|
+
# an argument. It is up to the application to catch this error.
|
22
|
+
def request(request)
|
23
|
+
status, body, headers, success = @adapter.request(request)
|
24
|
+
response = Bitly::HTTP::Response.new(status: status, body: body, headers: headers, request: request)
|
25
|
+
if success
|
26
|
+
return response
|
27
|
+
else
|
28
|
+
raise Bitly::Error, response
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "json"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module Bitly
|
6
|
+
module HTTP
|
7
|
+
class Request
|
8
|
+
# @return [String] The HTTP method that the request should be.
|
9
|
+
attr_reader :method
|
10
|
+
|
11
|
+
# @return [Hash] A hash of parameters that will be turned into query
|
12
|
+
# parameters or a request body
|
13
|
+
attr_reader :params
|
14
|
+
|
15
|
+
# @return [Hash] A hash of HTTP headers that will be included with the
|
16
|
+
# request
|
17
|
+
attr_reader :headers
|
18
|
+
|
19
|
+
##
|
20
|
+
# Creates a new Bitly::HTTP::Request object, which is to be used by the
|
21
|
+
# [Bitly::HTTP::Client].
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# request = Bitly::HTTP::Request.new(uri: URI.parse('https://api-ssl.bitly.com/v3/shorten'), method: "GET")
|
25
|
+
#
|
26
|
+
# @param [URI] uri A [URI] that you want to make the request to.
|
27
|
+
# @param [String] method The HTTP method that should be used to make the
|
28
|
+
# request.
|
29
|
+
# @param [Hash] params The parameters to be sent as part of the request.
|
30
|
+
# GET parameters will be sent as part of the query string and other
|
31
|
+
# methods will be added to the request body.
|
32
|
+
def initialize(uri: , method: "GET", params: {}, headers: {})
|
33
|
+
errors = []
|
34
|
+
@uri = uri
|
35
|
+
errors << "uri must be an object of type URI. Received a #{uri.class}" unless uri.kind_of?(URI)
|
36
|
+
@method = method
|
37
|
+
errors << "method must be a valid HTTP method. Received: #{method}." unless HTTP_METHODS.include?(method)
|
38
|
+
@params = params
|
39
|
+
errors << "params must be a hash. Received: #{params.inspect}." unless params.kind_of?(Hash)
|
40
|
+
@headers = headers
|
41
|
+
errors << "headers must be a hash. Received: #{headers.inspect}." unless headers.kind_of?(Hash)
|
42
|
+
raise ArgumentError, errors.join("\n") if errors.any?
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Returns the uri for the request. If the request is an HTTP method that
|
47
|
+
# uses a body to send data, then the uri is the one that the request was
|
48
|
+
# initialised with. If the request uses query parameters, then the
|
49
|
+
# parameters are serialised and added to the uri's query.
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# uri = URI.parse("https://api-ssl.bitly.com/v3/shorten")
|
53
|
+
# request = Bitly::HTTP::Request.new(uri: uri, params: { foo: "bar" })
|
54
|
+
# request.uri.to_s
|
55
|
+
# # => "https://api-ssl.bitly.com/v3/shorten?foo=bar"
|
56
|
+
#
|
57
|
+
# @return [URI] The full URI for the request
|
58
|
+
def uri
|
59
|
+
uri = @uri.dup
|
60
|
+
return uri if HTTP_METHODS_WITH_BODY.include?(@method)
|
61
|
+
if uri.query
|
62
|
+
existing_query = URI.decode_www_form(uri.query)
|
63
|
+
new_query = hash_to_arrays(@params)
|
64
|
+
uri.query = URI.encode_www_form((existing_query + new_query).uniq)
|
65
|
+
else
|
66
|
+
uri.query = URI.encode_www_form(@params) if @params.any?
|
67
|
+
end
|
68
|
+
uri
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Returns the body of the request if the request is an HTTP method that
|
73
|
+
# uses a body to send data. The body is a JSON string of the parameters.
|
74
|
+
# If the request doesn't use a body to send data, this returns nil.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# uri = URI.parse("https://api-ssl.bitly.com/v3/shorten")
|
78
|
+
# request = Bitly::HTTP::Request.new(uri: uri, method: 'POST', params: { foo: "bar" })
|
79
|
+
# request.body
|
80
|
+
# # => "{\"foo\":\"bar\"}"
|
81
|
+
#
|
82
|
+
# @return [String] The request body
|
83
|
+
def body
|
84
|
+
return nil if HTTP_METHODS_WITHOUT_BODY.include?(@method)
|
85
|
+
return JSON.generate(params)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def hash_to_arrays(hash)
|
91
|
+
hash.map do |key, value|
|
92
|
+
if value.is_a?(Array)
|
93
|
+
value.map { |v| [key, v] }
|
94
|
+
else
|
95
|
+
[[key, value]]
|
96
|
+
end
|
97
|
+
end.flatten(1)
|
98
|
+
end
|
99
|
+
|
100
|
+
HTTP_METHODS_WITHOUT_BODY = [
|
101
|
+
"GET",
|
102
|
+
"HEAD",
|
103
|
+
"DELETE",
|
104
|
+
"TRACE",
|
105
|
+
"OPTIONS"
|
106
|
+
]
|
107
|
+
|
108
|
+
HTTP_METHODS_WITH_BODY = [
|
109
|
+
"POST",
|
110
|
+
"PUT",
|
111
|
+
"PATCH",
|
112
|
+
"CONNECT"
|
113
|
+
]
|
114
|
+
|
115
|
+
HTTP_METHODS = HTTP_METHODS_WITH_BODY + HTTP_METHODS_WITHOUT_BODY
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Bitly
|
5
|
+
module HTTP
|
6
|
+
##
|
7
|
+
# The Response class handles generic responses from the API. It is made up
|
8
|
+
# of a status code, body and headers. The body is expected to be JSON and it
|
9
|
+
# will parse the body. The status should lie within the range 100 - 599 and
|
10
|
+
# the headers should be a hash.
|
11
|
+
class Response
|
12
|
+
|
13
|
+
# @return [String] The response's status code
|
14
|
+
attr_reader :status
|
15
|
+
|
16
|
+
# @return [Hash] The response's parsed body
|
17
|
+
attr_reader :body
|
18
|
+
|
19
|
+
# @return [Hash] The response's headers
|
20
|
+
attr_reader :headers
|
21
|
+
|
22
|
+
# @return [Bitly::HTTP::Request] The request that caused this response
|
23
|
+
attr_reader :request
|
24
|
+
|
25
|
+
##
|
26
|
+
# Creates a new Bitly::HTTP::Response object, which can be used by other
|
27
|
+
# objects in the library.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# response = Bitly::HTTP::Response.new(status: "200", body: "{}", headers: {})
|
31
|
+
#
|
32
|
+
# @param [String] status The status code of the response, which should be
|
33
|
+
# between 100 and 599
|
34
|
+
# @param [String] body The body of the response, a String that is valid
|
35
|
+
# JSON and will be parsed
|
36
|
+
# @param [Hash] headers The response headers
|
37
|
+
def initialize(status:, body:, headers:, request: nil)
|
38
|
+
errors = []
|
39
|
+
@status = status
|
40
|
+
errors << "Status must be a valid HTTP status code. Received #{status}" unless is_status?(status)
|
41
|
+
if body.nil? || body.empty?
|
42
|
+
@body = nil
|
43
|
+
else
|
44
|
+
begin
|
45
|
+
@body = JSON.parse(body)
|
46
|
+
rescue JSON::ParserError
|
47
|
+
@body = {
|
48
|
+
"message" => body
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
@headers = headers
|
53
|
+
errors << "Headers must be a hash. Received #{headers}" unless headers.is_a?(Hash)
|
54
|
+
@request = request
|
55
|
+
errors << "Request must be a Bitly::HTTP::Request. Received #{request}" if request && !request.is_a?(Request)
|
56
|
+
raise ArgumentError, errors.join("\n"), caller if errors.any?
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def is_status?(status)
|
62
|
+
!!status.match(/\A[1-5][0-9][0-9]\z/)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|