Class: Recurly::Resource

Inherits:
Object
  • Object
show all
Defined in:
lib/recurly/resource.rb,
lib/recurly/resource/pager.rb

Overview

The base class for all Recurly resources (e.g. Account, Subscription, Transaction).

Resources behave much like ActiveModel classes, especially like ActiveRecord.

Life Cycle

To take you through the typical life cycle of a resource, we'll use Account as an example.

Creating a Record

You can instantiate a record before attempting to save it.

 = Recurly::Account.new :first_name => 'Walter'

Once instantiated, you can assign and reassign any attribute.

.first_name = 'Walt'
.last_name = 'White'

When you're ready to save, do so.

.save # => false

If save returns false, validation likely failed. You can check the record for errors.

.errors # => {"account_code"=>["can't be blank"]}

Once the errors are fixed, you can try again.

. = 'heisenberg'
.save # => true

The object will be updated with any information provided by the server (including any UUIDs set).

.created_at # => 2011-04-30 07:13:35 -0700

You can also create accounts in one fell swoop.

Recurly::Account.create(
  :first_name   => 'Jesse'
  :last_name    => 'Pinkman'
  :account_code => 'capn_cook'
)
# => #<Recurly::Account account_code: "capn_cook" ...>

You can use alternative "bang" methods for exception control. If the record fails to save, a Recurly::Resource::Invalid exception will be raised.

begin
   = Recurly::Account.new :first_name => 'Junior'
  .save!
rescue Recurly::Resource::Invalid
  p .errors
end

You can access the invalid record from the exception itself (if, for example, you use the create! method).

begin
  Recurly::Account.create! :first_name => 'Skylar', :last_name => 'White'
rescue Recurly::Resource::Invalid => e
  p e.record.errors
end

Fetching a Record

Records are fetched by their unique identifiers.

 = Recurly::Account.find 'better_call_saul'
# => #<Recurly::Account account_code: "better_call_saul" ...>

If the record doesn't exist, a Recurly::Resource::NotFound exception will be raised.

Updating a Record

Once fetched, a record can be updated with a hash of attributes.

.update_attributes :first_name => 'Saul', :last_name => 'Goodman'
# => true

(A bang method, update_attributes!, will raise Recurly::Resource::Invalid.)

You can also update a record by setting attributes and calling save.

.last_name = 'McGill'
.save # Alternatively, call save!

Deleting a Record

To delete (deactivate, close, etc.) a fetched record, merely call destroy on it.

.destroy # => true

Fetching a List of Records

If you want to iterate over a list of accounts, you can use a Pager.

pager = Account.paginate :per_page => 50

If you want to iterate over every record, a convenience method will automatically paginate:

Account.find_each { || p  }

Direct Known Subclasses

Account, AddOn, Adjustment, BillingInfo, Coupon, Invoice, Plan, Redemption, Subscription, Transaction

Defined Under Namespace

Classes: Invalid, NotFound, Pager

Class Attribute Summary (collapse)

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Resource) initialize(attributes = {}) {|_self| ... }

Returns A new resource instance.

Parameters:

  • attributes (Hash) (defaults to: {})

    A hash of attributes.

Yields:

  • (_self)

Yield Parameters:



532
533
534
535
536
537
538
539
540
541
# File 'lib/recurly/resource.rb', line 532

def initialize attributes = {}
  if instance_of? Resource
    raise Error,
      "#{self.class} is an abstract class and cannot be instantiated"
  end

  @attributes, @new_record, @destroyed, @uri, @href = {}, true, false
  self.attributes = attributes
  yield self if block_given?
end

Class Attribute Details

+ (Array?) attribute_names (readonly)

Returns The list of attribute names defined for the resource class.

Returns:

  • (Array, nil)

    The list of attribute names defined for the resource class.



229
230
231
# File 'lib/recurly/resource.rb', line 229

def attribute_names
  @attribute_names
end

Instance Attribute Details

- (Hash) attributes

Returns The raw hash of record attributes.

Returns:

  • (Hash)

    The raw hash of record attributes.



518
519
520
# File 'lib/recurly/resource.rb', line 518

def attributes
  @attributes
end

- (String?) etag (readonly)

Returns An ETag for the current record.

Returns:

  • (String, nil)

    An ETag for the current record.



525
526
527
# File 'lib/recurly/resource.rb', line 525

def etag
  @etag
end

- (Net::HTTPResponse?) response (readonly)

Returns The most recent response object for the record (updated during #save and #destroy).

Returns:

  • (Net::HTTPResponse, nil)

    The most recent response object for the record (updated during #save and #destroy).



522
523
524
# File 'lib/recurly/resource.rb', line 522

def response
  @response
end

- (String?) uri

Returns The unique resource identifier (URI) of the record (if persisted).

Examples:

Recurly::Account.new(:account_code => "account_code").uri # => nil
Recurly::Account.find("account_code").uri
# => "https://api.recurly.com/v2/accounts/account_code"

Returns:

  • (String, nil)

    The unique resource identifier (URI) of the record (if persisted).



820
821
822
# File 'lib/recurly/resource.rb', line 820

def uri
  @href ||= ((API.base_uri + path).to_s if persisted?)
end

Class Method Details

+ (Object) all(options = {})



250
251
252
# File 'lib/recurly/resource.rb', line 250

def all options = {}
  paginate(options).to_a
end

+ (Hash) associations

Returns A list of association names for the current class.

Returns:

  • (Hash)

    A list of association names for the current class.



423
424
425
426
427
428
429
430
431
# File 'lib/recurly/resource.rb', line 423

def associations
  @associations ||= begin
    unless const_defined? :Associations
      include const_set :Associations, Module.new
    end

    { :has_many => [], :has_one => [], :belongs_to => [] }
  end
end

+ (Proc) belongs_to(parent_name, options = {})

Establishes a belongs_to association.

Returns:

  • (Proc)


492
493
494
495
496
497
498
499
500
501
502
# File 'lib/recurly/resource.rb', line 492

def belongs_to parent_name, options = {}
  associations[:belongs_to] << parent_name.to_s
  self::Associations.module_eval {
    define_method(parent_name) { self[parent_name] }
    if options.key?(:readonly) && options[:readonly] == false
      define_method("#{parent_name}=") { |parent|
        self[parent_name] = parent
      }
    end
  }
end

+ (String) collection_name Also known as: collection_path

Returns The underscored, pluralized name of the resource class.

Examples:

Recurly::Account.collection_name # => "accounts"

Returns:

  • (String)

    The underscored, pluralized name of the resource class.



167
168
169
# File 'lib/recurly/resource.rb', line 167

def collection_name
  Helper.pluralize Helper.underscore(resource_name)
end

+ (Integer) count

Returns The total record count of the resource in question.

Examples:

Recurly::Account.count # => 42

Returns:

  • (Integer)

    The total record count of the resource in question.

See Also:



286
287
288
# File 'lib/recurly/resource.rb', line 286

def count
  paginate.count
end

+ (Resource) create(attributes = {})

Instantiates and attempts to save a record.

Returns:

Raises:

See Also:



335
336
337
# File 'lib/recurly/resource.rb', line 335

def create attributes = {}
  new(attributes) { |record| record.save }
end

+ (Resource) create!(attributes = {})

Instantiates and attempts to save a record.

Returns:

Raises:

See Also:



345
346
347
# File 'lib/recurly/resource.rb', line 345

def create! attributes = {}
  new(attributes) { |record| record.save! }
end

+ (Array) define_attribute_methods(attribute_names)

Returns Per attribute, defines readers, writers, boolean and change-tracking methods.

Examples:

class Account < Resource
  define_attribute_methods [:name]
end

a = Account.new
a.name?            # => false
a.name             # => nil
a.name = "Stephen"
a.name?            # => true
a.name             # => "Stephen"
a.name_changed?    # => true
a.name_was         # => nil
a.name_change      # => [nil, "Stephen"]

Parameters:

  • attribute_names (Array)

    An array of attribute names.

Returns:

  • (Array)

    Per attribute, defines readers, writers, boolean and change-tracking methods.



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/recurly/resource.rb', line 206

def define_attribute_methods attribute_names
  @attribute_names = attribute_names.map! { |m| m.to_s }.sort!.freeze
  remove_const :AttributeMethods if const_defined? :AttributeMethods
  include const_set :AttributeMethods, Module.new {
    attribute_names.each do |name|
      define_method(name) { self[name] }                       # Get.
      define_method("#{name}=") { |value| self[name] = value } # Set.
      define_method("#{name}?") { !!self[name] }               # Present.
      define_method("#{name}_change") { changes[name] }        # Dirt...
      define_method("#{name}_changed?") { changed_attributes.key? name }
      define_method("#{name}_was") { changed_attributes[name] }
      define_method("#{name}_previously_changed?") {
        previous_changes.key? name
      }
      define_method("#{name}_previously_was") {
        previous_changes[name].first if previous_changes.key? name
      }
    end
  }
end

+ (Object) embedded!



509
510
511
512
513
514
# File 'lib/recurly/resource.rb', line 509

def embedded!
  private_class_method(*%w(
    new create create! paginate find_each scoped where all
  ))
  private :initialize
end

+ (Resource) find(uuid, options = {})

Returns A record matching the designated unique identifier.

Examples:

Recurly::Account.find "heisenberg"
# => #<Recurly::Account account_code: "heisenberg", ...>

Parameters:

  • uuid (String)

    The unique identifier of the resource to be retrieved.

  • options (Hash) (defaults to: {})

    A hash of options.

Options Hash (options):

  • :etag (String)

    When set, will raise API::NotModified if the record content has not changed.

Returns:

  • (Resource)

    A record matching the designated unique identifier.

Raises:

  • (Error)

    If the resource has no identifier (and thus cannot be retrieved).

  • (NotFound)

    If no resource can be found for the supplied identifier (or the supplied identifier is nil).

  • (API::NotModified)

    If the :etag option is set and matches the server's.



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/recurly/resource.rb', line 311

def find uuid, options = {}
  if uuid.nil?
    # Should we raise an ArgumentError, instead?
    raise NotFound, "can't find a record with nil identifier"
  end

  request_options = {}
  if etag = options[:etag]
    request_options[:head] = { 'If-None-Match' => etag }
  end

  uri = uuid =~ /^http/ ? uuid : member_path(uuid)
  begin
    from_response API.get(uri, {}, request_options)
  rescue API::NotFound => e
    raise NotFound, e.description
  end
end

+ (nil) find_each(per_page = 50) {|record| ... }

Iterates through every record by automatically paging.

Examples:

Recurly::Account.find_each { |a| p a }

Parameters:

  • per_page (Integer) (defaults to: 50)

    The number of records returned per request.

Yields:

  • (record)

Returns:

  • (nil)

See Also:



278
279
280
# File 'lib/recurly/resource.rb', line 278

def find_each per_page = 50
  paginate(:per_page => per_page).find_each(&Proc.new)
end

+ (Resource?) first

Returns:



292
293
294
# File 'lib/recurly/resource.rb', line 292

def first
  paginate(:per_page => 1).first
end

+ (Resource) from_response(response)

Instantiates a record from an HTTP response, setting the record's response attribute in the process.

Parameters:

  • response (Net::HTTPResponse)

Returns:



354
355
356
357
358
# File 'lib/recurly/resource.rb', line 354

def from_response response
  record = from_xml response.body
  record.instance_eval { @etag, @response = response['ETag'], response }
  record
end

+ (Resource) from_xml(xml)

Instantiates a record from an XML blob: either a String or XML element.

Assuming the record is from an API response, the record is flagged as persisted.

Parameters:

  • xml (String, REXML::Element, Nokogiri::XML::Node)

Returns:

See Also:



368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/recurly/resource.rb', line 368

def from_xml xml
  xml = XML.new xml
  if xml.name == member_name
    record = new
  elsif Recurly.const_defined?(class_name = Helper.classify(xml.name))
    record = Recurly.const_get(class_name).new
  elsif root = xml.root and root.elements.empty?
    return XML.cast root
  else
    record = {}
  end

  xml.root.attributes.each do |name, value|
    record.instance_variable_set "@#{name}", value.to_s
  end

  xml.each_element do |el|
    if el.name == 'a'
      name, uri = el.attribute('name').value, el.attribute('href').value
      record[name] = case el.attribute('method').to_s
        when 'get', '' then proc { |*opts| API.get uri, {}, *opts }
        when 'post'    then proc { |*opts| API.post uri, nil, *opts }
        when 'put'     then proc { |*opts| API.put uri, nil, *opts }
        when 'delete'  then proc { |*opts| API.delete uri, *opts }
      end
      next
    end

    if el.children.empty? && href = el.attribute('href')
      resource_class = Recurly.const_get(
        Helper.classify(el.attribute('type') || el.name)
      )
      record[el.name] = case el.name
      when *associations[:has_many]
        Pager.new resource_class, :uri => href.value, :parent => record
      when *(associations[:has_one] + associations[:belongs_to])
        lambda {
          begin
            relation = resource_class.from_response API.get(href.value)
            relation.attributes[member_name] = record
            relation
          rescue Recurly::API::NotFound
          end
        }
      end
    else
      record[el.name] = XML.cast el
    end
  end

  record.persist! if record.respond_to? :persist!
  record
end

+ (Proc?) has_many(collection_name, options = {})

Establishes a has_many association.

Parameters:

  • collection_name (Symbol)

    Association name.

  • options (Hash) (defaults to: {})

    A hash of association options.

Options Hash (options):

  • :readonly (true, false)

    Don't define a setter.

Returns:

  • (Proc, nil)


439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/recurly/resource.rb', line 439

def has_many collection_name, options = {}
  associations[:has_many] << collection_name.to_s
  self::Associations.module_eval {
    define_method(collection_name) {
      self[collection_name] ||= []
    }
    if options.key?(:readonly) && options[:readonly] == false
      define_method("#{collection_name}=") { |collection|
        self[collection_name] = collection
      }
    end
  }
end

+ (Proc?) has_one(member_name, options = {})

Establishes a has_one association.

Parameters:

  • member_name (Symbol)

    Association name.

  • options (Hash) (defaults to: {})

    A hash of association options.

Options Hash (options):

  • :readonly (true, false)

    Don't define a setter.

Returns:

  • (Proc, nil)


459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/recurly/resource.rb', line 459

def has_one member_name, options = {}
  associations[:has_one] << member_name.to_s
  self::Associations.module_eval {
    define_method(member_name) { self[member_name] }
    if options.key?(:readonly) && options[:readonly] == false
      associated = Recurly.const_get Helper.classify(member_name)
      define_method("#{member_name}=") { |member|
        associated_uri = "#{path}/#{member_name}"
        self[member_name] = case member
        when Hash
          associated.send :new, member.merge(:uri => associated_uri)
        when associated_class
          member.uri = associated_uri and member
        else
          raise ArgumentError, "expected #{associated_class}"
        end
      }
      define_method("build_#{member_name}") { |*args|
        attributes = args.shift || {}
        self[member_name] = associated.send(
          :new, attributes.merge(:uri => "#{path}/#{member_name}")
        )
      }
      define_method("create_#{member_name}") { |*args|
        send("build_#{member_name}", *args).tap { |child| child.save }
      }
    end
  }
end

+ (String) member_name

Returns The underscored name of the resource class.

Examples:

Recurly::Account.member_name # => "account"

Returns:

  • (String)

    The underscored name of the resource class.



175
176
177
# File 'lib/recurly/resource.rb', line 175

def member_name
  Helper.underscore resource_name
end

+ (String) member_path(uuid)

Returns The relative path to a resource's identifier from the API's base URI.

Examples:

Recurly::Account.member_path "code" # => "accounts/code"
Recurly::Account.member_path nil    # => "accounts"

Parameters:

  • uuid (String, nil)

Returns:

  • (String)

    The relative path to a resource's identifier from the API's base URI.



185
186
187
# File 'lib/recurly/resource.rb', line 185

def member_path uuid
  [collection_path, uuid].compact.join '/'
end

+ (Pager) paginate(options = {}) Also known as: scoped, where

Returns A pager with an iterable collection of records

Examples:

Fetch 50 records and iterate over them

Recurly::Account.paginate(:per_page => 50).each { |a| p a }

Fetch records before January 1, 2011

Recurly::Account.paginate(:cursor => Time.new(2011, 1, 1))

Parameters:

  • options (Hash) (defaults to: {})

    A hash of pagination options

Options Hash (options):

  • :per_page (Integer)

    The number of records returned per page

  • :cursor (DateTime, Time, Integer)

    A timestamp that the pager will skim back to and return records created before it

  • :etag (String)

    When set, will raise API::NotModified if the pager's loaded page content has not changed

Returns:

  • (Pager)

    A pager with an iterable collection of records



244
245
246
# File 'lib/recurly/resource.rb', line 244

def paginate options = {}
  Pager.new self, options
end

+ (:has_many, ...) reflect_on_association(name)

Returns An association type.

Returns:

  • (:has_many, :has_one, :belongs_to, nil)

    An association type.



505
506
507
# File 'lib/recurly/resource.rb', line 505

def reflect_on_association name
  a = associations.find { |k, v| v.include? name.to_s } and a.first
end

+ (String) resource_name

Returns The demodulized name of the resource class.

Examples:

Recurly::Account.name # => "Account"

Returns:

  • (String)

    The demodulized name of the resource class.



159
160
161
# File 'lib/recurly/resource.rb', line 159

def resource_name
  Helper.demodulize name
end

+ (Proc) scope(name, params = {})

Defines a new resource scope.

Parameters:

  • name (Symbol)

    the scope name

  • params (Hash) (defaults to: {})

    the scope params

Returns:

  • (Proc)


264
265
266
267
268
# File 'lib/recurly/resource.rb', line 264

def scope name, params = {}
  scopes[name = name.to_s] = params
  extend const_set :Scopes, Module.new unless const_defined? :Scopes
  self::Scopes.send(:define_method, name) { paginate scopes[name] }
end

+ (Hash) scopes

Returns Defined scopes per resource.

Returns:

  • (Hash)

    Defined scopes per resource.



255
256
257
# File 'lib/recurly/resource.rb', line 255

def scopes
  @scopes ||= Recurly::Helper.hash_with_indifferent_read_access
end

Instance Method Details

- (Object) ==(other)



843
844
845
# File 'lib/recurly/resource.rb', line 843

def == other
  other.is_a?(self.class) && other.to_s == to_s
end

- (Array) changed

Returns A list of changed attribute keys.

Returns:

  • (Array)

    A list of changed attribute keys.



571
572
573
# File 'lib/recurly/resource.rb', line 571

def changed
  changed_attributes.keys
end

- (true, false) changed?

Do any attributes have unsaved changes?

Returns:

  • (true, false)


577
578
579
# File 'lib/recurly/resource.rb', line 577

def changed?
  !changed_attributes.empty?
end

- (Hash) changed_attributes

Returns Hash of changed attributes.

Returns:

  • (Hash)

    Hash of changed attributes.

See Also:



566
567
568
# File 'lib/recurly/resource.rb', line 566

def changed_attributes
  @changed_attributes ||= {}
end

- (Hash) changes

Returns Map of changed attributes to original value and new value.

Returns:

  • (Hash)

    Map of changed attributes to original value and new value.



582
583
584
585
586
# File 'lib/recurly/resource.rb', line 582

def changes
  changed_attributes.inject({}) { |changes, (key, original_value)|
    changes[key] = [original_value, self[key]] and changes
  }
end

- (true, false) destroy

Attempts to destroy the record.

(if the record does not persist on Recurly).

Examples:

 = Recurly::Account.find 
race_condition = Recurly::Account.find 
.destroy        # => true
.destroy        # => false (already destroyed)
race_condition.destroy # raises Recurly::Resource::NotFound

Returns:

  • (true, false)

    true if successful, false if unable to destroy

Raises:

  • (NotFound)

    The record cannot be found.



835
836
837
838
839
840
841
# File 'lib/recurly/resource.rb', line 835

def destroy
  return false unless persisted?
  @response = API.delete uri
  @destroyed = true
rescue API::NotFound => e
  raise NotFound, e.description
end

- (true, false) destroyed?

Has the record been destroyed? (Set true after a successful destroy.)

Returns:

  • (true, false)

See Also:



607
608
609
# File 'lib/recurly/resource.rb', line 607

def destroyed?
  @destroyed
end

- (Hash) errors

Returns A hash with indifferent read access containing any validation errors where the key is the attribute name and the value is an array of error messages.

Examples:

.errors                # => {"account_code"=>["can't be blank"]}
.errors[:account_code] # => ["can't be blank"]

Returns:

  • (Hash)

    A hash with indifferent read access containing any validation errors where the key is the attribute name and the value is an array of error messages.



795
796
797
# File 'lib/recurly/resource.rb', line 795

def errors
  @errors ||= Recurly::Helper.hash_with_indifferent_read_access
end

- (String) inspect(attributes = self.class.attribute_names.to_a) Also known as: to_s

Returns:

  • (String)


848
849
850
851
852
853
854
855
856
# File 'lib/recurly/resource.rb', line 848

def inspect attributes = self.class.attribute_names.to_a
  string = "#<#{self.class}"
  string << "##@type" if instance_variable_defined? :@type
  attributes += %w(errors) if errors.any?
  string << " %s" % attributes.map { |k|
    "#{k}: #{self.send(k).inspect}"
  }.join(', ')
  string << '>'
end

- (true, false) new_record?

Is the record new (i.e., not saved on Recurly's servers)?

Returns:

  • (true, false)

See Also:



599
600
601
# File 'lib/recurly/resource.rb', line 599

def new_record?
  @new_record
end

- (true) persist!(saved = false)

Marks a record as persisted, i.e. not a new or deleted record, resetting any tracked attribute changes in the process. (This is an internal method and should probably not be called unless you know what you're doing.)

Returns:

  • (true)


805
806
807
808
809
810
811
812
# File 'lib/recurly/resource.rb', line 805

def persist! saved = false
  @new_record, @uri = false
  if changed?
    @previous_changes = changes if saved
    changed_attributes.clear
  end
  true
end

- (true, false) persisted?

Has the record persisted (i.e., saved on Recurly's servers)?

Returns:

  • (true, false)

See Also:



616
617
618
# File 'lib/recurly/resource.rb', line 616

def persisted?
  !(new_record? || destroyed?)
end

- (Hash) previous_changes

Returns Previously-changed attributes.

Returns:

  • (Hash)

    Previously-changed attributes.

See Also:



590
591
592
# File 'lib/recurly/resource.rb', line 590

def previous_changes
  @previous_changes ||= {}
end

- (Object) read_attribute(key) Also known as: []

The value of a specified attribute, lazily fetching any defined association.

Examples:

.read_attribute :first_name # => "Ted"
[:last_name]                # => "Beneke"

Parameters:

  • key (Symbol, String)

    The name of the attribute to be fetched.

See Also:



628
629
630
631
632
633
634
# File 'lib/recurly/resource.rb', line 628

def read_attribute key
  value = attributes[key = key.to_s]
  if value.respond_to?(:call) && self.class.reflect_on_association(key)
    value = attributes[key] = value.call 
  end
  value
end

- (self) reload(response = nil)

Returns Reloads the record from the server.

Returns:

  • (self)

    Reloads the record from the server.



548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
# File 'lib/recurly/resource.rb', line 548

def reload response = nil
  if response
    return if response.body.length.zero?
    fresh = self.class.from_response response
  else
    fresh = self.class.find(
      @href || to_param, :etag => (etag unless changed?)
    )
  end
  fresh and copy_from fresh
  persist! true
  self
rescue API::NotModified
  self
end

- (true, false) save

Attempts to save the record, returning the success of the request.

Examples:

 = Recurly::Account.new
.save # => false
. = 'account_code'
.save # => true

Returns:

  • (true, false)

Raises:

See Also:



711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
# File 'lib/recurly/resource.rb', line 711

def save
  if new_record? || changed?
    clear_errors
    @response = API.send(
      persisted? ? :put : :post, path, to_xml(:delta => true)
    )
    reload response
    persist! true
  end
  true
rescue API::UnprocessableEntity => e
  apply_errors e
  Transaction::Error.validate! e, (self if is_a? Transaction)
  false
end

- (true) save!

Attempts to save the record, returning true if the record was saved and raising Invalid otherwise.

Examples:

 = Recurly::Account.new
.save! # raises Recurly::Resource::Invalid
. = 'account_code'
.save! # => true

Returns:

  • (true)

Raises:

See Also:



739
740
741
# File 'lib/recurly/resource.rb', line 739

def save!
  save || raise(Invalid.new(self))
end

- (Object) to_param



543
544
545
# File 'lib/recurly/resource.rb', line 543

def to_param
  self[self.class.param_name]
end

- (String) to_xml(options = {})

Serializes the record to XML.

Examples:

Recurly::Account.new(:account_code => 'code').to_xml
# => "<account><account_code>code</account_code></account>"

Parameters:

  • options (Hash) (defaults to: {})

    A hash of XML options.

Returns:

  • (String)

    An XML string.



680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
# File 'lib/recurly/resource.rb', line 680

def to_xml options = {}
  builder = options[:builder] || XML.new("<#{self.class.member_name}/>")
  xml_keys.each { |key|
    value = respond_to?(key) ? send(key) : self[key]
    node = builder.add_element key

    # Duck-typing here is problematic because of ActiveSupport's #to_xml.
    case value
    when Resource, Subscription::AddOns
      value.to_xml options.merge(:builder => node)
    when Array
      value.each { |e| node.add_element Helper.singularize(key), e }
    when Hash
      value.each_pair { |k, v| node.add_element k.to_s, v }
    else
      node.text = value
    end
  }
  builder.to_s
end

- (true, false) update_attributes(attributes = {})

Update a record with a given hash of attributes.

Examples:

 = Account.find 'junior'
.update_attributes :account_code => 'flynn' # => true

Parameters:

  • attributes (Hash) (defaults to: {})

    A hash of attributes.

Returns:

  • (true, false)

    The success of the update.

Raises:

See Also:



771
772
773
# File 'lib/recurly/resource.rb', line 771

def update_attributes attributes = {}
  self.attributes = attributes and save
end

- (true) update_attributes!(attributes = {})

Update a record with a given hash of attributes.

Examples:

 = Account.find 'gale_boetticher'
.update_attributes! :account_code => nil # Raises an exception.

Parameters:

  • attributes (Hash) (defaults to: {})

    A hash of attributes.

Returns:

  • (true)

    The update was successful.

Raises:

See Also:



785
786
787
# File 'lib/recurly/resource.rb', line 785

def update_attributes! attributes = {}
  self.attributes = attributes and save!
end

- (true, ...) valid?

Returns The validity of the record: true if the record was successfully saved (or persisted and unchanged), false if the record was not successfully saved, or nil for a record with an unknown state (i.e. (i.e. new records that haven't been saved and persisted records with changed attributes).

Examples:

 = Recurly::Account.new
.valid? # => nil
.save   # => false
.valid? # => false
. = 'account_code'
.save   # => true
.valid? # => true

Returns:

  • (true, false, nil)

    The validity of the record: true if the record was successfully saved (or persisted and unchanged), false if the record was not successfully saved, or nil for a record with an unknown state (i.e. (i.e. new records that haven't been saved and persisted records with changed attributes).



756
757
758
759
760
# File 'lib/recurly/resource.rb', line 756

def valid?
  return true if persisted? && changed_attributes.empty?
  return if response.nil? || (errors.empty? && changed_attributes?)
  errors.empty?
end

- (Object) write_attribute(key, value) Also known as: []=

Sets the value of a specified attribute.

Examples:

.write_attribute :first_name, 'Gus'
[:company_name] = 'Los Pollos Hermanos'

Parameters:

  • key (Symbol, String)

    The name of the attribute to be set.

  • value (Object)

    The value the attribute will be set to.

See Also:



645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
# File 'lib/recurly/resource.rb', line 645

def write_attribute key, value
  if changed_attributes.key?(key = key.to_s)
    changed_attributes.delete key if changed_attributes[key] == value
  elsif self[key] != value
    changed_attributes[key] = self[key] 
  end

  if self.class.associations.values.flatten.include? key
    value = fetch_association key, value
  # FIXME: More explicit; less magic.
  elsif key.end_with?('_in_cents') && !respond_to?(:currency)
    value = Money.new value unless value.is_a? Money
  end

  attributes[key] = value
end