Effective Addresses

Extend any ActiveRecord object to have one or more named addresses. Includes a geographic region-aware custom form input backed by Carmen.

Creates methods such as user.billing_address and user.billing_address= for dealing with a has_many :addresses relationship as a single method.

Adds region-aware validations for provinces/states and postal codes.

Includes a Formtastic and SimpleForm one-liner method to drop in address fields to any form.

Uses the Carmen gem internally so when a Country is selected, an AJAX request populates the province/state fields as appropriate.

Rails 3.2.x and Rails 4 Support

Getting Started

Add to your Gemfile:

gem 'effective_addresses'

Run the bundle command to install it:

bundle install

If you're using effective_addresses from within a rails engine (optional):

require 'effective_addresses'

Then run the generator:

rails generate effective_addresses:install

The generator will install an initializer which describes all configuration options and creates a database migration.

If you want to tweak the table name (to use something other than the default addresses), manually adjust both the configuration file and the migration now.

Then migrate the database:

rake db:migrate

And require the javascript in your application.js:

//= require effective_addresses

Usage

Model

Add the mixin to your existing model and specify the name or names of the addresses it should respond to:

class User
  acts_as_addressable :billing
end

This adds the following getters and setters:

@user.billing_address
@user.billing_address=
@user.billing_addresses

Calling user.billing_address will return a single Effective::Address object. Calling user.billing_addresses will return an array of Effective:Address objects.

You can also specify a :presence validation as follows:

class User
  acts_as_addressable :billing => true, :shipping => false
end

or

class User
  acts_as_addressable :billing => {:presence => true}
end

or

class User
  acts_as_addressable :billing

  validates_presence_of :billing_address
end

This means when a User is created, it will not be valid unless a billing_address exist and is valid.

Full Name

Sometimes you want to collect a full_name field with your addresses, such as in the case of a mailing address; other times, it's an unnecessary field.

When you specify the config option config.use_full_name = true all, any calls to effective_address_fields form helper will display the full_name input, and the model will validate_presence_of :full_name.

Address Validations

Address1, City, Country and Postal/Zip Code are all required fields.

If the selected Country has provinces/states (not all countries do), then the province/state field will be required.

If the country is Canada or United States, then the postal/zip code formatting will be enforced.

This validation on the postal code/zip format can be disabled via the config/initializers validate_postal_code_format setting

Canadian postal codes will be automatically upcased and formatted to a consistent format: T5Z 3A4 (capitalized with a single space).

Multiple Addresses

Everytime an address is changed, an additional address record is created. The latest address will always be returned by:

@user.billing_address

You can find all past addresses (including the current one) by:

@user.billing_addresses

Strong Parameters

Make your controller aware of the acts_as_addressable passed parameters:

def permitted_params
  params.require(:base_object).permit(
    :billing_address => EffectiveAddresses.permitted_params,
    :shipping_address => EffectiveAddresses.permitted_params
  )
end

The actual permitted parameters are:

[:full_name, :address1, :address2, :city, :country_code, :state_code, :postal_code]

Form Helpers

Use the helper in a Formtastic or SimpleForm form to quickly create the address fields. Currently only supports Formtastic and SimpleForm.

When you select a country from the select input an AJAX GET request will be made to effective_addresses.address_subregions_path (/addresses/subregions/:country_code) which populates the province/state dropdown with the selected country's states or provinces.

Formtastic

= semantic_form_for @user do |f|
  %h3 Billing Address
  = effective_address_fields(f, :billing_address)

  = f.action :submit

SimpleForm

= simple_form_for @user do |f|
  %h3 Billing Address
  = effective_address_fields(f, :billing_address)

  = f.submit 'Save'

Field Ordering

You may choose to order fields different than the default, such as putting the country first. You can do so with the :field_order option, for example:

= simple_form_for @user do |f|
  %h3 Billing Address
  = effective_address_fields(f, :billing_address, :field_order => [:country_code, :full_name, :address1, :address2, :city, :state_code, :postal_code])

  = f.submit 'Save'

Geocoder option

Effective addresses has an optional integration with Geocoder. At it's simplest, this provides preselection and prefill of country, state, city, and postal_code based on the user's IP address. See Geocoder for a complete list of possibilities.

Installation and Setup

gem 'geocoder'

Add config/initializer/geocoder.rb, below is a sample:

Geocoder.configure(
    # Remote IP address geocoding service (see below for supported options):
    #    https://github.com/alexreisner/geocoder#ip-address-services
    ip_lookup: :telize,

    # Local IP address file database:
    # 1. gem 'maxminddb'
    # 2. download database: http://dev.maxmind.com/geoip/geoip2/geolite2/
    # 3. save file in db/geocoder
    # ip_lookup: :geoip2,
    # geoip2: { file: File.expand_path('../../db/geocoder/GeoLite2-City.mmdb', File.dirname(__FILE__)) },

    cache: Rails.cache,
    cache_prefix: 'geocoder:'
)

# Provide a hardcoded ip of 1.2.3.4 when in developmnt/test and the ip address resolves as localhost
if %w(development test).include? Rails.env
  module Geocoder
    module Request
      def geocoder_spoofable_ip_with_localhost_override
        ip_candidate = geocoder_spoofable_ip_without_localhost_override
        if ip_candidate == '127.0.0.1'
          '1.2.3.4'
        else
          ip_candidate
        end
      end
      alias_method_chain :geocoder_spoofable_ip, :localhost_override
    end
  end
end

License

MIT License. Copyright Code and Effect Inc.

Testing

The test suite for this gem is unfortunately not yet complete.

Run tests by:

rake spec

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Bonus points for test coverage
  6. Create new Pull Request