Module: XeroKiwi::Accounting::Resource

Overview

Mixin that gives an accounting resource class a declarative ‘attribute` DSL. One declaration per field drives reader generation, hydration (via Hydrator), `to_h`, `from_response`, `==` / `eql?` / `hash`, and ActiveRecord-style `inspect`.

Usage:

class Invoice
  include Accounting::Resource

  payload_key "Invoices"
  identity :invoice_id        # two Invoices are equal iff invoice_id matches

  attribute :invoice_id, xero: "InvoiceID", type: :guid
  attribute :date,       xero: "Date",      type: :date
  attribute :contact,    xero: "Contact",   type: :object,
                         of: Contact, reference: true
  attribute :line_items, xero: "LineItems", type: :collection, of: LineItem
end

Resources with a server-side primary key (Invoice, Contact, Payment, …) declare ‘identity :xxx_id`. Value types without a stable ID (Address, Phone, LineItem, …) omit `identity` and fall back to structural equality (every attribute must match).

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



30
31
32
# File 'lib/xero_kiwi/accounting/resource.rb', line 30

def self.included(base)
  base.extend(ClassMethods)
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



97
98
99
100
101
102
103
104
105
106
# File 'lib/xero_kiwi/accounting/resource.rb', line 97

def ==(other)
  return false unless other.is_a?(self.class)

  ids = self.class.identity_attributes
  if ids && !ids.empty?
    ids.all? { |attr| public_send(attr) == other.public_send(attr) }
  else
    to_h == other.to_h
  end
end

#hashObject



109
110
111
112
113
114
115
116
# File 'lib/xero_kiwi/accounting/resource.rb', line 109

def hash
  ids = self.class.identity_attributes
  if ids && !ids.empty?
    ([self.class] + ids.map { |attr| public_send(attr) }).hash
  else
    [self.class, to_h].hash
  end
end

#initialize(attrs, opts = {}) ⇒ Object

‘opts` is positional, not a kwarg, so that bare string-keyed hashes (`Klass.new(“Foo” => “bar”)`) don’t get silently absorbed as kwargs in Ruby 3. Callers passing ‘reference: true` land here as a positional symbol-keyed hash, which is what we want.



79
80
81
82
83
84
85
86
87
# File 'lib/xero_kiwi/accounting/resource.rb', line 79

def initialize(attrs, opts = {})
  attrs         = attrs.transform_keys(&:to_s)
  @is_reference = opts[:reference] == true

  self.class.attributes.each do |name, spec|
    value = Hydrator.call(attrs[spec[:xero]], spec)
    instance_variable_set("@#{name}", value)
  end
end

#inspectObject

ActiveRecord-style inspect: shows every declared attribute inline. Nested objects collapse to a one-line reference (identity-only when available, otherwise just the class name) so cascades don’t explode. Collections collapse to ‘[N items]`.



122
123
124
125
# File 'lib/xero_kiwi/accounting/resource.rb', line 122

def inspect
  pairs = self.class.attributes.map { |name, spec| "#{name}=#{format_for_inspect(public_send(name), spec)}" }
  "#<#{self.class} #{pairs.join(' ')}>"
end

#reference?Boolean

Returns:

  • (Boolean)


89
90
91
# File 'lib/xero_kiwi/accounting/resource.rb', line 89

def reference?
  @is_reference
end

#to_hObject



93
94
95
# File 'lib/xero_kiwi/accounting/resource.rb', line 93

def to_h
  self.class.attributes.keys.to_h { |key| [key, public_send(key)] }
end