ncore 2.3.1 → 3.3.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: ad2896952813c29015f47f4f9abbbadaaedeeb1751ca400665ed139bf6701743
4
- data.tar.gz: 7fe5ce49a086118502a711bffe0913a307e1ca87f1a3bec32545b9bdc8efd1f2
3
+ metadata.gz: 0104ef39fd536e1845266782c236e1a00af04c0c6187b62567f247f3558f51bd
4
+ data.tar.gz: 99668e3c3ee5b104f2ec649a6dce845821943c9b67e361d0721a64ea9e5e94f0
5
5
  SHA512:
6
- metadata.gz: fb9af96b8efe29b420f88538e6b64f2215de16d2b3d05f48a1bc91bbbaa133e1975aec395f6ed45099773c2411721853fba930b0d7fb36a06b85d8ab8734a4e3
7
- data.tar.gz: 1f85607319bb2047d2efa8434db0f703db884a27c8606667f764bd67ceb871cf560b015d029c44d1d1dac156319f7d8344a8d72bb333f7fc52d5d2d23521cf07
6
+ metadata.gz: 257d7bd01c3f0c77b5b7c6f65d6bba245c8a7331627003ea1a72af9664eaf8a19026bc21df976c2499106f3701ef712a9b0a92b3754ec784468b9eb2783171b8
7
+ data.tar.gz: 2858e6b9cd9c067b41ebe4ba2968747db8553af7fe37c2a3e69abda1f22d60f8e841fce7e4b8dcf204ba6a5bbf6debf601fb18b111cc4a6f050ce47195042685
data/CHANGELOG.md CHANGED
@@ -1,6 +1,68 @@
1
+ #### 3.3.0
2
+
3
+ - Allow headers: {} as part of params/attribs everywhere
4
+ - Drop ActiveModel <= 5.1
5
+ - Remove ValidationError
6
+
7
+ #### 3.2.1
8
+
9
+ DEPRECATION NOTICE
10
+ - Support for ActiveModel < 5.2 is deprecated and will be removed in 3.3.
11
+
12
+ Other changes
13
+ - Allow ActiveModel 6.1
14
+
15
+ #### 3.2.0
16
+
17
+ - Use system's CA certificate store by default
18
+ To use bundled CAs instead:
19
+ SomeAppName::Api.ssl_cert_bundle = :bundled
20
+ On failure reading specified bundle, raises exception instead of warning
21
+
22
+ #### 3.1.0
23
+
24
+ - Add .bulk_update and bulk_update!
25
+
26
+ #### 3.0.0
27
+
28
+ BREAKING CHANGES
29
+ - Update has_many, belongs_to signatures
30
+ - Rename Base#url -> #resource_path
31
+ - Drop ActiveModel <= 4.1
32
+ - `#errors` is now always an ActiveModel::Errors instance
33
+
34
+ DEPRECATION NOTICE
35
+ - ValidationError is deprecated and will be removed in 3.1.
36
+
37
+ Other changes
38
+ - Add :cache option for requests
39
+ Set default store at MyApi::Api.cache_store=
40
+ See example railtie.rb for auto-config
41
+ Examples:
42
+ SomeResource.all(cache: true)
43
+ uses MyApi::Api.cache_store
44
+ SomeResource.find(id, cache: {expires_in: 5.minutes})
45
+ uses MyApi::Api.cache_store with specified options
46
+ SomeResource.find(id, cache: Dalli::Store.new(...))
47
+ uses provided cache store (with its default options)
48
+ - Make bearer_credential_key allow strings or symbols
49
+ - Warn on attr name collision
50
+ - Update CA certificates
51
+ - Better default output for #as_json
52
+ - Allow ActiveModel/Support 6.0
53
+ - Resolve deprecation messages on Ruby 2.6
54
+ - Add #factory
55
+ - API response :errors may be hash or array
56
+ - Add RecordInvalid#errors
57
+ - Better Ruby and ActiveModel integration
58
+ - #eql?, #==, #hash
59
+ - #model_name
60
+ - #i18n_scope, config via Api.i18n_scope=
61
+ - #cache_key, #cache_version, #cache_key_with_version
62
+
1
63
  #### 2.3.1
2
64
 
3
- - Allow ActiveSupport 6.1
65
+ - Allow ActiveModel 6.1
4
66
 
5
67
  #### 2.3.0
6
68
 
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2018 Notioneer, Inc.
1
+ Copyright (c) 2014-2021 Notioneer, Inc.
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  NCore is a Ruby gem designed to help build REST API clients. It is not an API
4
4
  client by itself, but provides several useful building blocks to build one.
5
5
 
6
- It relies on `excon` for HTTP handling and `activesupport`.
6
+ It relies on `excon` for HTTP handling and `activemodel`.
7
7
 
8
8
  If present, uses `multi_json`. Otherwise, the stdlib 'json' is used.
9
9
  'multi_json' with an accelerated json gem is recommended.
@@ -21,6 +21,8 @@ module MyApi
21
21
 
22
22
  self.strict_attributes = false
23
23
 
24
+ self.i18n_scope = :my_api
25
+
24
26
  self.instrument_key = 'request.my_api'
25
27
 
26
28
  self.status_page = 'http://my.api.status.page'
@@ -30,6 +32,14 @@ module MyApi
30
32
  # self.bearer_credential_key = :api_key
31
33
 
32
34
  self.credentials_error_message = %Q{Missing API credentials. Set default credentials using "MyApi.credentials = {api_user: YOUR_API_USER, api_key: YOUR_API_KEY}"}
35
+
36
+ # self.verify_ssl = true
37
+
38
+ # Uses system/OS bundle by default
39
+ # self.ssl_cert_bundle = :bundled # uses excon's included bundle
40
+ # self.ssl_cert_bundle = 'path/to/bundle.crt'
41
+
42
+ # self.logger = Logger.new(STDOUT)
33
43
  end
34
44
 
35
45
  end
@@ -1,6 +1,10 @@
1
1
  module MyApi
2
2
  class Railtie < Rails::Railtie
3
3
 
4
+ config.after_initialize do
5
+ MyApi::Api.cache_store = Rails.cache
6
+ end
7
+
4
8
  initializer "my_api.log_runtime" do |app|
5
9
  require 'my_api/rails/log_subscriber'
6
10
  ActiveSupport.on_load(:action_controller) do
data/lib/ncore.rb CHANGED
@@ -1,15 +1,42 @@
1
1
  require 'active_support/all'
2
+ require 'active_model'
2
3
  require 'excon'
3
4
  require 'pp'
4
5
 
5
- %w(version builder configuration associations attributes client collection exceptions identity lifecycle util base singleton_base).each do |f|
6
+ %w(
7
+ version
8
+ builder
9
+ configuration
10
+ associations
11
+ attributes
12
+ client
13
+ client_cache
14
+ collection
15
+ exceptions
16
+ identity
17
+ lifecycle
18
+ util
19
+ base
20
+ singleton_base
21
+ ).each do |f|
6
22
  require "ncore/#{f}"
7
23
  end
8
24
 
9
- %w(all build count create delete delete_bulk delete_single find find_single update).each do |f|
25
+ %w(
26
+ all
27
+ build
28
+ count
29
+ create
30
+ delete
31
+ delete_bulk
32
+ delete_single
33
+ find
34
+ find_single
35
+ update
36
+ update_bulk
37
+ ).each do |f|
10
38
  require "ncore/methods/#{f}"
11
39
  end
12
40
 
13
41
  require 'ncore/rails/action_controller' if defined?(::ActionController)
14
- require 'ncore/rails/active_model' if defined?(::ActiveModel)
15
- require 'ncore/rails/module_fix'
42
+ require 'ncore/rails/active_model'
@@ -1,17 +1,17 @@
1
1
  module NCore
2
2
  module Associations
3
3
 
4
- def has_many(assoc, klass=nil)
4
+ def has_many(assoc, class_name: nil)
5
5
  assoc = assoc.to_s
6
- klass ||= "#{module_name}::#{assoc.camelize.singularize}"
6
+ klass = class_name || "#{module_name}::#{assoc.camelize.singularize}"
7
7
  key = "#{attrib_name}_id"
8
8
  class_eval <<-M1, __FILE__, __LINE__+1
9
9
  def #{assoc}(params={})
10
10
  return [] unless id
11
11
  reload = params.delete :reload
12
- params = parse_request_params(params).reverse_merge credentials: api_creds
13
12
  cacheable = params.except(:credentials, :request).empty?
14
- params.merge! #{key}: id
13
+ params = parse_request_params(params).reverse_merge credentials: api_creds
14
+ params[:#{key}] = id
15
15
  if cacheable
16
16
  # only cache unfiltered, default api call
17
17
  @attribs[:#{assoc}] = (!reload && @attribs[:#{assoc}]) || #{klass}.all(params)
@@ -24,7 +24,7 @@ module NCore
24
24
  def find_#{assoc.singularize}(aid, params={})
25
25
  raise UnsavedObjectError unless id
26
26
  params = parse_request_params(params).reverse_merge credentials: api_creds
27
- params.merge! #{key}: id
27
+ params[:#{key}] = id
28
28
  #{klass}.find(aid, params)
29
29
  end
30
30
  M2
@@ -33,7 +33,7 @@ module NCore
33
33
  def create_#{assoc.singularize}(params={})
34
34
  raise UnsavedObjectError unless id
35
35
  params = parse_request_params(params).reverse_merge credentials: api_creds
36
- params.merge! #{key}: id
36
+ params[:#{key}] = id
37
37
  #{klass}.create(params)
38
38
  end
39
39
  M3
@@ -42,7 +42,7 @@ module NCore
42
42
  def update_#{assoc.singularize}(aid, params={})
43
43
  raise UnsavedObjectError unless id
44
44
  params = parse_request_params(params).reverse_merge credentials: api_creds
45
- params.merge! #{key}: id
45
+ params[:#{key}] = id
46
46
  #{klass}.update(aid, params)
47
47
  end
48
48
  M4
@@ -50,7 +50,7 @@ module NCore
50
50
  def create_#{assoc.singularize}!(params={})
51
51
  raise UnsavedObjectError unless id
52
52
  params = parse_request_params(params).reverse_merge credentials: api_creds
53
- params.merge! #{key}: id
53
+ params[:#{key}] = id
54
54
  #{klass}.create!(params)
55
55
  end
56
56
  M5
@@ -58,7 +58,7 @@ module NCore
58
58
  def update_#{assoc.singularize}!(aid, params={})
59
59
  raise UnsavedObjectError unless id
60
60
  params = parse_request_params(params).reverse_merge credentials: api_creds
61
- params.merge! #{key}: id
61
+ params[:#{key}] = id
62
62
  #{klass}.update!(aid, params)
63
63
  end
64
64
  M6
@@ -67,7 +67,7 @@ module NCore
67
67
  def delete_#{assoc.singularize}(aid, params={})
68
68
  raise UnsavedObjectError unless id
69
69
  params = parse_request_params(params).reverse_merge credentials: api_creds
70
- params.merge! #{key}: id
70
+ params[:#{key}] = id
71
71
  #{klass}.delete(aid, params)
72
72
  end
73
73
  M7
@@ -75,15 +75,15 @@ module NCore
75
75
  def delete_#{assoc.singularize}!(aid, params={})
76
76
  raise UnsavedObjectError unless id
77
77
  params = parse_request_params(params).reverse_merge credentials: api_creds
78
- params.merge! #{key}: id
78
+ params[:#{key}] = id
79
79
  #{klass}.delete!(aid, params)
80
80
  end
81
81
  M8
82
82
  end
83
83
 
84
- def belongs_to(assoc, klass=nil)
84
+ def belongs_to(assoc, class_name: nil)
85
85
  assoc = assoc.to_s
86
- klass ||= "#{module_name}::#{assoc.camelize}"
86
+ klass = class_name || "#{module_name}::#{assoc.camelize}"
87
87
  class_eval <<-M1, __FILE__, __LINE__+1
88
88
  attr :#{assoc}_id
89
89
  def #{assoc}(params={})
@@ -10,6 +10,7 @@ module NCore
10
10
  module ClassMethods
11
11
  def attr(*attrs)
12
12
  attrs.each do |attr|
13
+ check_existing_method(attr)
13
14
  class_eval <<-AR, __FILE__, __LINE__+1
14
15
  def #{attr}
15
16
  self[:#{attr}]
@@ -20,6 +21,7 @@ module NCore
20
21
 
21
22
  def attr_datetime(*attrs)
22
23
  attrs.each do |attr|
24
+ check_existing_method(attr)
23
25
  class_eval <<-AD, __FILE__, __LINE__+1
24
26
  def #{attr}
25
27
  case self[:#{attr}]
@@ -39,6 +41,7 @@ module NCore
39
41
 
40
42
  def attr_decimal(*attrs)
41
43
  attrs.each do |attr|
44
+ check_existing_method(attr)
42
45
  class_eval <<-AD, __FILE__, __LINE__+1
43
46
  def #{attr}
44
47
  case self[:#{attr}]
@@ -52,10 +55,20 @@ module NCore
52
55
  end
53
56
  end
54
57
 
58
+ def check_existing_method(attr)
59
+ if method_defined?(attr) || private_method_defined?(attr)
60
+ sc = self
61
+ sc = sc.superclass while sc.superclass != Object
62
+ warn "Warning: Existing method #{sc.name}##{attr} being overwritten at #{caller[3]}"
63
+ end
64
+ end
65
+
55
66
  def parse_request_params(params={}, opts={})
56
67
  params = params.with_indifferent_access
57
68
  req = params.delete(:request)
69
+ hdr = params.delete(:headers)
58
70
  creds = params.delete(:credentials)
71
+ cache = params.delete(:cache)
59
72
  if opts[:json_root]
60
73
  if params.key?(opts[:json_root])
61
74
  o = params
@@ -66,7 +79,9 @@ module NCore
66
79
  o = params
67
80
  end
68
81
  o[:request] = req if req
82
+ o[:headers] = hdr if hdr
69
83
  o[:credentials] = creds if creds
84
+ o[:cache] = cache if cache
70
85
  o
71
86
  end
72
87
  end
@@ -86,23 +101,18 @@ module NCore
86
101
  creds_attr = attribs.delete(:credentials)
87
102
  @api_creds = api_creds || creds_attr
88
103
 
89
- if attribs.keys.sort == %w(data error metadata)
90
- load_attrs = attribs
91
- else
92
- load_attrs = {
93
- metadata: attribs.delete(:metadata),
94
- errors: attribs.delete(:errors),
95
- data: attribs.delete(:data) || attribs
96
- }
97
- end
98
- load(load_attrs)
104
+ load(
105
+ metadata: attribs.delete(:metadata),
106
+ errors: attribs.delete(:errors),
107
+ data: attribs.delete(:data) || attribs
108
+ )
99
109
  end
100
110
 
101
111
 
102
112
  def attributes
103
113
  Util.deep_clone(@attribs)
104
114
  end
105
-
115
+ alias_method :as_json, :attributes
106
116
 
107
117
  def [](attr)
108
118
  @attribs[attr]
@@ -150,10 +160,10 @@ module NCore
150
160
  end
151
161
 
152
162
 
153
- def load(parsed)
154
- self.metadata = parsed[:metadata] || {}.with_indifferent_access
155
- self.errors = parsed[:errors] || {}.with_indifferent_access
156
- parsed[:data].each do |k,v|
163
+ def load(data:, errors: nil, metadata: nil)
164
+ self.metadata = metadata || {}.with_indifferent_access
165
+ self.errors = parse_errors(errors)
166
+ data.each do |k,v|
157
167
  if respond_to? "#{k}="
158
168
  send "#{k}=", self.class.interpret_type(v, api_creds)
159
169
  else
@@ -163,6 +173,17 @@ module NCore
163
173
  self
164
174
  end
165
175
 
176
+ def parse_errors(errors)
177
+ errors ||= []
178
+ if errors.is_a?(::ActiveModel::Errors)
179
+ errors
180
+ else
181
+ ::ActiveModel::Errors.new(self).tap do |e0|
182
+ errors.each{|msg| e0.add :base, msg }
183
+ end
184
+ end
185
+ end
186
+
166
187
  end
167
188
 
168
189
 
data/lib/ncore/base.rb CHANGED
@@ -4,8 +4,10 @@ module NCore
4
4
 
5
5
  included do
6
6
  extend Associations
7
+ include ActiveModel
7
8
  include Attributes
8
9
  include Client
10
+ include Client::Cache
9
11
  include Identity
10
12
  include Lifecycle
11
13
  include Util
@@ -21,15 +23,16 @@ module NCore
21
23
  include DeleteBulk if types.include? :delete_bulk
22
24
  include Find if types.include? :find
23
25
  include Update if types.include? :update
26
+ include UpdateBulk if types.include? :update_bulk
24
27
  end
25
28
 
26
- def url
29
+ def resource_path
27
30
  class_name.underscore.pluralize
28
31
  end
29
32
  end
30
33
 
31
- def url
32
- "#{self.class.url}/#{CGI.escape((id||'-').to_s)}"
34
+ def resource_path
35
+ "#{self.class.resource_path}/#{CGI.escape((id||'-').to_s)}"
33
36
  end
34
37
 
35
38
  end
data/lib/ncore/client.rb CHANGED
@@ -6,11 +6,12 @@ module NCore
6
6
 
7
7
  module ClassMethods
8
8
 
9
- # opts - {params: {}, headers: {}, credentials: {}}
9
+ # opts - {params: {}, headers: {}, credentials: {}, cache: {}}
10
10
  # unknown keys assumed to be :params if :params is missing
11
11
  def request(method, url, opts={})
12
12
  opts = opts.with_indifferent_access
13
13
  request_credentials = opts.delete 'credentials'
14
+ cache_opts = opts.delete 'cache'
14
15
  headers = opts.delete('headers') || {}
15
16
  params = opts['params'] || opts
16
17
 
@@ -48,7 +49,7 @@ module NCore
48
49
  write_timeout: 50,
49
50
  }
50
51
 
51
- response = execute_request(rest_opts)
52
+ response = execute_request(rest_opts, cache_opts)
52
53
  parsed = parse_response(response)
53
54
  [parsed, request_credentials]
54
55
  end
@@ -58,7 +59,7 @@ module NCore
58
59
 
59
60
  def retrieve_credentials
60
61
  if credentials.blank?
61
- raise parent::Error, credentials_error_message
62
+ raise module_parent::Error, credentials_error_message
62
63
  end
63
64
  credentials
64
65
  end
@@ -69,7 +70,7 @@ module NCore
69
70
 
70
71
  def retrieve_default_url
71
72
  if default_url.blank?
72
- raise parent::Error, credentials_error_message
73
+ raise module_parent::Error, credentials_error_message
73
74
  end
74
75
  default_url
75
76
  end
@@ -87,12 +88,12 @@ module NCore
87
88
 
88
89
  def build_query_string(params)
89
90
  if params.any?
90
- query_string = params.map do |k,v|
91
+ query_string = params.sort.map do |k,v|
91
92
  if v.is_a?(Array)
92
93
  if v.empty?
93
94
  "#{k.to_s}[]="
94
95
  else
95
- v.map do |v2|
96
+ v.sort.map do |v2|
96
97
  "#{k.to_s}[]=#{CGI::escape(v2.to_s)}"
97
98
  end.join('&')
98
99
  end
@@ -163,7 +164,7 @@ module NCore
163
164
  end
164
165
 
165
166
 
166
- def execute_request(rest_opts)
167
+ def execute_request(rest_opts, _)
167
168
  debug_request rest_opts if debug
168
169
 
169
170
  tries = 0
@@ -175,7 +176,8 @@ module NCore
175
176
  begin
176
177
  tries += 1
177
178
  response = connection.request rest_opts.except(:url)
178
- rescue Excon::Errors::SocketError, Excon::Error::Timeout, Errno::EADDRNOTAVAIL => e
179
+ rescue Excon::Error::Socket, Excon::Errors::SocketError, Excon::Error::Timeout,
180
+ Errno::EADDRNOTAVAIL => e
179
181
  # retry when keepalive was closed
180
182
  if tries <= 1 #&& e.message =~ /end of file reached/
181
183
  retry
@@ -187,30 +189,30 @@ module NCore
187
189
  debug_response response if debug
188
190
  end
189
191
  rescue Errno::ECONNRESET
190
- raise parent::ConnectionError, "Connection reset for #{host_for_error rest_opts[:url]} : check network or visit #{status_page}."
192
+ raise module_parent::ConnectionError, "Connection reset for #{host_for_error rest_opts[:url]} : check network or visit #{status_page}."
191
193
  rescue Errno::ECONNREFUSED
192
- raise parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
194
+ raise module_parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
193
195
  rescue Excon::Error::Timeout => e
194
196
  case e.message
195
197
  when /timeout reached/
196
- raise parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
198
+ raise module_parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
197
199
  else
198
200
  raise e
199
201
  end
200
202
  rescue Excon::Errors::SocketError => e
201
203
  case e.message
202
204
  when /Unable to verify certificate/
203
- raise parent::ConnectionError, "Unable to verify certificate for #{host_for_error rest_opts[:url]} : verify URL, set ssl_cert_bundle=, or disable SSL certificate verification (insecure)."
205
+ raise module_parent::ConnectionError, "Unable to verify certificate for #{host_for_error rest_opts[:url]} : verify URL, set ssl_cert_bundle=, or disable SSL certificate verification (insecure)."
204
206
  when /Name or service not known/, /No address associated with hostname/
205
- raise parent::ConnectionError, "DNS error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
207
+ raise module_parent::ConnectionError, "DNS error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
206
208
  when /Errno::ECONNREFUSED/
207
- raise parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
209
+ raise module_parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
208
210
  else
209
211
  raise e
210
212
  end
211
213
  rescue SocketError => e
212
214
  if e.message =~ /nodename nor servname provided/
213
- raise parent::ConnectionError, "DNS error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
215
+ raise module_parent::ConnectionError, "DNS error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
214
216
  else
215
217
  raise e
216
218
  end
@@ -218,21 +220,21 @@ module NCore
218
220
 
219
221
  case response.status
220
222
  when 401 # API auth valid; API call itself is an auth-related call and failed
221
- raise parent::AuthenticationFailed
223
+ raise module_parent::AuthenticationFailed
222
224
  when 402
223
- raise parent::AccountInactive, "Account inactive; login to portal to check account status."
225
+ raise module_parent::AccountInactive, "Account inactive; login to portal to check account status."
224
226
  when 403 # API auth failed or insufficient permissions
225
- raise parent::AccessDenied, "Access denied; check your API credentials and permissions."
227
+ raise module_parent::AccessDenied, "Access denied; check your API credentials and permissions."
226
228
  when 404
227
- raise parent::RecordNotFound
229
+ raise module_parent::RecordNotFound
228
230
  when 409, 422
229
231
  # pass through
230
232
  when 429
231
- raise parent::RateLimited
233
+ raise module_parent::RateLimited
232
234
  when 400..499
233
- raise parent::Error, "Client error: #{response.status}\n #{response.body}"
235
+ raise module_parent::Error, "Client error: #{response.status}\n #{response.body}"
234
236
  when 500..599
235
- raise parent::Error, "Server error: #{response.status}\n #{response.body}"
237
+ raise module_parent::Error, "Server error: #{response.status}\n #{response.body}"
236
238
  end
237
239
  response
238
240
  end
@@ -245,20 +247,20 @@ module NCore
245
247
  begin
246
248
  json = MultiJson.load(response.body, symbolize_keys: false) || {}
247
249
  rescue MultiJson::ParseError
248
- raise parent::Error, "Unable to parse API response; HTTP status: #{response.status}; body: #{response.body.inspect}"
250
+ raise module_parent::Error, "Unable to parse API response; HTTP status: #{response.status}; body: #{response.body.inspect}"
249
251
  end
250
252
  else
251
253
  begin
252
254
  json = JSON.parse(response.body, symbolize_names: false) || {}
253
255
  rescue JSON::ParserError
254
- raise parent::Error, "Unable to parse API response; HTTP status: #{response.status}; body: #{response.body.inspect}"
256
+ raise module_parent::Error, "Unable to parse API response; HTTP status: #{response.status}; body: #{response.body.inspect}"
255
257
  end
256
258
  end
257
259
  end
258
260
  json = json.with_indifferent_access
259
261
  errors = json.delete(:errors) || []
260
262
  if errors.any?
261
- errors = errors.values.flatten
263
+ errors = errors.values.flatten if errors.is_a?(Hash)
262
264
  metadata, json = json, {}
263
265
  else
264
266
  errors = []
@@ -299,12 +301,12 @@ module NCore
299
301
  if ssl_cert_bundle
300
302
  bundle_readable = File.readable?(ssl_cert_bundle) rescue false
301
303
  unless bundle_readable
302
- raise parent::CertificateError, "Unable to read SSL cert bundle #{ssl_cert_bundle}."
304
+ raise module_parent::CertificateError, "Unable to read SSL cert bundle #{ssl_cert_bundle}."
303
305
  end
304
306
  end
305
307
  @verify_ssl_cert = true
306
308
  else
307
- m = "WARNNG: SSL cert verification is disabled. Enable verification with: #{parent}::Api.verify_ssl = true."
309
+ m = "WARNNG: SSL cert verification is disabled. Enable verification with: #{module_parent}::Api.verify_ssl = true."
308
310
  $stderr.puts m
309
311
  @verify_ssl_cert = false
310
312
  end