Class: Spree::Address

Inherits:
Base
  • Object
show all
Extended by:
ActiveModel::ForbiddenAttributesProtection
Defined in:
app/models/spree/address.rb,
app/models/spree/address/name.rb

Overview

‘Spree::Address` provides the foundational ActiveRecord model for recording and validating address information for `Spree::Order`, `Spree::Shipment`, `Spree::UserAddress`, and `Spree::Carton`.

Defined Under Namespace

Classes: Name, StateValidator

Constant Summary collapse

DB_ONLY_ATTRS =
%w(id updated_at created_at)
TAXATION_ATTRS =
%w(state_id country_id zipcode)
LEGACY_NAME_ATTRS =
%w(firstname lastname full_name)

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

display_includes, page, preference, #preferences

Methods included from Core::Permalinks

#generate_permalink, #save_permalink

Class Method Details

.build_default(*args, &block) ⇒ Address

Returns an address with default attributes.

Returns:

  • (Address)

    an address with default attributes



57
58
59
# File 'app/models/spree/address.rb', line 57

def self.build_default(*args, &block)
  where(country: Spree::Country.default).build(*args, &block)
end

.factory(attributes) ⇒ Address

Returns an equal address already in the database or a newly created one.

Returns:

  • (Address)

    an equal address already in the database or a newly created one



62
63
64
65
# File 'app/models/spree/address.rb', line 62

def self.factory(attributes)
  full_attributes = value_attributes(column_defaults, new(attributes).attributes)
  find_or_initialize_by(full_attributes)
end

.immutable_merge(existing_address, new_attributes) ⇒ Address

@note, this may return existing_address if there are no changes to value equality

Returns:

  • (Address)

    address from existing address plus new_attributes as diff



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'app/models/spree/address.rb', line 69

def self.immutable_merge(existing_address, new_attributes)
  # Ensure new_attributes is a sanitized hash
  new_attributes = sanitize_for_mass_assignment(new_attributes)

  return factory(new_attributes) if existing_address.nil?

  merged_attributes = value_attributes(existing_address.attributes, new_attributes)
  new_address = factory(merged_attributes)
  if existing_address == new_address
    existing_address
  else
    new_address
  end
end

.value_attributes(base_attributes, merge_attributes = {}) ⇒ Hash

Returns hash of attributes contributing to value equality with optional merge.

Returns:

  • (Hash)

    hash of attributes contributing to value equality with optional merge



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/models/spree/address.rb', line 85

def self.value_attributes(base_attributes, merge_attributes = {})
  base = base_attributes.stringify_keys.merge(merge_attributes.stringify_keys)

  name_from_attributes = Spree::Address::Name.from_attributes(base)
  if base['firstname'].presence || base['first_name'].presence
    base['firstname'] = name_from_attributes.first_name
  end
  if base['lastname'].presence || base['last_name'].presence
    base['lastname'] = name_from_attributes.last_name
  end

  virtual_name = name_from_attributes.value
  if base['name'].blank? && virtual_name.present?
    base['name'] = virtual_name
  end

  excluded_attributes = DB_ONLY_ATTRS + %w(first_name last_name)

  base.except(*excluded_attributes)
end

Instance Method Details

#==(other_address) ⇒ Boolean

Note:

This compares the addresses based on only the fields that make up the logical “address” and excludes the database specific fields (id, created_at, updated_at).

Returns true if the two addresses have the same address fields.

Returns:

  • (Boolean)

    true if the two addresses have the same address fields



127
128
129
130
131
132
133
134
# File 'app/models/spree/address.rb', line 127

def ==(other_address)
  return false unless other_address && other_address.respond_to?(:value_attributes)
  if Spree::Config.use_combined_first_and_last_name_in_address
    value_attributes.except(*LEGACY_NAME_ATTRS) == other_address.value_attributes.except(*LEGACY_NAME_ATTRS)
  else
    value_attributes == other_address.value_attributes
  end
end

#active_merchant_hashHash

Returns an ActiveMerchant compatible address hash.

Returns:

  • (Hash)

    an ActiveMerchant compatible address hash



162
163
164
165
166
167
168
169
170
171
172
173
# File 'app/models/spree/address.rb', line 162

def active_merchant_hash
  {
    name: name,
    address1: address1,
    address2: address2,
    city: city,
    state: state_text,
    zip: zipcode,
    country: country.try(:iso),
    phone: phone
  }
end

#as_json(options = {}) ⇒ Object



235
236
237
238
239
240
241
242
243
# File 'app/models/spree/address.rb', line 235

def as_json(options = {})
  if Spree::Config.use_combined_first_and_last_name_in_address
    super(options.merge(except: LEGACY_NAME_ATTRS)).tap do |hash|
      hash['name'] = name
    end
  else
    super
  end
end

#blank?Boolean

This exists because the default Object#blank?, checks empty? if it is defined, and we have defined empty. This should be removed once empty? is removed

Returns:

  • (Boolean)


157
158
159
# File 'app/models/spree/address.rb', line 157

def blank?
  false
end

#country_isoObject



202
203
204
# File 'app/models/spree/address.rb', line 202

def country_iso
  country && country.iso
end

#country_iso=(iso) ⇒ Country

Returns setter that sets self.country to the Country with a matching 2 letter iso.

Parameters:

  • iso (String)

    2 letter Country ISO

Returns:

  • (Country)

    setter that sets self.country to the Country with a matching 2 letter iso

Raises:

  • (ActiveRecord::RecordNotFound)

    if country with the iso doesn’t exist



198
199
200
# File 'app/models/spree/address.rb', line 198

def country_iso=(iso)
  self.country = Spree::Country.find_by!(iso: iso)
end

#empty?Boolean

Deprecated.

Do not use this

Returns:

  • (Boolean)


149
150
151
152
# File 'app/models/spree/address.rb', line 149

def empty?
  Spree::Deprecation.warn("Address#empty? is deprecated.", caller)
  attributes.except('id', 'created_at', 'updated_at', 'country_id').all? { |_, value| value.nil? }
end

#nameString

Returns the full name on this address.

Returns:

  • (String)

    the full name on this address



217
218
219
220
221
222
223
224
# File 'app/models/spree/address.rb', line 217

def name
  self[:name] || begin
    Spree::Address::Name.new(
      read_attribute(:firstname),
      read_attribute(:lastname)
    ).value
  end
end

#name=(value) ⇒ Object



226
227
228
229
230
231
232
233
# File 'app/models/spree/address.rb', line 226

def name=(value)
  return if value.nil?

  write_attribute(:name, value)
  name_from_value = Spree::Address::Name.new(value)
  write_attribute(:firstname, name_from_value.first_name)
  write_attribute(:lastname, name_from_value.last_name)
end

#readonly?Boolean

This is set in order to preserve immutability of Addresses. Use #dup to create new records as required, but it probably won’t be required as often as you think. Since addresses do not change, you won’t accidentally alter historical data.

Returns:

  • (Boolean)


191
192
193
# File 'app/models/spree/address.rb', line 191

def readonly?
  persisted?
end

#require_phone?true

TODO:

Remove this from the public API if possible.

Returns whether or not the address requires a phone number to be present.

Returns:

  • (true)

    whether or not the address requires a phone number to be present



178
179
180
# File 'app/models/spree/address.rb', line 178

def require_phone?
  Spree::Config[:address_requires_phone]
end

#require_zipcode?true

TODO:

Remove this from the public API if possible.

Returns whether or not the address requires a zipcode to be present.

Returns:

  • (true)

    whether or not the address requires a zipcode to be present



184
185
186
# File 'app/models/spree/address.rb', line 184

def require_zipcode?
  true
end

#same_as(other_address) ⇒ Object

Deprecated.

Do not use this. Use Address.== instead.



143
144
145
146
# File 'app/models/spree/address.rb', line 143

def same_as(other_address)
  Spree::Deprecation.warn("Address#same_as is deprecated. It's equivalent to Address.==", caller)
  self == other_address
end

#same_as?(other_address) ⇒ Boolean

Deprecated.

Do not use this. Use Address.== instead.

Returns:

  • (Boolean)


137
138
139
140
# File 'app/models/spree/address.rb', line 137

def same_as?(other_address)
  Spree::Deprecation.warn("Address#same_as? is deprecated. It's equivalent to Address.==", caller)
  self == other_address
end

#set_name_from_firstname_and_lastnameObject



208
209
210
211
212
213
214
# File 'app/models/spree/address.rb', line 208

def set_name_from_firstname_and_lastname
  name_from_firstname_and_lastname = Spree::Address::Name.from_attributes(attributes.except(:name, 'name'))

  if read_attribute(:name).blank? && name_from_firstname_and_lastname.present?
    write_attribute(:name, name_from_firstname_and_lastname)
  end
end

#state_textString

Returns a string representation of this state.

Returns:

  • (String)

    a string representation of this state



116
117
118
# File 'app/models/spree/address.rb', line 116

def state_text
  state.try(:abbr) || state.try(:name) || state_name
end

#taxation_attributesObject



111
112
113
# File 'app/models/spree/address.rb', line 111

def taxation_attributes
  self.class.value_attributes(attributes.slice(*TAXATION_ATTRS))
end

#to_sObject



120
121
122
# File 'app/models/spree/address.rb', line 120

def to_s
  "#{name}: #{address1}"
end

#value_attributesHash

Returns hash of attributes contributing to value equality.

Returns:

  • (Hash)

    hash of attributes contributing to value equality



107
108
109
# File 'app/models/spree/address.rb', line 107

def value_attributes
  self.class.value_attributes(attributes)
end