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
105
# 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)
  base.delete("name") unless Spree::Config.use_combined_first_and_last_name_in_address

  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



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

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



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

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



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

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)


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

def blank?
  false
end

#country_isoObject



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

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



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

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

#empty?Boolean

Deprecated.

Do not use this

Returns:

  • (Boolean)


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

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



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

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

#name=(value) ⇒ Object



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

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)


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

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



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

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



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

def require_zipcode?
  true
end

#same_as(other_address) ⇒ Object

Deprecated.

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



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

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)


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

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



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

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



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

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

#taxation_attributesObject



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

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

#to_sObject



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

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



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

def value_attributes
  self.class.value_attributes(attributes)
end