/address_concern

A reusable Address model for your Rails apps

Primary LanguageRubyMIT LicenseMIT

Address Concern

A reusable polymorphic Address model concern for your Rails apps.

Installation

Add address_concern to your Gemfile:

gem 'address_concern'

Include the AddressConcern::Address concern in your app's Address model by adding the acts_as_address macro to it:

class Address < ApplicationRecord
  acts_as_address
end

Then run the generator to create your addresses table:

rails generate address_concern:install
rails db:migrate

You can modify the migration and add any other fields you may wish to include.

For country and state/providence, you may choose to store both the code and name or just code or just name. Remove from the migration the columns you don't need.

By default, it will store country name in country_name or country if one of those columns exist, and store country code in country_code or country if one of those columns exist. If only a country column exists, it will be used to store the name attribute by default.

By default, it will store state name in state_name or state if one of those columns exist, and store state code in state_code or state if one of those columns exist. If only a state column exists, it will be used to store the name attribute by default.

These column names can be configured. For example, to store country code in country and state code in state, you could do:

class Address < ApplicationRecord
  acts_as_address(
    country: {
      code_attribute: :country,
    },
    state: {
      code_attribute: :state,
    },
  )
end

Usage

belongs_to_address

AddressConcern::AddressAssociations is automatically included into ActiveRecord::Base and provides a few macros for defining associations with your app's Address model.

class Person < ApplicationRecord
  belongs_to_address
end

person = Person.new
person.build_address(address: '...')

Multiple addresses on same model

class User < ApplicationRecord
  belongs_to_address :shipping_address
  belongs_to_address :billing_address
end

user = User.new
shipping_address = user.build_shipping_address(address: '...')
billing_address  = user.build_billing_address( address: '...')

See "Adding an address association to your ActiveRecord models" section for more examples of configuration your associations.

Adding an address association to your ActiveRecord models

You can add an address association (or multiple) to any model that has an address.

You can associate with the address via a belongs_to, has_one, or has_many — whichever makes the most sense for your use case.

You can either use standard ActiveRecord association macros, like this:

class Person < ApplicationRecord
  belongs_to :address
end

... or use the provided macros:

belongs_to_address

class Person < ApplicationRecord
  belongs_to_address
end

person = Person.new
person.build_address(address: '...')

If needed, you can pass a name as well as options for the belongs_to and (optional) inverse has_one associations.

class Child < ApplicationRecord
  belongs_to_address inverse: false
  belongs_to_address :secret_hideout, inverse: {name: :child_for_secret_hideout}
end

child = Child.new
child.build_secret_hideout(address: '...')

has_address

has_address creates a has_one :address association:

class Company < ApplicationRecord
  has_address
end
company = company.new
address = company.build_address(address: '...')

This also adds a polymorphic addressable association on the Address model (not available if you're using belongs_to_address on your addressable models instead of has_address):

  belongs_to :addressable, polymorphic: true, touch: true, optional: true

If you wish to customize that belongs_to, you can pass in any options you like:

class Address < ApplicationRecord
  include AddressConcern::Address

  belongs_to_addressable options…
end

has_addresses

has_addresses creates a has_many :addresses association:

class User < ApplicationRecord
  has_addresses
end

If you want to have several individually accessible addresses associated with a single model (such as a separate shipping and billing address), you can do something like this:

class User < ApplicationRecord
  has_addresses :types => [:physical, :shipping, :billing]
end

Then you can refer to them by name, like this:

shipping_address = user.build_shipping_address(address: 'Some address')
user.shipping_address # => shipping_address

Note that you aren't limited to only the address types you specifically list in your has_addresses declaration; you can still add and retrieve other addresses using the has_many :addresses association:

vacation_address = user.addresses.build(address: 'Vacation', :address_type => 'Vacation')
user.addresses # => [shipping_address, vacation_address]

Street address

You are free to either store the street address in a single column like this:

  create_table :addresses do |t|
    
    t.text     :address
    

or in separate columns like this:

  create_table :addresses do |t|
    
    t.string   :address_1
    t.string   :address_2
    t.string   :address_3
    

If you store it in a single column of type text, then it will support multi-line addresses stored in that single column. Calling address.address_lines, for example, will return an array of address lines — however many lines the user entered (you may add validations to limit this as you wish).

Country/state database

Country/state data comes from the carmen gem.

  • You can set the country either by using the country= writer (if you want to use a country name as input in your frontend) or the country_code= writer (if you want to use a country code as input). It will automatically update the other column for you and keep both of them up-to-date.
  • The country name is stored in the country attribute (most common use case).
  • Country codes can be optionally get/set via the country_code2 (ISO 3166-1 alpha-2 codes) (aliased as country_code) or country_code3 attributes.
  • Be aware that if the country you entered isn't recognized (in Carmen's database), it will be rejected and the country field reset to nil. This should probably be considered a bug and be fixed in a later release (using validations instead).

Other notes regarding country:

  • Added some special handling of UK countries, since Carmen doesn't recognize 'England', etc. as countries but we want to allow those country names to be stored since they may be a part of the address you want to preserve.

View helpers

Because this gem depends on carmen, you have access to its country_select and state_select helpers.

Related Projects

(Along with some feature/API ideas that we may want to incorporate (pull requests welcome!).)

Not maintained for 3+ years:

License

Licensed under the MIT License.

See LICENSE.txt for further details.