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.
-
.resource_definition_mutex ⇒ Mutex
Mutex so that multiple Threads don’t try to load the resource definition at the same time.
-
.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
Returns a new instance of Base.
500 501 502 503 504 505 506 507 508 |
# File 'lib/api_resource/base.rb', line 500 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
345 346 347 |
# File 'lib/api_resource/base.rb', line 345 def collection_name @collection_name ||= ActiveSupport::Inflector.pluralize(self.element_name) end |
.element_name ⇒ Object
element_name with default
341 342 343 |
# File 'lib/api_resource/base.rb', line 341 def element_name @element_name ||= self.model_name.element end |
Class Method Details
.build(attributes = {}) ⇒ Object
382 383 384 |
# File 'lib/api_resource/base.rb', line 382 def build(attributes = {}) self.new(attributes) end |
.collection_path(prefix_options = {}, query_options = nil) ⇒ Object
375 376 377 378 379 380 |
# File 'lib/api_resource/base.rb', line 375 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
71 72 73 74 75 76 77 |
# File 'lib/api_resource/base.rb', line 71 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
386 387 388 |
# File 'lib/api_resource/base.rb', line 386 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
405 406 407 |
# File 'lib/api_resource/base.rb', line 405 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=
353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/api_resource/base.rb', line 353 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
85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/api_resource/base.rb', line 85 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
103 104 105 106 107 |
# File 'lib/api_resource/base.rb', line 103 def headers {}.tap do |ret| ret['Lifebooker-Token'] = self.token if self.token.present? end end |
.inherited(klass) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/api_resource/base.rb', line 109 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
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/api_resource/base.rb', line 128 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
367 368 369 370 371 372 373 |
# File 'lib/api_resource/base.rb', line 367 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
155 156 157 158 |
# File 'lib/api_resource/base.rb', line 155 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
169 170 171 172 173 174 |
# File 'lib/api_resource/base.rb', line 169 def prefix( = {}) default = (self.site ? self.site.path : '/') default << '/' unless default[-1..-1] == '/' self.prefix = default prefix() end |
.prefix=(value = '/') ⇒ Object
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/api_resource/base.rb', line 321 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
178 179 180 181 |
# File 'lib/api_resource/base.rb', line 178 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
188 189 190 191 192 193 194 195 196 |
# File 'lib/api_resource/base.rb', line 188 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
214 215 216 217 |
# File 'lib/api_resource/base.rb', line 214 def reset_connection remove_instance_variable(:@connection) if @connection.present? true end |
.resource_definition ⇒ Hash?
Reader for the resource_definition
223 224 225 |
# File 'lib/api_resource/base.rb', line 223 def resource_definition @resource_definition end |
.resource_definition_is_invalid? ⇒ Boolean
do we have an invalid resource
410 411 412 413 414 415 416 417 |
# File 'lib/api_resource/base.rb', line 410 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 |
.resource_definition_mutex ⇒ Mutex
Mutex so that multiple Threads don’t try to load the resource definition at the same time
205 206 207 |
# File 'lib/api_resource/base.rb', line 205 def resource_definition_mutex @resource_definition_mutex ||= Mutex.new end |
.respond_to?(*args) ⇒ Boolean
Load our resource definition to make sure we know what this class responds to
232 233 234 235 |
# File 'lib/api_resource/base.rb', line 232 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
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/api_resource/base.rb', line 242 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
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/api_resource/base.rb', line 268 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.
421 422 423 424 425 426 427 428 429 |
# File 'lib/api_resource/base.rb', line 421 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
296 297 298 299 |
# File 'lib/api_resource/base.rb', line 296 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
308 309 310 311 312 313 314 315 |
# File 'lib/api_resource/base.rb', line 308 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
528 529 530 |
# File 'lib/api_resource/base.rb', line 528 def ==(other) other.equal?(self) || (other.instance_of?(self.class) && other.id == self.id) end |
#destroy ⇒ Object
557 558 559 |
# File 'lib/api_resource/base.rb', line 557 def destroy connection.delete(element_path(self.id), self.class.headers) end |
#dup ⇒ Object
540 541 542 |
# File 'lib/api_resource/base.rb', line 540 def dup self.class.instantiate_record(self.attributes) end |
#encode(options = {}) ⇒ Object
561 562 563 |
# File 'lib/api_resource/base.rb', line 561 def encode( = {}) self.send("to_#{self.class.format.extension}", ) end |
#eql?(other) ⇒ Boolean
532 533 534 |
# File 'lib/api_resource/base.rb', line 532 def eql?(other) self == other end |
#hash ⇒ Object
536 537 538 |
# File 'lib/api_resource/base.rb', line 536 def hash id.hash end |
#id ⇒ Object
519 520 521 |
# File 'lib/api_resource/base.rb', line 519 def id self.read_attribute(self.class.primary_key) end |
#id=(id) ⇒ Object
Bypass dirty tracking for this field
524 525 526 |
# File 'lib/api_resource/base.rb', line 524 def id=(id) @attributes[self.class.primary_key] = id end |
#new? ⇒ Boolean Also known as: new_record?
510 511 512 |
# File 'lib/api_resource/base.rb', line 510 def new? id.blank? end |
#persisted? ⇒ Boolean
515 516 517 |
# File 'lib/api_resource/base.rb', line 515 def persisted? !new? end |
#prefix_attribute_names ⇒ Object
592 593 594 595 |
# File 'lib/api_resource/base.rb', line 592 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
583 584 585 586 587 588 589 590 |
# File 'lib/api_resource/base.rb', line 583 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
565 566 567 568 569 570 571 572 573 574 575 |
# File 'lib/api_resource/base.rb', line 565 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
549 550 551 |
# File 'lib/api_resource/base.rb', line 549 def save(*args) new? ? create(*args) : update(*args) end |
#save!(*args) ⇒ Object
553 554 555 |
# File 'lib/api_resource/base.rb', line 553 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
631 632 633 |
# File 'lib/api_resource/base.rb', line 631 def serializable_hash( = {}) return Serializer.new(self, ).to_hash end |
#to_json(options = {}) ⇒ Object
611 612 613 614 615 616 617 618 619 620 621 |
# File 'lib/api_resource/base.rb', line 611 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
577 578 579 580 581 |
# File 'lib/api_resource/base.rb', line 577 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
601 602 603 |
# File 'lib/api_resource/base.rb', line 601 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
607 608 609 |
# File 'lib/api_resource/base.rb', line 607 def to_xml( = {}) self.serializable_hash().to_xml(root: self.class.element_name) end |
#update_attributes(attrs) ⇒ Object
544 545 546 547 |
# File 'lib/api_resource/base.rb', line 544 def update_attributes(attrs) self.attributes = attrs self.save end |