has_addresses 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG ADDED
@@ -0,0 +1,19 @@
1
+ *SVN*
2
+
3
+ *0.0.1* (August 21st, 2007)
4
+
5
+ * Add documentation
6
+
7
+ * Add descriptive output for rake tasks
8
+
9
+ * Remove dependency on has_association_helper
10
+
11
+ * Remove default bootstrap files in favor of creating new ones through the bootstrap tasks
12
+
13
+ * Add tests for rake tests
14
+
15
+ * Fix not allowing single character abbreviations for regions
16
+
17
+ * Add countries:create_fixtures, countries:bootstrap, regions:create_fixtures, and regions:bootstrap files
18
+
19
+ * Refactor Rake code into Country/Region classes
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2007 Aaron Pfeifer & Neil Abraham
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,111 @@
1
+ = has_addresses
2
+
3
+ has_addresses adds a base skeleton for handling countries, regions, and
4
+ addresses.
5
+
6
+ == Resources
7
+
8
+ API
9
+
10
+ * http://api.pluginaweek.org/has_addresses
11
+
12
+ Wiki
13
+
14
+ * http://wiki.pluginaweek.org/Has_addresses
15
+
16
+ Announcement
17
+
18
+ * http://www.pluginaweek.org/
19
+
20
+ Source
21
+
22
+ * http://svn.pluginaweek.org/trunk/plugins/active_record/has/has_addresses
23
+
24
+ Development
25
+
26
+ * http://dev.pluginaweek.org/browser/trunk/plugins/active_record/has/has_addresses
27
+
28
+ == Description
29
+
30
+ Countries, regions, and addresses are all simple models whose data and
31
+ functionality should be able to be standardized across multiple applications.
32
+ has_addresses adds support for countries and regions based on the ISO 3166 and
33
+ ISO 3166-2 standard. The data for these standards is obtained through the
34
+ open-source Debian package, iso-codes.
35
+
36
+ Along with the simple Country and Region models, addresses can be defined and
37
+ integrated based on the data in these models. Addresses are minimalistic in
38
+ terms of the type of data required and follows the standard U.S. format.
39
+
40
+ === Running migrations
41
+
42
+ To migrate the tables required for has_addresses, you can either run the
43
+ migration from the command line like so:
44
+
45
+ rake db:migrate:plugins PLUGIN=has_addresses
46
+
47
+ or (more ideally) generate a migration file that will integrate into your main
48
+ application's migration path:
49
+
50
+ ruby script/generate plugin_migration MigrateHasAddressesToVersion3
51
+
52
+ === Bootstrapping the database
53
+
54
+ has_addresses comes bundled with tasks for bootstrapping the countries and
55
+ regions table based on the ISO 3166 and ISO 3166-2 standard. You can bootstrap
56
+ the database using built-in rake tasks or by creating fixtures which contain the
57
+ bootstrap data.
58
+
59
+ To bootstrap country/region data into the current database schema:
60
+
61
+ $ rake countries:bootstrap
62
+ (in /my/project)
63
+ Downloading ISO 3166 data...
64
+ Parsing countries...
65
+ Loading countries into database...
66
+ Done!
67
+
68
+ $ rake regions:bootstrap
69
+ Downloading ISO 3166 data...
70
+ Parsing countries...
71
+ Downloading ISO 3166-2 data...
72
+ Parsing regions...
73
+ Loading regions into database...
74
+ Done!
75
+
76
+ To create fixtures for the country/region bootstrap data:
77
+
78
+ $ rake countries:create_fixtures
79
+ (in /my/project)
80
+ Downloading ISO 3166 data...
81
+ Parsing countries...
82
+ Saving countries to /my/project/config/../db/bootstrap/countries.yml...
83
+ Done!
84
+
85
+ $ rake regions:create_fixtures
86
+ (in /my/project)
87
+ Downloading ISO 3166 data...
88
+ Parsing countries...
89
+ Downloading ISO 3166-2 data...
90
+ Parsing regions...
91
+ Saving regions to /my/project/config/../db/bootstrap/regions.yml...
92
+ Done!
93
+
94
+ == Testing
95
+
96
+ Since the rake tasks for installing TinyMCE and updating the configuration
97
+ options are part of the unit tests, already-downloaded files are included with
98
+ the plugin. If you want to perform a "live" test which actually downloads the
99
+ files off the Internet (rather than using the local versions), you must set
100
+ the LIVE environment variable to true. For example,
101
+
102
+ rake test LIVE=true
103
+
104
+ The following plugins/gems must be installed before any tests can be run:
105
+ * plugin_dependencies - http://wiki.pluginaweekk.org/Plugin_dependencies
106
+ * loaded_plugins - http://wiki.pluginaweek.org/Loaded_plugins
107
+ * appable_plugins - http://wiki.pluginaweek.org/Appable_plugins
108
+
109
+ == Dependencies
110
+
111
+ This plugin does not depend on the presence of any other plugin.
data/Rakefile ADDED
@@ -0,0 +1,82 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/sshpublisher'
5
+
6
+ # Load custom rakefile extensions
7
+ Dir["#{File.dirname(__FILE__)}/tasks/**/*.rake"].sort.each {|ext| load ext}
8
+
9
+ PKG_NAME = 'has_addresses'
10
+ PKG_VERSION = '0.0.1'
11
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
12
+ RUBY_FORGE_PROJECT = 'pluginaweek'
13
+
14
+ desc 'Default: run unit tests.'
15
+ task :default => :test
16
+
17
+ desc 'Test the has_addresses plugin.'
18
+ Rake::TestTask.new(:test) do |t|
19
+ t.libs << 'lib'
20
+ t.pattern = 'test/unit/**/*_test.rb'
21
+ t.verbose = true
22
+ end
23
+
24
+ desc 'Generate documentation for the has_addresses plugin.'
25
+ Rake::RDocTask.new(:rdoc) do |rdoc|
26
+ rdoc.rdoc_dir = 'rdoc'
27
+ rdoc.title = 'HasAddresses'
28
+ rdoc.options << '--line-numbers' << '--inline-source'
29
+ rdoc.rdoc_files.include('README')
30
+ rdoc.rdoc_files.include('lib/**/*.rb')
31
+ end
32
+
33
+ spec = Gem::Specification.new do |s|
34
+ s.name = PKG_NAME
35
+ s.version = PKG_VERSION
36
+ s.platform = Gem::Platform::RUBY
37
+ s.summary = 'Adds a base skeleton for handling countries, regions, and addresses.'
38
+
39
+ s.files = FileList['{app,db,lib,tasks,test}/**/*'].to_a + %w(CHANGELOG init.rb MIT-LICENSE Rakefile README)
40
+ s.require_path = 'lib'
41
+ s.autorequire = 'has_addresses'
42
+ s.has_rdoc = true
43
+ s.test_files = Dir['test/unit/**/*_test.rb']
44
+
45
+ s.author = 'Aaron Pfeifer, Neil Abraham'
46
+ s.email = 'info@pluginaweek.org'
47
+ s.homepage = 'http://www.pluginaweek.org'
48
+ end
49
+
50
+ Rake::GemPackageTask.new(spec) do |p|
51
+ p.gem_spec = spec
52
+ p.need_tar = true
53
+ p.need_zip = true
54
+ end
55
+
56
+ desc 'Publish the beta gem'
57
+ task :pgem => [:package] do
58
+ Rake::SshFilePublisher.new('pluginaweek@pluginaweek.org', '/home/pluginaweek/gems.pluginaweek.org/gems', 'pkg', "#{PKG_FILE_NAME}.gem").upload
59
+ end
60
+
61
+ desc 'Publish the API documentation'
62
+ task :pdoc => [:rdoc] do
63
+ Rake::SshDirPublisher.new('pluginaweek@pluginaweek.org', "/home/pluginaweek/api.pluginaweek.org/#{PKG_NAME}", 'rdoc').upload
64
+ end
65
+
66
+ desc 'Publish the API docs and gem'
67
+ task :publish => [:pdoc, :release]
68
+
69
+ desc 'Publish the release files to RubyForge.'
70
+ task :release => [:gem, :package] do
71
+ require 'rubyforge'
72
+
73
+ ruby_forge = RubyForge.new
74
+ ruby_forge.login
75
+
76
+ %w( gem tgz zip ).each do |ext|
77
+ file = "pkg/#{PKG_FILE_NAME}.#{ext}"
78
+ puts "Releasing #{File.basename(file)}..."
79
+
80
+ ruby_forge.add_release(RUBY_FORGE_PROJECT, PKG_NAME, PKG_VERSION, file)
81
+ end
82
+ end
@@ -0,0 +1,84 @@
1
+ # Represents a mailing address
2
+ class Address < ActiveRecord::Base
3
+ belongs_to :addressable,
4
+ :polymorphic => true
5
+ belongs_to :region
6
+ belongs_to :country
7
+
8
+ validates_presence_of :addressable_id,
9
+ :addressable_type,
10
+ :street_1,
11
+ :city,
12
+ :postal_code
13
+ validates_presence_of :region_id,
14
+ :if => :known_region_required?
15
+ validates_presence_of :custom_region,
16
+ :if => :custom_region_required?
17
+ validates_format_of :postal_code,
18
+ :with => /^[0-9]{5}$/,
19
+ :allow_nil => true
20
+
21
+ before_save :ensure_exclusive_references
22
+
23
+ # Returns the region's country if the region is specified
24
+ def country_with_region_check
25
+ region ? region.country : country_without_region_check
26
+ end
27
+ alias_method_chain :country, :region_check
28
+
29
+ # Gets the name of the region that this address is for (whether it is a custom or
30
+ # stored region in the database)
31
+ def region_name
32
+ custom_region || (region ? region.name : nil)
33
+ end
34
+
35
+ # Gets the value of the address on a single line
36
+ def single_line
37
+ multi_line.join(', ')
38
+ end
39
+
40
+ # Gets the value of the address on multiple lines
41
+ def multi_line
42
+ lines = []
43
+ lines << street_1 if street_1?
44
+ lines << street_2 if street_2?
45
+
46
+ line = ''
47
+ line << city if city?
48
+ if region_name
49
+ line << ', ' if !line.blank?
50
+ line << region_name
51
+ end
52
+ if postal_code?
53
+ line << ' ' if !line.blank?
54
+ line << postal_code
55
+ end
56
+ lines << line if !line.blank?
57
+
58
+ lines << country.name if country
59
+ lines
60
+ end
61
+
62
+ private
63
+ def known_region_required?
64
+ country.nil? || country.regions.count != 0
65
+ end
66
+
67
+ # A custom region name is required if a known region was not specified and
68
+ # the country in which this address resides has no known regions in the
69
+ # database
70
+ def custom_region_required?
71
+ region_id.nil? && country && country.regions.count == 0
72
+ end
73
+
74
+ # Ensures that the country id/user region combo is not set at the same time as
75
+ # the region id
76
+ def ensure_exclusive_references
77
+ if known_region_required?
78
+ self.country_id = nil
79
+ self.custom_region = nil
80
+ end
81
+
82
+ true
83
+ end
84
+ end
@@ -0,0 +1,114 @@
1
+ require 'open-uri'
2
+ require 'cgi'
3
+ require 'rexml/document'
4
+
5
+ # Defined by the ISO 3166 standard. The ISO 3166 standard includes a
6
+ # "Country Subdivision Code", giving a code for the names of the principal
7
+ # administrative subdivisions of the countries coded in ISO 3166.
8
+ class Country < ActiveRecord::Base
9
+ # The url of the open source version of the ISO 3166 standard
10
+ ISO_3166_URL = 'http://svn.debian.org/wsvn/pkg-isocodes/trunk/iso-codes/iso_3166/iso_3166.xml?op=file'
11
+
12
+ class << self
13
+ # Creates a fixtures file containing all possible countries
14
+ def create_fixtures(output_file_path = nil)
15
+ output_file_path ||= 'db/bootstrap/countries.yml'
16
+ output_file_path = File.join(RAILS_ROOT, output_file_path)
17
+
18
+ FileUtils.mkdir_p(File.dirname(output_file_path))
19
+ output_file = File.new(output_file_path, File::CREAT|File::TRUNC|File::RDWR)
20
+ output = ''
21
+
22
+ countries = download_all
23
+ puts "Saving countries to #{output_file_path}..." if PluginAWeek::Has::Addresses.verbose
24
+ countries.each do |country|
25
+ record_name = country.name.gsub(' ', '_').gsub(/[^A-Za-z_]/, '').downcase
26
+
27
+ output << "#{record_name}:\n"
28
+ country.attributes.each do |attr, value|
29
+ output << " #{attr}: #{value}\n" if value
30
+ end
31
+ end
32
+
33
+ output_file << output.slice(0..output.length-2)
34
+ output_file.close
35
+
36
+ puts 'Done!' if PluginAWeek::Has::Addresses.verbose
37
+ true
38
+ end
39
+
40
+ # Bootstraps the table by downloading the latest ISO 3166 standard and
41
+ # saving each country to the database
42
+ def bootstrap
43
+ countries = download_all
44
+
45
+ puts 'Loading countries into database...' if PluginAWeek::Has::Addresses.verbose
46
+ delete_all
47
+ countries.each {|country| country.save!}
48
+
49
+ puts 'Done!' if PluginAWeek::Has::Addresses.verbose
50
+ true
51
+ end
52
+
53
+ # Downloads the latest ISO 3166 standard and returns the data as a list of
54
+ # countries
55
+ def download_all
56
+ # Download the standard
57
+ puts 'Downloading ISO 3166 data...' if PluginAWeek::Has::Addresses.verbose
58
+ iso_3166 = open(ISO_3166_URL).readlines * "\n"
59
+ iso_3166 = CGI::unescapeHTML(/<pre>(.+)<\/pre>/im.match(iso_3166)[1].gsub('&nbsp;', ''))
60
+
61
+ # Parse and load the countries
62
+ puts 'Parsing countries...' if PluginAWeek::Has::Addresses.verbose
63
+ countries = []
64
+ REXML::Document.new(iso_3166).elements.each('*/iso_3166_entry') do |country|
65
+ name = country.attributes['name'].to_s.gsub("'","\\'")
66
+ official_name = country.attributes['official_name']
67
+ official_name = official_name.to_s.gsub!("'", "\\'") if official_name
68
+ alpha_2_code = country.attributes['alpha_2_code'].to_s.upcase
69
+ alpha_3_code = country.attributes['alpha_3_code'].to_s.upcase
70
+ country_id = country.attributes['numeric_code'].to_s.to_i
71
+ record_name = name.gsub(' ', '_').gsub(/[^A-Za-z_]/, '').downcase
72
+
73
+ country = new(
74
+ :name => name,
75
+ :official_name => official_name,
76
+ :alpha_2_code => alpha_2_code,
77
+ :alpha_3_code => alpha_3_code
78
+ )
79
+ country.id = country_id
80
+ countries << country
81
+ end
82
+
83
+ countries
84
+ end
85
+ end
86
+
87
+ has_many :regions
88
+
89
+ validates_presence_of :name,
90
+ :alpha_2_code,
91
+ :alpha_3_code
92
+ validates_uniqueness_of :name,
93
+ :alpha_2_code,
94
+ :alpha_3_code
95
+ validates_length_of :name,
96
+ :within => 2..80
97
+ validates_length_of :alpha_2_code,
98
+ :is => 2
99
+ validates_length_of :alpha_3_code,
100
+ :is => 3
101
+
102
+ alias_attribute :abbreviation_2, :alpha_2_code
103
+ alias_attribute :abbreviation_3, :alpha_3_code
104
+
105
+ # The official name of the country
106
+ def official_name
107
+ read_attribute(:official_name) || name
108
+ end
109
+
110
+ # Returns the name of the country
111
+ def to_s #:nodoc
112
+ name
113
+ end
114
+ end
@@ -0,0 +1,112 @@
1
+ require 'open-uri'
2
+ require 'cgi'
3
+ require 'rexml/document'
4
+
5
+ # Defined by the ISO 3166-2 standard. This is a standard that gives short codes
6
+ # for provinces, etc. within a country.
7
+ class Region < ActiveRecord::Base
8
+ # The url of the open source version of the ISO 3166-2 standard
9
+ ISO_3166_2_URL = 'http://svn.debian.org/wsvn/pkg-isocodes/trunk/iso-codes/iso_3166/iso_3166_2/iso_3166_2.xml?op=file'
10
+
11
+ class << self
12
+ # Creates a fixtures containing all possible regions
13
+ def create_fixtures(output_file_path = nil)
14
+ output_file_path ||= 'db/bootstrap/regions.yml'
15
+ output_file_path = File.join(RAILS_ROOT, output_file_path)
16
+
17
+ FileUtils.mkdir_p(File.dirname(output_file_path))
18
+ output_file = File.new(output_file_path, File::CREAT|File::TRUNC|File::RDWR)
19
+ output = ''
20
+
21
+ regions = download_all
22
+ puts "Saving regions to #{output_file_path}..." if PluginAWeek::Has::Addresses.verbose
23
+ regions.each do |region|
24
+ record_name = region.name.gsub(' ', '_').gsub(/[^A-Za-z_]/, '').downcase
25
+
26
+ output << "#{record_name}:\n"
27
+ region.attributes.each do |attr, value|
28
+ output << " #{attr}: #{value}\n" if value
29
+ end
30
+ end
31
+
32
+ output_file << output.slice(0..output.length-2)
33
+ output_file.close
34
+
35
+ puts 'Done!' if PluginAWeek::Has::Addresses.verbose
36
+ true
37
+ end
38
+
39
+ # Bootstraps the table by downloading the latest ISO 3166-2 standard and
40
+ # saving each region to the database
41
+ def bootstrap
42
+ regions = download_all
43
+
44
+ puts 'Loading regions into database...' if PluginAWeek::Has::Addresses.verbose
45
+ delete_all
46
+ regions.each {|region| region.save!}
47
+
48
+ puts 'Done!' if PluginAWeek::Has::Addresses.verbose
49
+ true
50
+ end
51
+
52
+ # Downloads the latest ISO 3166-2 standard and returns the data as a list of
53
+ # regions
54
+ def download_all
55
+ countries = Country.download_all
56
+ alpha_2_code_to_country = Hash[*countries.collect {|c| [c.alpha_2_code, c]}.flatten]
57
+
58
+ # Download the standard
59
+ puts 'Downloading ISO 3166-2 data...' if PluginAWeek::Has::Addresses.verbose
60
+ iso_3166_2 = open(ISO_3166_2_URL).readlines * "\n"
61
+ iso_3166_2 = CGI::unescapeHTML(/<pre>(.+)<\/pre>/im.match(iso_3166_2)[1].gsub('&nbsp;', ''))
62
+
63
+ # Parse and load the regions
64
+ puts 'Parsing regions...' if PluginAWeek::Has::Addresses.verbose
65
+ region_id = 1
66
+ regions = []
67
+ REXML::Document.new(iso_3166_2).elements.each('*/iso_3166_country') do |country|
68
+ code = country.attributes['code'][0..1] # Belarius is more than 2 characters for some reason, so just get the first 2
69
+ country_record = alpha_2_code_to_country[code.upcase]
70
+
71
+ country.elements.each('iso_3166_2_entry') do |region|
72
+ name = region.attributes['name'].to_s.gsub("'","\\'").strip
73
+ abbreviation = region.attributes['code'].upcase.sub("#{code}-", '')
74
+
75
+ region = new(
76
+ :country_id => country_record.id,
77
+ :name => name,
78
+ :abbreviation => abbreviation
79
+ )
80
+ region.id = region_id
81
+
82
+ if !country_record.regions.any? {|known_region| known_region.name == region.name}
83
+ regions << region
84
+ country_record.regions << region
85
+ region_id += 1
86
+ end
87
+ end
88
+ end
89
+
90
+ regions
91
+ end
92
+ end
93
+
94
+ belongs_to :country
95
+ has_many :addresses
96
+
97
+ validates_presence_of :name,
98
+ :country_id,
99
+ :abbreviation
100
+ validates_length_of :name,
101
+ :within => 2..80
102
+ validates_length_of :abbreviation,
103
+ :within => 1..5
104
+ validates_uniqueness_of :name,
105
+ :scope => :country_id
106
+ validates_uniqueness_of :abbreviation,
107
+ :scope => :country_id
108
+
109
+ def to_s #:nodoc
110
+ name
111
+ end
112
+ end
@@ -0,0 +1,17 @@
1
+ class CreateCountries < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :countries do |t|
4
+ t.column :name, :string, :null => false, :limit => 80
5
+ t.column :official_name, :string, :limit => 80
6
+ t.column :alpha_2_code, :string, :null => false, :limit => 2
7
+ t.column :alpha_3_code, :string, :null => false, :limit => 3
8
+ end
9
+ add_index :countries, :name, :unique => true
10
+ add_index :countries, :alpha_2_code, :unique => true
11
+ add_index :countries, :alpha_3_code, :unique => true
12
+ end
13
+
14
+ def self.down
15
+ drop_table :countries
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ class CreateRegions < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :regions do |t|
4
+ t.column :country_id, :integer, :null => false
5
+ t.column :name, :string, :null => false, :limit => 50
6
+ t.column :abbreviation, :string, :null => false, :limit => 5
7
+ end
8
+ add_index :regions, [:name, :country_id], :unique => true
9
+ add_index :regions, [:abbreviation, :country_id], :unique => true
10
+ end
11
+
12
+ def self.down
13
+ drop_table :regions
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ class CreateAddresses < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :addresses do |t|
4
+ t.column :addressable_id, :integer, :null => false, :references => nil
5
+ t.column :addressable_type, :string, :null => false
6
+ t.column :street_1, :string, :null => false, :limit => 100
7
+ t.column :street_2, :string, :limit => 100
8
+ t.column :city, :string, :null => false, :limit => 255
9
+ t.column :region_id, :integer
10
+ t.column :custom_region, :string, :limit => 50
11
+ t.column :postal_code, :string, :null => false, :limit => 5
12
+ t.column :country_id, :integer, :default => 223
13
+ t.column :created_at, :timestamp, :null => false
14
+ t.column :updated_at, :datetime, :null => false
15
+ end
16
+ end
17
+
18
+ def self.down
19
+ drop_table :addresses
20
+ end
21
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'has_addresses'
@@ -0,0 +1,74 @@
1
+ module PluginAWeek #:nodoc:
2
+ module Has #:nodoc:
3
+ # Adds base models for interacting with addresses, including Country,
4
+ # Region, and Address. These have the minimal attribute definitions needed
5
+ # to store addresses.
6
+ module Addresses
7
+ # Whether or not to use verbose output
8
+ mattr_accessor :verbose
9
+ @@verbose = true
10
+
11
+ def self.included(base) #:nodoc:
12
+ base.extend(MacroMethods)
13
+ end
14
+
15
+ module MacroMethods
16
+ # Creates a new association for having a single address. This takes
17
+ # the same parameters as ActiveRecord::Associations::ClassMethods#has_one.
18
+ # By default, the following associations are the same:
19
+ #
20
+ # class Person < ActiveRecord::Base
21
+ # has_address
22
+ # end
23
+ #
24
+ # and
25
+ #
26
+ # class Person < ActiveRecord::Base
27
+ # has_one :address,
28
+ # :class_name => 'Address',
29
+ # :as => :addressable,
30
+ # :dependent => :destroy
31
+ # end
32
+ def has_address(*args, &extension)
33
+ create_address_association(:one, :address, *args, &extension)
34
+ end
35
+
36
+ # Creates a new association for having a multiple addresses. This takes
37
+ # the same parameters as ActiveRecord::Associations::ClassMethods#has_many.
38
+ # By default, the following associations are the same:
39
+ #
40
+ # class Person < ActiveRecord::Base
41
+ # has_addresses
42
+ # end
43
+ #
44
+ # and
45
+ #
46
+ # class Person < ActiveRecord::Base
47
+ # has_many :addresses,
48
+ # :class_name => 'Address',
49
+ # :as => :addressable,
50
+ # :dependent => :destroy
51
+ # end
52
+ def has_addresses(*args, &extension)
53
+ create_address_association(:many, :addresses, *args, &extension)
54
+ end
55
+
56
+ private
57
+ def create_address_association(cardinality, association_id, *args, &extension)
58
+ options = extract_options_from_args!(args)
59
+ options.symbolize_keys!.reverse_merge!(
60
+ :class_name => 'Address',
61
+ :as => :addressable,
62
+ :dependent => :destroy
63
+ )
64
+
65
+ send("has_#{cardinality}", args.first || association_id, options, &extension)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ ActiveRecord::Base.class_eval do
73
+ include PluginAWeek::Has::Addresses
74
+ end