Class: ApiResource::Base
- Inherits:
-
Object
- Object
- ApiResource::Base
- Extended by:
- ActiveModel::Naming
- Includes:
- AssociationActivation, Attributes, Callbacks, Conditions, Finders, ModelErrors, Observing, Scopes, Typecast
- Defined in:
- lib/api_resource/base.rb,
lib/api_resource/base.rb
Direct Known Subclasses
Constant Summary
Constants included from Typecast
Typecast::FALSE_VALUES, Typecast::ISO_DATE, Typecast::ISO_DATETIME, Typecast::TRUE_VALUES
Class Attribute Summary collapse
-
.collection_name ⇒ Object
collection_name with default.
-
.element_name ⇒ Object
element_name with default.
Class Method Summary collapse
- .build(attributes = {}) ⇒ Object
- .collection_path(prefix_options = {}, query_options = nil) ⇒ Object
-
.connection(refresh = false) ⇒ Connection
Accessor for the connection.
- .create(attributes = {}) ⇒ Object
-
.delete(id, options = {}) ⇒ Object
Deletes the resources with the ID in the
idparameter. -
.element_path(id, prefix_options = {}, query_options = nil) ⇒ Object
alias_method :set_prefix, :prefix= alias_method :set_element_name, :element_name= alias_method :set_collection_name, :collection_name=.
-
.format_with_mimetype_or_format_set=(mime_type_or_format) ⇒ MimeType
Handles the setting of format to a MimeType.
-
.headers ⇒ Hash
Reader for headers.
- .inherited(klass) ⇒ Object
-
.load_resource_definition ⇒ Boolean
Explicit call to load the resource definition.
-
.new_element_path(prefix_options = {}) ⇒ Object
path to find.
-
.open_timeout_with_connection_reset=(timeout) ⇒ Fixnum
Set the open timeout on the connection and connect.
-
.prefix(options = {}) ⇒ String
Prefix for the resource path.
- .prefix=(value = '/') ⇒ Object
- .prefix_source ⇒ Object
-
.reload_resource_definition ⇒ Boolean
(also: reload_class_attributes)
Clear the old resource definition and reload it from the server.
-
.reset_connection ⇒ Boolean
Reset our connection instance so that we will reconnect the next time we need it.
-
.resource_definition ⇒ Hash?
Reader for the resource_definition.
-
.resource_definition_is_invalid? ⇒ Boolean
do we have an invalid resource.
-
.respond_to?(*args) ⇒ Boolean
Load our resource definition to make sure we know what this class responds to.
-
.set_class_attributes_upon_load ⇒ Boolean
This makes a request to new_element_path and sets up the correct attribute, scope and association methods for this class.
-
.site_with_connection_reset=(site) ⇒ String
Handles the setting of site while reloading the resource definition to ensure we have the latest definition.
-
.split_options(options = {}) ⇒ Object
and the other containing the leftovers.
-
.timeout_with_connection_reset=(timeout) ⇒ Fixnum
Set the timeout on the connection and connect.
-
.token_with_new_token_set=(new_token) ⇒ String
Handles the setting of tokens on descendants.
Instance Method Summary collapse
- #==(other) ⇒ Object
- #destroy ⇒ Object
- #dup ⇒ Object
- #encode(options = {}) ⇒ Object
- #eql?(other) ⇒ Boolean
- #hash ⇒ Object
- #id ⇒ Object
-
#id=(id) ⇒ Object
Bypass dirty tracking for this field.
-
#initialize(attributes = {}) ⇒ Base
constructor
A new instance of Base.
- #new? ⇒ Boolean (also: #new_record?)
- #persisted? ⇒ Boolean
- #prefix_attribute_names ⇒ Object
- #prefix_options ⇒ Object
- #reload ⇒ Object
- #save(*args) ⇒ Object
- #save!(*args) ⇒ Object
-
#serializable_hash(options = {}) ⇒ Object
TODO: (Updated 10/26/2013): Leaving this old message here though the behavior is now in Serializer.
- #to_json(options = {}) ⇒ Object
- #to_param ⇒ Object
-
#to_s ⇒ Object
(also: #inspect)
Override to_s and inspect so they only show attributes and not associations, this prevents force loading of associations when we call to_s or inspect on a descendent of base but allows it if we try to evaluate an association directly.
-
#to_xml(options = {}) ⇒ Object
Methods for serialization as json or xml, relying on the serializable_hash method.
- #update_attributes(attrs) ⇒ Object
Methods included from Scopes
#scope?, #scope_attributes, #scopes
Methods included from Callbacks
#create_with_callbacks, #destroy_with_callbacks, #save_with_callbacks, #update_with_callbacks
Methods included from Observing
Methods included from Attributes
#[], #[]=, #attribute?, #attributes, #attributes=, #clear_changes, #make_changes_current, #method_missing, #protected_attribute?, #public_attribute?, #read_attribute, #read_attribute_before_type_cast, #reset_changes, #respond_to?, #save_with_dirty_tracking, #typecast_attribute_for_read, #typecast_attribute_for_write, #write_attribute
Methods included from ModelErrors
#errors, #load_remote_errors, #save_with_validations, #valid?
Constructor Details
#initialize(attributes = {}) ⇒ Base
493 494 495 496 497 498 499 500 501 |
# File 'lib/api_resource/base.rb', line 493 def initialize(attributes = {}) # call super's initialize to set up any variables that we need super(attributes) # if we initialize this class, load the attributes self.class.load_resource_definition # Now we can make a call to setup the inheriting # klass with its attributes self.attributes = attributes end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class ApiResource::Attributes
Class Attribute Details
.collection_name ⇒ Object
collection_name with default
338 339 340 |
# File 'lib/api_resource/base.rb', line 338 def collection_name @collection_name ||= ActiveSupport::Inflector.pluralize(self.element_name) end |
.element_name ⇒ Object
element_name with default
334 335 336 |
# File 'lib/api_resource/base.rb', line 334 def element_name @element_name ||= self.model_name.element end |
Class Method Details
.build(attributes = {}) ⇒ Object
375 376 377 |
# File 'lib/api_resource/base.rb', line 375 def build(attributes = {}) self.new(attributes) end |
.collection_path(prefix_options = {}, query_options = nil) ⇒ Object
368 369 370 371 372 373 |
# File 'lib/api_resource/base.rb', line 368 def collection_path( = {}, = nil) , = () if .nil? # Fall back on this rather than search without the id "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}" end |
.connection(refresh = false) ⇒ Connection
Accessor for the connection
73 74 75 76 77 78 79 |
# File 'lib/api_resource/base.rb', line 73 def connection(refresh = false) if refresh || @connection.nil? @connection = Connection.new(self.site, self.format, self.headers) end @connection.timeout = self.timeout @connection end |
.create(attributes = {}) ⇒ Object
379 380 381 |
# File 'lib/api_resource/base.rb', line 379 def create(attributes = {}) self.new(attributes).tap{ |resource| resource.save } end |
.delete(id, options = {}) ⇒ Object
Deletes the resources with the ID in the id parameter.
Options
All options specify prefix and query parameters.
Examples
Event.delete(2) # sends DELETE /events/2
Event.create(name: 'Free Concert', location: 'Community Center')
my_event = Event.find(:first) # let's assume this is event with ID 7
Event.delete(my_event.id) # sends DELETE /events/7
# Let's assume a request to events/5/cancel.xml
Event.delete(params[:id]) # sends DELETE /events/5
398 399 400 |
# File 'lib/api_resource/base.rb', line 398 def delete(id, = {}) connection.delete(element_path(id, )) end |
.element_path(id, prefix_options = {}, query_options = nil) ⇒ Object
alias_method :set_prefix, :prefix= alias_method :set_element_name, :element_name= alias_method :set_collection_name, :collection_name=
346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/api_resource/base.rb', line 346 def element_path(id, = {}, = nil) , = () if .nil? # If we have a prefix, we need a foreign key id # This regex detects '//', which means no foreign key id is present. if prefix() =~ /\/\/$/ "/#{collection_name}/#{URI.escape id.to_s}.#{format.extension}#{query_string(query_options)}" else # Fall back on this rather than search without the id "#{prefix(prefix_options)}#{collection_name}/#{URI.escape id.to_s}.#{format.extension}#{query_string(query_options)}" end end |
.format_with_mimetype_or_format_set=(mime_type_or_format) ⇒ MimeType
Handles the setting of format to a MimeType
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/api_resource/base.rb', line 87 def format_with_mimetype_or_format_set=(mime_type_or_format) if mime_type_or_format.is_a?(Symbol) format = ApiResource::Formats[mime_type_or_format] else format = mime_type_or_format end self.format_without_mimetype_or_format_set = format if self.site self.connection.format = format end format end |
.headers ⇒ Hash
Reader for headers
105 106 107 108 109 |
# File 'lib/api_resource/base.rb', line 105 def headers {}.tap do |ret| ret['Lifebooker-Token'] = self.token if self.token.present? end end |
.inherited(klass) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/api_resource/base.rb', line 111 def inherited(klass) # Call the methods of the superclass to make sure inheritable accessors and the like have been inherited super # Now we need to define the inherited method on the klass that's doing the inheriting # it calls super which will allow the chaining effect we need klass.instance_eval " def inherited(klass)\n klass.send(:define_singleton_method, :collection_name, lambda {self.superclass.collection_name})\n super(klass)\n end\n EOE\n true\nend\n", __FILE__, __LINE__ + 1 |
.load_resource_definition ⇒ Boolean
Explicit call to load the resource definition
loaded
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/api_resource/base.rb', line 130 def load_resource_definition unless instance_variable_defined?(:@resource_definition) # Lock the mutex to make sure only one thread does # this at a time self.resource_definition_mutex.synchronize do # once we have the lock, check to make sure the resource # definition wasn't fetched while we were sleeping return true if instance_variable_defined?(:@resource_definition) # the last time we checked @resource_load_time = Time.now # set to not nil so we don't get an infinite loop @resource_definition = true self.set_class_attributes_upon_load return true end end # we didn't do anything false end |
.new_element_path(prefix_options = {}) ⇒ Object
path to find
360 361 362 363 364 365 366 |
# File 'lib/api_resource/base.rb', line 360 def new_element_path( = {}) File.join( self.prefix(), self.collection_name, "new.#{format.extension}" ) end |
.open_timeout_with_connection_reset=(timeout) ⇒ Fixnum
Set the open timeout on the connection and connect
157 158 159 160 |
# File 'lib/api_resource/base.rb', line 157 def open_timeout_with_connection_reset=(timeout) @connection = nil self.open_timeout_without_connection_reset = timeout end |
.prefix(options = {}) ⇒ String
Are the options used?
Prefix for the resource path
171 172 173 174 175 176 |
# File 'lib/api_resource/base.rb', line 171 def prefix( = {}) default = (self.site ? self.site.path : '/') default << '/' unless default[-1..-1] == '/' self.prefix = default prefix() end |
.prefix=(value = '/') ⇒ Object
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/api_resource/base.rb', line 314 def prefix=(value = '/') prefix_call = value.gsub(/:\w+/) { |key| "\#{URI.escape options[#{key}].to_s}" } @prefix_parameters = nil silence_warnings do instance_eval " def prefix_source() \"\#{value}\" end\n def prefix(options={})\n ret = \"\#{prefix_call}\"\n ret =~ Regexp.new(Regexp.escape(\"//\")) ? \"/\" : ret\n end\n EOE\n end\nrescue Exception => e\n logger.error \"Couldn't set prefix: \#{e}\\n \#{code}\" if logger\n raise\nend\n", __FILE__, __LINE__ + 1 |
.prefix_source ⇒ Object
180 181 182 183 |
# File 'lib/api_resource/base.rb', line 180 def prefix_source prefix prefix_source end |
.reload_resource_definition ⇒ Boolean Also known as: reload_class_attributes
Clear the old resource definition and reload it from the server
190 191 192 193 194 195 196 197 198 |
# File 'lib/api_resource/base.rb', line 190 def reload_resource_definition # clear the public_attribute_names, protected_attribute_names if instance_variable_defined?(:@resource_definition) remove_instance_variable(:@resource_definition) self.clear_attributes self. end self.load_resource_definition end |
.reset_connection ⇒ Boolean
Reset our connection instance so that we will reconnect the next time we need it
207 208 209 210 |
# File 'lib/api_resource/base.rb', line 207 def reset_connection remove_instance_variable(:@connection) if @connection.present? true end |
.resource_definition ⇒ Hash?
Reader for the resource_definition
216 217 218 |
# File 'lib/api_resource/base.rb', line 216 def resource_definition @resource_definition end |
.resource_definition_is_invalid? ⇒ Boolean
do we have an invalid resource
403 404 405 406 407 408 409 410 |
# File 'lib/api_resource/base.rb', line 403 def resource_definition_is_invalid? # if we have a Hash, it's valid return false if @resource_definition.is_a?(Hash) # if we have no recheck time, it's invalid return true if @resource_load_time.nil? # have we checked in the last minute? return @resource_load_time < Time.now - 1.minute end |
.respond_to?(*args) ⇒ Boolean
Load our resource definition to make sure we know what this class responds to
225 226 227 228 |
# File 'lib/api_resource/base.rb', line 225 def respond_to?(*args) self.load_resource_definition super end |
.set_class_attributes_upon_load ⇒ Boolean
This makes a request to new_element_path and sets up the correct attribute, scope and association methods for this class
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/api_resource/base.rb', line 235 def set_class_attributes_upon_load # this only happens in subclasses return true if self == ApiResource::Base begin @resource_definition = self.connection.get( self.new_element_path, self.headers ) # set up methods derived from our class definition self.define_all_attributes self.define_all_scopes self.define_all_associations # Swallow up any loading errors because the site may be incorrect rescue Exception => e self.handle_resource_definition_error(e) end true end |
.site_with_connection_reset=(site) ⇒ String
Handles the setting of site while reloading the resource definition to ensure we have the latest definition
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/api_resource/base.rb', line 261 def site_with_connection_reset=(site) # store so we can reload attributes if the site changed old_site = self.site.to_s.clone @connection = nil if site.nil? self.site_without_connection_reset = nil # no site, so we'll skip the reload return site else self.site_without_connection_reset = create_site_uri_from(site) end # reset class attributes and try to reload them if the site changed unless self.site.to_s == old_site self.reload_resource_definition end return site end |
.split_options(options = {}) ⇒ Object
and the other containing the leftovers.
414 415 416 417 418 419 420 421 422 |
# File 'lib/api_resource/base.rb', line 414 def ( = {}) , = {}, {} ( || {}).each do |key, value| next if key.blank? (prefix_parameters.include?(key.to_sym) ? : )[key.to_sym] = value end [ , ] end |
.timeout_with_connection_reset=(timeout) ⇒ Fixnum
Set the timeout on the connection and connect
289 290 291 292 |
# File 'lib/api_resource/base.rb', line 289 def timeout_with_connection_reset=(timeout) @connection = nil self.timeout_without_connection_reset = timeout end |
.token_with_new_token_set=(new_token) ⇒ String
Handles the setting of tokens on descendants
301 302 303 304 305 306 307 308 |
# File 'lib/api_resource/base.rb', line 301 def token_with_new_token_set=(new_token) self.token_without_new_token_set = new_token self.connection(true) self.descendants.each do |child| child.send(:token=, new_token) end new_token end |
Instance Method Details
#==(other) ⇒ Object
521 522 523 |
# File 'lib/api_resource/base.rb', line 521 def ==(other) other.equal?(self) || (other.instance_of?(self.class) && other.id == self.id) end |
#destroy ⇒ Object
550 551 552 |
# File 'lib/api_resource/base.rb', line 550 def destroy connection.delete(element_path(self.id), self.class.headers) end |
#dup ⇒ Object
533 534 535 |
# File 'lib/api_resource/base.rb', line 533 def dup self.class.instantiate_record(self.attributes) end |
#encode(options = {}) ⇒ Object
554 555 556 |
# File 'lib/api_resource/base.rb', line 554 def encode( = {}) self.send("to_#{self.class.format.extension}", ) end |
#eql?(other) ⇒ Boolean
525 526 527 |
# File 'lib/api_resource/base.rb', line 525 def eql?(other) self == other end |
#hash ⇒ Object
529 530 531 |
# File 'lib/api_resource/base.rb', line 529 def hash id.hash end |
#id ⇒ Object
512 513 514 |
# File 'lib/api_resource/base.rb', line 512 def id self.read_attribute(self.class.primary_key) end |
#id=(id) ⇒ Object
Bypass dirty tracking for this field
517 518 519 |
# File 'lib/api_resource/base.rb', line 517 def id=(id) @attributes[self.class.primary_key] = id end |
#new? ⇒ Boolean Also known as: new_record?
503 504 505 |
# File 'lib/api_resource/base.rb', line 503 def new? id.blank? end |
#persisted? ⇒ Boolean
508 509 510 |
# File 'lib/api_resource/base.rb', line 508 def persisted? !new? end |
#prefix_attribute_names ⇒ Object
585 586 587 588 |
# File 'lib/api_resource/base.rb', line 585 def prefix_attribute_names return [] unless self.class.prefix_source =~ /\:/ self.class.prefix_source.scan(/\:(\w+)/).collect{|match| match.first.to_sym} end |
#prefix_options ⇒ Object
576 577 578 579 580 581 582 583 |
# File 'lib/api_resource/base.rb', line 576 def return {} unless self.class.prefix_source =~ /\:/ ret = {} self.prefix_attribute_names.each do |name| ret[name] = self.send(name) end ret end |
#reload ⇒ Object
558 559 560 561 562 563 564 565 566 567 568 |
# File 'lib/api_resource/base.rb', line 558 def reload # find the record from the remote service reloaded = self.class.find(self.id) # clear out the attributes cache @attributes_cache = HashWithIndifferentAccess.new # set up our attributes cache on our record @attributes = reloaded.instance_variable_get(:@attributes) reloaded end |
#save(*args) ⇒ Object
542 543 544 |
# File 'lib/api_resource/base.rb', line 542 def save(*args) new? ? create(*args) : update(*args) end |
#save!(*args) ⇒ Object
546 547 548 |
# File 'lib/api_resource/base.rb', line 546 def save!(*args) save(*args) || raise(ApiResource::ResourceInvalid.new(self)) end |
#serializable_hash(options = {}) ⇒ Object
TODO: (Updated 10/26/2013): Leaving this old message here though the behavior is now in Serializer. Any changes should be done there
this method needs to change seriously to fit in with the new typecasting scheme, it should call self.outgoing_attributes which should return the converted versions after calling to_api, that should be implemented in the attributes module though
624 625 626 |
# File 'lib/api_resource/base.rb', line 624 def serializable_hash( = {}) return Serializer.new(self, ).to_hash end |
#to_json(options = {}) ⇒ Object
604 605 606 607 608 609 610 611 612 613 614 |
# File 'lib/api_resource/base.rb', line 604 def to_json( = {}) # handle whether or not we include root in our JSON if self.class.include_root_in_json ret = { self.class.element_name => self.serializable_hash() } else ret = self.serializable_hash() end ret.to_json end |
#to_param ⇒ Object
570 571 572 573 574 |
# File 'lib/api_resource/base.rb', line 570 def to_param # Stolen from active_record. # We can't use alias_method here, because method 'id' optimizes itself on the fly. id && id.to_s # Be sure to stringify the id for routes end |
#to_s ⇒ Object Also known as: inspect
Override to_s and inspect so they only show attributes and not associations, this prevents force loading of associations when we call to_s or inspect on a descendent of base but allows it if we try to evaluate an association directly
594 595 596 |
# File 'lib/api_resource/base.rb', line 594 def to_s return "#<#{self.class}:#{(self.object_id * 2).to_s(16)} @attributes=#{self.attributes}" end |
#to_xml(options = {}) ⇒ Object
Methods for serialization as json or xml, relying on the serializable_hash method
600 601 602 |
# File 'lib/api_resource/base.rb', line 600 def to_xml( = {}) self.serializable_hash().to_xml(root: self.class.element_name) end |
#update_attributes(attrs) ⇒ Object
537 538 539 540 |
# File 'lib/api_resource/base.rb', line 537 def update_attributes(attrs) self.attributes = attrs self.save end |