Class: RestApi::Base
- Inherits:
-
ActiveResource::Base
show all
- Includes:
- ActiveModel::Dirty, ActiveModel::MassAssignmentSecurity, Cacheable
- Defined in:
- app/models/rest_api/base.rb,
app/models/rest_api/base.rb
Direct Known Subclasses
Alias, Application, Cartridge, CartridgeType, CartridgeType::Property, Domain, Embedded, Gear, GearGroup, Key, Quickstart, Environment, Info, User
Defined Under Namespace
Classes: AttributeHash
Class Attribute Summary collapse
Class Method Summary
collapse
-
.alias_attribute(from, to) ⇒ Object
-
.aliased_attributes ⇒ Object
-
.allow_anonymous? ⇒ Boolean
-
.attr_alters(from, *args) ⇒ Object
-
.calculated_attributes ⇒ Object
-
.configuration=(config) ⇒ Object
Update the configuration of the Rest API.
-
.connection(options = {}, refresh = false) ⇒ Object
Make connection specific to the instance, and aware of user context.
-
.custom_id(name, mutable = false) ⇒ Object
-
.delete(id, options = {}) ⇒ Object
-
.element_path(id = nil, prefix_options = {}, query_options = nil) ⇒ Object
-
.exception_for_code(code, type = nil) ⇒ Object
-
.find(*arguments) ⇒ Object
-
.find_one(options) ⇒ Object
-
.get(custom_method_name, options = {}, call_options = {}) ⇒ Object
-
.headers ⇒ Object
-
.on_exit_code(code, handles = nil, &block) ⇒ Object
-
.remote_errors_for(response) ⇒ Object
Must provide OpenShift compatible error decoding.
-
.shared_connection(options = {}, refresh = false) ⇒ Object
-
.singleton? ⇒ Boolean
-
.translate_api_error(errors, code, field, text) ⇒ Object
-
.use_patch_on_update? ⇒ Boolean
Instance Method Summary
collapse
#belongs_to, #has_many, #has_one
Constructor Details
#initialize(attributes = {}, persisted = false) ⇒ Base
Returns a new instance of Base.
200
201
202
203
|
# File 'app/models/rest_api/base.rb', line 200
def initialize(attributes = {}, persisted=false)
@as = attributes.delete :as
super attributes, persisted
end
|
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_symbol, *arguments) ⇒ Object
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
|
# File 'app/models/rest_api/base.rb', line 684
def method_missing(method_symbol, *arguments) method_name = method_symbol.to_s
if method_name =~ /(=|\?)$/
case $1
when "="
res = :"#{method_name}_will_change!"
send(res) if respond_to?(res) && attributes[name] != arguments.first
attributes[$`] = arguments.first
when "?"
attributes[$`]
end
else
return attributes[method_name] if attributes.include?(method_name)
return nil if known_attributes.include?(method_name)
super
end
end
|
Class Attribute Details
.collection_name ⇒ Object
Returns the value of attribute collection_name.
331
332
333
|
# File 'app/models/rest_api/base.rb', line 331
def collection_name
@collection_name
end
|
Class Method Details
.alias_attribute(from, to) ⇒ Object
149
150
151
152
153
154
155
156
157
158
159
160
161
|
# File 'app/models/rest_api/base.rb', line 149
def alias_attribute(from, to)
aliased_attributes[from] = to
define_method :"#{from}" do
self.send :"#{to}"
end
define_method :"#{from}?" do
self.send :"#{to}?"
end
define_method :"#{from}=" do |val|
self.send :"#{to}=", val
end
end
|
.aliased_attributes ⇒ Object
162
163
164
|
# File 'app/models/rest_api/base.rb', line 162
def aliased_attributes
@aliased_attributes ||= {}
end
|
.allow_anonymous? ⇒ Boolean
390
391
392
|
# File 'app/models/rest_api/base.rb', line 390
def allow_anonymous?
self.anonymous_api?
end
|
.attr_alters(from, *args) ⇒ Object
165
166
167
168
169
|
# File 'app/models/rest_api/base.rb', line 165
def attr_alters(from, *args)
targets = (calculated_attributes[from] ||= [])
targets.concat(args.flatten.uniq)
define_attribute_method from
end
|
.calculated_attributes ⇒ Object
170
171
172
|
# File 'app/models/rest_api/base.rb', line 170
def calculated_attributes
@calculated_attributes ||= {}
end
|
.configuration=(config) ⇒ Object
Update the configuration of the Rest API. Use instead of setting static variables directly.
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
|
# File 'app/models/rest_api/base.rb', line 748
def self.configuration=(config)
return if @last_config == config
url = URI.parse(config[:url])
path = url.path
if path[-1..1] == '/'
url.path = path[0..-2]
else
path = "#{path}/"
end
self.site = url.to_s
self.prefix = path
[:ssl_options, :idle_timeout, :read_timeout, :open_timeout].each do |sym|
self.send(:"#{sym}=", config[sym]) if config[sym]
end
self..delete 'User-Agent'
self.['User-Agent'] = config[:user_agent] if config[:user_agent]
self.proxy = if config[:proxy] == 'ENV'
:ENV
elsif config[:proxy]
URI config[:proxy]
end
@last_config = config
@info = false
end
|
.connection(options = {}, refresh = false) ⇒ Object
Make connection specific to the instance, and aware of user context
526
527
528
529
530
531
532
533
534
535
|
# File 'app/models/rest_api/base.rb', line 526
def connection(options = {}, refresh = false)
c = shared_connection(options, refresh)
if options[:as]
UserAwareConnection.new(c, options[:as])
elsif allow_anonymous?
c
else
raise RestApi::MissingAuthorizationError
end
end
|
.custom_id(name, mutable = false) ⇒ Object
312
313
314
315
316
317
318
319
320
321
322
323
324
|
# File 'app/models/rest_api/base.rb', line 312
def custom_id(name, mutable=false)
raise "Name #{name.inspect} must be a symbol" unless name.is_a?(Symbol) && !name.is_a?(Class)
define_attribute_method name
define_method :"#{name}=" do |s|
send(:"#{name}_will_change!") if !send(:"#{name}_changed?") && attributes[name] != s
attributes[name] = s
end
define_method 'to_key' do
persisted? ? [send(:"#{name}_was") || send(name)] : nil
end
end
|
.delete(id, options = {}) ⇒ Object
519
520
521
|
# File 'app/models/rest_api/base.rb', line 519
def delete(id, options = {})
connection(options).delete(element_path(id, options)) end
|
.element_path(id = nil, prefix_options = {}, query_options = nil) ⇒ Object
342
343
344
345
346
347
348
349
350
351
352
353
354
|
# File 'app/models/rest_api/base.rb', line 342
def element_path(id = nil, prefix_options = {}, query_options = nil) check_prefix_options(prefix_options)
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
path = "#{prefix(prefix_options)}#{collection_name}"
unless singleton?
raise ArgumentError, "id is required for non-singleton resources #{self}" if id.nil?
path << "/#{URI.parser.escape id.to_s}"
end
path << ".#{format.extension}#{query_string(query_options)}"
end
|
.exception_for_code(code, type = nil) ⇒ Object
490
491
492
493
494
495
496
|
# File 'app/models/rest_api/base.rb', line 490
def exception_for_code(code, type=nil)
if @exit_code_conditions
handler = @exit_code_conditions[code]
handler = handler[type] if type && Hash === handler
handler
end
end
|
.find(*arguments) ⇒ Object
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
|
# File 'app/models/rest_api/base.rb', line 356
def find(*arguments)
scope = arguments.slice!(0)
options = arguments.slice!(0) || {}
scope = :one if scope.nil? && singleton?
case scope
when :all then find_every(options)
when :first then find_every(options).first
when :last then find_every(options).last
when :one then find_one(options)
else find_single(scope, options)
end
end
|
.find_one(options) ⇒ Object
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
|
# File 'app/models/rest_api/base.rb', line 372
def find_one(options)
as = options[:as]
case from = options[:from]
when Symbol
instantiate_record(get(from, options[:params]))
when String
path = "#{from}#{query_string(options[:params])}"
instantiate_record(format.decode(connection(options).get(path, ).body), as) when nil prefix_options, query_options = split_options(options[:params])
path = element_path(nil, prefix_options, query_options)
instantiate_record(format.decode(connection(options).get(path, ).body), as) end
rescue ActiveResource::ResourceNotFound => e
raise ResourceNotFound.new(self.model_name, nil, e.response)
end
|
.get(custom_method_name, options = {}, call_options = {}) ⇒ Object
516
517
518
|
# File 'app/models/rest_api/base.rb', line 516
def get(custom_method_name, options = {}, call_options = {})
connection(call_options).get(custom_method_collection_url(custom_method_name, options), )
end
|
136
137
138
139
140
|
# File 'app/models/rest_api/base.rb', line 136
def
@headers ||= begin
(superclass != ActiveResource::Base) ? superclass..dup : {}
end
end
|
.on_exit_code(code, handles = nil, &block) ⇒ Object
469
470
471
|
# File 'app/models/rest_api/base.rb', line 469
def on_exit_code(code, handles=nil, &block)
(@exit_code_conditions ||= {})[code] = handles || block
end
|
.remote_errors_for(response) ⇒ Object
Must provide OpenShift compatible error decoding
415
416
417
418
419
420
421
422
|
# File 'app/models/rest_api/base.rb', line 415
def self.remote_errors_for(response)
format.decode(response.body)['messages'].map do |m|
[(m['exit_code'].to_i rescue m['exit_code']),
m['field'],
m['text'],
]
end rescue []
end
|
.shared_connection(options = {}, refresh = false) ⇒ Object
537
538
539
540
541
542
543
544
|
# File 'app/models/rest_api/base.rb', line 537
def shared_connection(options = {}, refresh = false)
if defined?(@connection) || _format != superclass._format || superclass == Object || superclass == ActiveResource::Base
@connection = update_connection(ActiveResource::PersistentConnection.new(site, format)) if refresh || @connection.nil?
@connection
else
superclass.shared_connection(options, refresh)
end
end
|
.singleton? ⇒ Boolean
393
394
395
|
# File 'app/models/rest_api/base.rb', line 393
def singleton?
self.singleton_api?
end
|
.translate_api_error(errors, code, field, text) ⇒ Object
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
|
# File 'app/models/rest_api/base.rb', line 472
def translate_api_error(errors, code, field, text)
Rails.logger.debug " Server error: :#{field} \##{code}: #{text}"
if @exit_code_conditions
handler = @exit_code_conditions[code]
handler = handler[:raise] if Hash === handler
case handler
when Proc then return if handler.call errors, code, field, text
when Class then raise handler, text
end
end
message = I18n.t(code, :scope => [:rest_api, :errors], :default => text.to_s)
field = (field || 'base').to_sym
errors.add(field, message) unless message.blank?
codes = errors.instance_variable_get(:@codes)
codes = errors.instance_variable_set(:@codes, {}) unless codes
(codes[field] ||= []).push(code)
end
|
.use_patch_on_update? ⇒ Boolean
396
397
398
|
# File 'app/models/rest_api/base.rb', line 396
def use_patch_on_update?
self.use_patch_api?
end
|
Instance Method Details
#as ⇒ Object
The user under whose context we will be accessing the remote server
611
612
613
|
# File 'app/models/rest_api/base.rb', line 611
def as
@as
end
|
#as=(as) ⇒ Object
614
615
616
617
|
# File 'app/models/rest_api/base.rb', line 614
def as=(as)
@connection = nil
@as = as
end
|
#assign_attributes(values, options = {}) ⇒ Object
Default mass assignment support
626
627
628
629
630
631
|
# File 'app/models/rest_api/base.rb', line 626
def assign_attributes(values, options = {})
sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
send("#{k}=", v)
end
self
end
|
#attributes=(attrs) ⇒ Object
252
253
254
255
256
257
258
259
260
|
# File 'app/models/rest_api/base.rb', line 252
def attributes=(attrs)
attrs.with_indifferent_access.slice(*(
self.class.known_attributes +
self.class.aliased_attributes.keys
)).each_pair do |k,v|
send(:"#{k}=", v)
end
self
end
|
#clone ⇒ Object
Ensure Fixnums and booleans can be cloned
187
188
189
190
191
192
193
194
195
196
197
198
|
# File 'app/models/rest_api/base.rb', line 187
def clone
cloned = Hash[attributes.reject {|k,v| k == self.class.primary_key || v.is_a?(ActiveResource::Base)}.map { |k, v| [k, v.duplicable? ? v.clone : v] }]
resource = self.class.new({})
resource.prefix_options = self.prefix_options
resource.send :instance_variable_set, '@attributes', cloned
resource.as = @as resource
end
|
#dup ⇒ Object
Ensure user authorization info is duplicated
178
179
180
181
182
|
# File 'app/models/rest_api/base.rb', line 178
def dup
super.tap do |resource|
resource.as = @as
end
end
|
#duplicate_errors ⇒ Object
296
297
298
299
300
301
302
303
304
305
306
307
308
309
|
# File 'app/models/rest_api/base.rb', line 296
def duplicate_errors
self.class.calculated_attributes.each_pair do |from, attrs|
attrs.each do |to|
(errors[to] || []).each do |error|
errors.add(from, error) unless errors.has_key?(from) && errors[:from].include?(error)
end
end
end
self.class.aliased_attributes.each_pair do |from, to|
(errors[to] || []).each do |error|
errors.add(from, error) unless errors.has_key?(from) && errors[:from].include?(error)
end
end
end
|
#get(custom_method_name, options = {}) ⇒ Object
Override method from CustomMethods to handle body objects
503
504
505
|
# File 'app/models/rest_api/base.rb', line 503
def get(custom_method_name, options = {})
self.class.send(:instantiate_collection, self.class.format.decode(connection.get(custom_method_element_url(custom_method_name, options), self.class.).body), as, prefix_options ) end
|
#has_exit_code?(code, opts = nil) ⇒ Boolean
459
460
461
462
463
464
465
466
|
# File 'app/models/rest_api/base.rb', line 459
def has_exit_code?(code, opts=nil)
codes = errors.instance_variable_get(:@codes) || {}
if opts && opts[:on]
(codes[opts[:on].to_sym] || []).include? code
else
codes.values.any?{ |c| c.include? code }
end
end
|
#load(attributes, remove_root = false) ⇒ Object
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
|
# File 'app/models/rest_api/base.rb', line 205
def load(attributes, remove_root=false)
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
self.prefix_options, attributes = split_options(attributes)
attributes = attributes.dup
aliased = self.class.aliased_attributes
calculated = self.class.calculated_attributes
known = self.class.known_attributes
aliased.each do |from,to|
value = attributes.delete(from)
send("#{to}=", value) unless value.nil?
end
attributes.each do |key, value|
if !known.include? key.to_s and !calculated.include? key and respond_to?("#{key}=")
send("#{key}=", value)
else
self.attributes[key.to_s] =
case value
when Array
resource = nil
value.map do |attrs|
if attrs.is_a?(Hash)
resource ||= find_or_create_resource_for_collection(key)
attrs[:as] = as if resource.method_defined? :as=
resource.new(attrs)
else
attrs.duplicable? ? attrs.dup : attrs
end
end
when Hash
resource = find_or_create_resource_for(key)
value[:as] = as if resource.method_defined? :as=
resource.new(value)
else
value.duplicable? ? value.dup : value
end
end
end
calculated.each_key { |key| send("#{key}=", attributes[key]) if attributes.include?(key) }
@changed_attributes.clear if @changed_attributes
self
end
|
#load_remote_errors(remote_errors, save_cache = false, optional = false) ⇒ Object
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
|
# File 'app/models/rest_api/base.rb', line 424
def load_remote_errors(remote_errors, save_cache=false, optional=false)
begin
self.class.remote_errors_for(remote_errors.response).each do |m|
self.class.translate_api_error(errors, *m)
end
Rails.logger.debug " Found errors on the response object: #{errors.to_hash.inspect}"
duplicate_errors
rescue ActiveResource::ConnectionError
raise
rescue Exception => e
Rails.logger.warn e
Rails.logger.warn e.backtrace
msg = if defined? response
Rails.logger.warn "Unable to read server response, #{response.inspect}"
Rails.logger.warn " Body: #{response.body.inspect}" if defined? response.body
defined?(response.body) ? response.body.to_s : 'No response body from server'
else
'No response object'
end
raise RestApi::BadServerResponseError, msg, $@ unless optional
end
optional ? !errors.empty? : errors
end
|
#raise_on_invalid ⇒ Object
262
263
264
265
266
267
268
|
# File 'app/models/rest_api/base.rb', line 262
def raise_on_invalid
(errors.instance_variable_get(:@codes) || {}).values.flatten(1).compact.uniq.each do |code|
exc = self.class.exception_for_code(code, :on_invalid)
raise exc.new(self) if exc
end
raise(ActiveResource::ResourceInvalid.new(self))
end
|
#reload ⇒ Object
Override methods from ActiveResource to make them contextual connection aware
511
512
513
|
# File 'app/models/rest_api/base.rb', line 511
def reload
self.load(prefix_options.merge(self.class.find(to_param, :params => prefix_options, :as => as).attributes))
end
|
#remote_results ⇒ Object
456
457
458
|
# File 'app/models/rest_api/base.rb', line 456
def remote_results
(attributes[:messages] || []).select{ |m| m['field'] == 'result' }.map{ |m| m['text'].presence }.compact
end
|
#save! ⇒ Object
270
271
272
|
# File 'app/models/rest_api/base.rb', line 270
def save!
save || raise_on_invalid
end
|
#save_with_change_tracking(*args, &block) ⇒ Object
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
# File 'app/models/rest_api/base.rb', line 274
def save_with_change_tracking(*args, &block)
save_without_change_tracking(*args, &block).tap do |valid|
if valid
@previously_changed = changes
@changed_attributes.clear
end
end
rescue ActiveResource::ConnectionError => error
raise unless set_remote_errors(error, true)
end
|
#to_json(options = {}) ⇒ Object
619
620
621
|
# File 'app/models/rest_api/base.rb', line 619
def to_json(options={})
super({:root => nil}.merge(options))
end
|
#valid? ⇒ Boolean
Copy calculated attribute errors
292
293
294
|
# File 'app/models/rest_api/base.rb', line 292
def valid?
super.tap { |valid| duplicate_errors unless valid }
end
|