has_addresses 0.0.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +50 -0
- data/{MIT-LICENSE → LICENSE} +2 -2
- data/README.rdoc +63 -0
- data/Rakefile +48 -41
- data/app/models/address.rb +92 -50
- data/app/models/country.rb +287 -101
- data/app/models/region.rb +5378 -106
- data/db/migrate/001_create_countries.rb +7 -7
- data/db/migrate/002_create_regions.rb +7 -5
- data/db/migrate/003_create_addresses.rb +10 -12
- data/lib/has_addresses.rb +9 -68
- data/test/app_root/app/models/company.rb +1 -5
- data/test/app_root/config/environment.rb +6 -19
- data/test/app_root/db/migrate/001_create_companies.rb +1 -1
- data/test/app_root/db/migrate/002_migrate_has_addresses_to_version_3.rb +13 -0
- data/test/factory.rb +68 -0
- data/test/functional/has_addresses_test.rb +22 -0
- data/test/test_helper.rb +11 -5
- data/test/unit/address_test.rb +210 -79
- data/test/unit/country_test.rb +101 -72
- data/test/unit/region_test.rb +104 -61
- metadata +70 -61
- data/CHANGELOG +0 -23
- data/README +0 -122
- data/tasks/has_addresses_tasks.rake +0 -23
- data/test/app_root/db/migrate/002_add_address_kinds.rb +0 -9
- data/test/files/iso_3166.xml +0 -1719
- data/test/files/iso_3166_2.xml +0 -8617
- data/test/fixtures/addresses.yml +0 -47
- data/test/fixtures/companies.yml +0 -7
- data/test/fixtures/countries.yml +0 -12
- data/test/fixtures/regions.yml +0 -5
- data/test/unit/has_addresses_test.rb +0 -15
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
== master
|
2
|
+
|
3
|
+
== 0.5.0 / 2009-04-30
|
4
|
+
|
5
|
+
* Replace acts_as_enumeration with enumerate_by
|
6
|
+
* Add dependency on Rails 2.3
|
7
|
+
|
8
|
+
== 0.4.0 / 2008-12-14
|
9
|
+
|
10
|
+
* Remove the PluginAWeek namespace
|
11
|
+
|
12
|
+
== 0.3.0 / 2008-10-26
|
13
|
+
|
14
|
+
* Add mass-assignment protection in the Address model
|
15
|
+
* Change how the base module is included to prevent namespacing conflicts
|
16
|
+
|
17
|
+
== 0.2.1 / 2008-06-22
|
18
|
+
|
19
|
+
* Remove log files from gems
|
20
|
+
|
21
|
+
== 0.2.0 / 2008-06-22
|
22
|
+
|
23
|
+
* Fix incorrect regions being listed for most countries
|
24
|
+
* Use an id scheme for regions based on the id of the country to prevent id clashing in future updates
|
25
|
+
* Improve performance by not requiring that the country be looked up for every new region created
|
26
|
+
* Index region enumerations by their ISO code
|
27
|
+
* Use common display names for countries, rather than the default specified in the standard; use the official name if needed
|
28
|
+
* Organize country enumerations by id
|
29
|
+
* Index country enumerations by their alpha 2 code
|
30
|
+
|
31
|
+
== 0.1.0 / 2008-05-05
|
32
|
+
|
33
|
+
* Convert Country/Region to enumerations
|
34
|
+
* Add dependency on acts_as_enumeration
|
35
|
+
* Update documentation to reflect plugins_plus changes
|
36
|
+
|
37
|
+
== 0.0.2 / 2007-09-26
|
38
|
+
|
39
|
+
* Move test fixtures out of the test application root directory
|
40
|
+
|
41
|
+
== 0.0.1 / 2007-08-21
|
42
|
+
|
43
|
+
* Add documentation
|
44
|
+
* Add descriptive output for rake tasks
|
45
|
+
* Remove dependency on has_association_helper
|
46
|
+
* Remove default bootstrap files in favor of creating new ones through the bootstrap tasks
|
47
|
+
* Add tests for rake tests
|
48
|
+
* Fix not allowing single character abbreviations for regions
|
49
|
+
* Add countries:create_fixtures, countries:bootstrap, regions:create_fixtures, and regions:bootstrap files
|
50
|
+
* Refactor Rake code into Country/Region classes
|
data/{MIT-LICENSE → LICENSE}
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2006-
|
1
|
+
Copyright (c) 2006-2009 Aaron Pfeifer
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
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.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= has_addresses
|
2
|
+
|
3
|
+
+has_addresses+ demonstrates a reference implementation for handling countries,
|
4
|
+
regions, and addresses.
|
5
|
+
|
6
|
+
== Resources
|
7
|
+
|
8
|
+
API
|
9
|
+
|
10
|
+
* http://api.pluginaweek.org/has_addresses
|
11
|
+
|
12
|
+
Bugs
|
13
|
+
|
14
|
+
* http://pluginaweek.lighthouseapp.com/projects/13271-has_addresses
|
15
|
+
|
16
|
+
Development
|
17
|
+
|
18
|
+
* http://github.com/pluginaweek/has_addresses
|
19
|
+
|
20
|
+
Source
|
21
|
+
|
22
|
+
* git://github.com/pluginaweek/has_addresses.git
|
23
|
+
|
24
|
+
== Description
|
25
|
+
|
26
|
+
Countries, regions, and addresses are all simple models whose data and
|
27
|
+
functionality should be able to be standardized across multiple applications.
|
28
|
+
has_addresses adds support for countries and regions based on the ISO 3166 and
|
29
|
+
ISO 3166-2 standard. The data for these standards is obtained through the
|
30
|
+
open-source Debian package, iso-codes.
|
31
|
+
|
32
|
+
Along with the simple Country and Region models, addresses can be defined and
|
33
|
+
integrated based on the data in these models. Addresses are minimalistic in
|
34
|
+
terms of the type of data required.
|
35
|
+
|
36
|
+
== Usage
|
37
|
+
|
38
|
+
Note that this is a reference implementation and, most likely, should be
|
39
|
+
modified for your own usage.
|
40
|
+
|
41
|
+
=== Example
|
42
|
+
|
43
|
+
address = Address.new(
|
44
|
+
:street_1 => '1600 Amphitheatre Parkway',
|
45
|
+
:city => 'Mountain View',
|
46
|
+
:region => 'US-CA',
|
47
|
+
:postal_code => '94043'
|
48
|
+
)
|
49
|
+
address.single_line # => "1600 Amphitheatre Parkway, Mountain View, California 94043, United States"
|
50
|
+
|
51
|
+
== Testing
|
52
|
+
|
53
|
+
Before you can run any tests, the following gem must be installed:
|
54
|
+
* plugin_test_helper[http://github.com/pluginaweek/plugin_test_helper]
|
55
|
+
|
56
|
+
To run against a specific version of Rails:
|
57
|
+
|
58
|
+
rake test RAILS_FRAMEWORK_ROOT=/path/to/rails
|
59
|
+
|
60
|
+
== Dependencies
|
61
|
+
|
62
|
+
* Rails 2.3 or later
|
63
|
+
* enumerate_by[http://github.com/pluginaweek/enumerate_by]
|
data/Rakefile
CHANGED
@@ -3,48 +3,55 @@ require 'rake/rdoctask'
|
|
3
3
|
require 'rake/gempackagetask'
|
4
4
|
require 'rake/contrib/sshpublisher'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
spec = Gem::Specification.new do |s|
|
7
|
+
s.name = 'has_addresses'
|
8
|
+
s.version = '0.5.0'
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.summary = 'Demonstrates a reference implementation for handling countries, regions, and addresses.'
|
11
|
+
|
12
|
+
s.files = FileList['{app,db,lib,test}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc) - FileList['test/app_root/{log,log/*,script,script/*}']
|
13
|
+
s.require_path = 'lib'
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.test_files = Dir['test/**/*_test.rb']
|
16
|
+
s.add_dependency 'enumerate_by', '>= 0.4.0'
|
17
|
+
|
18
|
+
s.author = 'Aaron Pfeifer'
|
19
|
+
s.email = 'aaron@pluginaweek.org'
|
20
|
+
s.homepage = 'http://www.pluginaweek.org'
|
21
|
+
s.rubyforge_project = 'pluginaweek'
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Default: run all tests.'
|
15
25
|
task :default => :test
|
16
26
|
|
17
|
-
desc
|
27
|
+
desc "Test the #{spec.name} plugin."
|
18
28
|
Rake::TestTask.new(:test) do |t|
|
19
29
|
t.libs << 'lib'
|
20
|
-
t.
|
30
|
+
t.test_files = spec.test_files
|
21
31
|
t.verbose = true
|
22
32
|
end
|
23
33
|
|
24
|
-
|
34
|
+
begin
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
namespace :test do
|
37
|
+
desc "Test the #{spec.name} plugin with Rcov."
|
38
|
+
Rcov::RcovTask.new(:rcov) do |t|
|
39
|
+
t.libs << 'lib'
|
40
|
+
t.test_files = spec.test_files
|
41
|
+
t.rcov_opts << '--exclude="^(?!lib/|app/)"'
|
42
|
+
t.verbose = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue LoadError
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Generate documentation for the #{spec.name} plugin."
|
25
49
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
26
50
|
rdoc.rdoc_dir = 'rdoc'
|
27
|
-
rdoc.title =
|
51
|
+
rdoc.title = spec.name
|
52
|
+
rdoc.template = '../rdoc_template.rb'
|
28
53
|
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'
|
54
|
+
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb', 'app/**/*.rb')
|
48
55
|
end
|
49
56
|
|
50
57
|
Rake::GemPackageTask.new(spec) do |p|
|
@@ -53,30 +60,30 @@ Rake::GemPackageTask.new(spec) do |p|
|
|
53
60
|
p.need_zip = true
|
54
61
|
end
|
55
62
|
|
56
|
-
desc 'Publish the beta gem'
|
63
|
+
desc 'Publish the beta gem.'
|
57
64
|
task :pgem => [:package] do
|
58
|
-
Rake::SshFilePublisher.new('
|
65
|
+
Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{spec.name}-#{spec.version}.gem").upload
|
59
66
|
end
|
60
67
|
|
61
|
-
desc 'Publish the API documentation'
|
68
|
+
desc 'Publish the API documentation.'
|
62
69
|
task :pdoc => [:rdoc] do
|
63
|
-
Rake::SshDirPublisher.new('
|
70
|
+
Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{spec.name}", 'rdoc').upload
|
64
71
|
end
|
65
72
|
|
66
73
|
desc 'Publish the API docs and gem'
|
67
|
-
task :publish => [:pdoc, :release]
|
74
|
+
task :publish => [:pgem, :pdoc, :release]
|
68
75
|
|
69
76
|
desc 'Publish the release files to RubyForge.'
|
70
77
|
task :release => [:gem, :package] do
|
71
78
|
require 'rubyforge'
|
72
79
|
|
73
|
-
ruby_forge = RubyForge.new
|
80
|
+
ruby_forge = RubyForge.new.configure
|
74
81
|
ruby_forge.login
|
75
82
|
|
76
|
-
%w(
|
77
|
-
file = "pkg/#{
|
83
|
+
%w(gem tgz zip).each do |ext|
|
84
|
+
file = "pkg/#{spec.name}-#{spec.version}.#{ext}"
|
78
85
|
puts "Releasing #{File.basename(file)}..."
|
79
86
|
|
80
|
-
ruby_forge.add_release(
|
87
|
+
ruby_forge.add_release(spec.rubyforge_project, spec.name, spec.version, file)
|
81
88
|
end
|
82
89
|
end
|
data/app/models/address.rb
CHANGED
@@ -1,43 +1,84 @@
|
|
1
|
-
# Represents a mailing address
|
1
|
+
# Represents a generic mailing address. Addresses are expected to consist of
|
2
|
+
# the following fields:
|
3
|
+
# * +addressable+ - The record that owns the address
|
4
|
+
# * +street_1+ - The first line of the street address
|
5
|
+
# * +city+ - The name of the city
|
6
|
+
# * +postal_code+ - A value uniquely identifying the area in the city
|
7
|
+
# * +region+ - Either a known region or a custom value for countries that have no list of regions available
|
8
|
+
# * +country+ - The country, which may be implied by the region
|
9
|
+
#
|
10
|
+
# The following fields are optional:
|
11
|
+
# * +street_2+ - The second line of the street address
|
12
|
+
#
|
13
|
+
# There is no attempt to validate open-ended fields since the context in which
|
14
|
+
# the addresses will be used is unknown
|
2
15
|
class Address < ActiveRecord::Base
|
3
|
-
belongs_to
|
4
|
-
|
5
|
-
belongs_to
|
6
|
-
belongs_to :country
|
16
|
+
belongs_to :addressable, :polymorphic => true
|
17
|
+
belongs_to :region
|
18
|
+
belongs_to :country
|
7
19
|
|
8
|
-
validates_presence_of :addressable_id,
|
9
|
-
:
|
10
|
-
|
11
|
-
|
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
|
+
validates_presence_of :addressable_id, :addressable_type, :street_1, :city,
|
21
|
+
:postal_code, :country_id
|
22
|
+
validates_presence_of :region_id, :if => :known_region_required?
|
23
|
+
validates_presence_of :custom_region, :if => :custom_region_required?
|
20
24
|
|
21
|
-
|
25
|
+
attr_accessible :street_1, :street_2, :city, :postal_code, :region,
|
26
|
+
:custom_region, :country
|
22
27
|
|
23
|
-
|
24
|
-
def country_with_region_check
|
25
|
-
region ? region.country : country_without_region_check
|
26
|
-
end
|
27
|
-
alias_method_chain :country, :region_check
|
28
|
+
before_validation :ensure_exclusive_references, :set_region_attributes
|
28
29
|
|
29
|
-
# Gets the name of the region that this address is for (whether it is a
|
30
|
-
#
|
30
|
+
# Gets the name of the region that this address is for (whether it is a
|
31
|
+
# custom or known region in the database)
|
31
32
|
def region_name
|
32
|
-
custom_region ||
|
33
|
+
custom_region || region && region.name
|
33
34
|
end
|
34
35
|
|
35
|
-
# Gets
|
36
|
+
# Gets a representation of the address on a single line.
|
37
|
+
#
|
38
|
+
# For example,
|
39
|
+
#
|
40
|
+
# address = Address.new
|
41
|
+
# address.single_line # => ""
|
42
|
+
#
|
43
|
+
# address.street_1 = "1600 Amphitheatre Parkey"
|
44
|
+
# address.single_line # => "1600 Amphitheatre Parkway"
|
45
|
+
#
|
46
|
+
# address.street_2 = "Suite 100"
|
47
|
+
# address.single_line # => "1600 Amphitheatre Parkway, Suite 100"
|
48
|
+
#
|
49
|
+
# address.city = "Mountain View"
|
50
|
+
# address.single_line # => "1600 Amphitheatre Parkway, Suite 100, Mountain View"
|
51
|
+
#
|
52
|
+
# address.region = Region['US-CA']
|
53
|
+
# address.single_line # => "1600 Amphitheatre Parkway, Suite 100, Mountain View, California, United States"
|
54
|
+
#
|
55
|
+
# address.postal_code = '94043'
|
56
|
+
# address.single_line # => "1600 Amphitheatre Parkway, Suite 100, Mountain View, California 94043, United States"
|
36
57
|
def single_line
|
37
58
|
multi_line.join(', ')
|
38
59
|
end
|
39
60
|
|
40
|
-
# Gets
|
61
|
+
# Gets a representation of the address on multiple lines.
|
62
|
+
#
|
63
|
+
# For example,
|
64
|
+
#
|
65
|
+
# address = Address.new
|
66
|
+
# address.multi_line # => []
|
67
|
+
#
|
68
|
+
# address.street_1 = "1600 Amphitheatre Parkey"
|
69
|
+
# address.multi_line # => ["1600 Amphitheatre Parkey"]
|
70
|
+
#
|
71
|
+
# address.street_2 = "Suite 100"
|
72
|
+
# address.multi_line # => ["1600 Amphitheatre Parkey", "Suite 100"]
|
73
|
+
#
|
74
|
+
# address.city = "Mountain View"
|
75
|
+
# address.multi_line # => ["1600 Amphitheatre Parkey", "Suit 100", "Mountain View"]
|
76
|
+
#
|
77
|
+
# address.region = Region['US-CA']
|
78
|
+
# address.multi_line # => ["1600 Amphitheatre Parkey", "Suit 100", "Mountain View, California", "United States"]
|
79
|
+
#
|
80
|
+
# address.postal_code = '94043'
|
81
|
+
# address.multi_line # => ["1600 Amphitheatre Parkey", "Suit 100", "Mountain View, California 94043", "United States"]
|
41
82
|
def multi_line
|
42
83
|
lines = []
|
43
84
|
lines << street_1 if street_1?
|
@@ -46,39 +87,40 @@ class Address < ActiveRecord::Base
|
|
46
87
|
line = ''
|
47
88
|
line << city if city?
|
48
89
|
if region_name
|
49
|
-
line << ', '
|
90
|
+
line << ', ' unless line.blank?
|
50
91
|
line << region_name
|
51
92
|
end
|
52
93
|
if postal_code?
|
53
|
-
line << ' '
|
94
|
+
line << ' ' unless line.blank?
|
54
95
|
line << postal_code
|
55
96
|
end
|
56
|
-
lines << line
|
97
|
+
lines << line unless line.blank?
|
57
98
|
|
58
99
|
lines << country.name if country
|
59
100
|
lines
|
60
101
|
end
|
61
102
|
|
62
103
|
private
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
104
|
+
# Does the current country have a list of regions to choose from?
|
105
|
+
def known_region_required?
|
106
|
+
!country || country.regions.any?
|
80
107
|
end
|
81
108
|
|
82
|
-
|
83
|
-
|
109
|
+
# A custom region name is required if a known region was not specified and
|
110
|
+
# the country in which this address resides has no known regions in the
|
111
|
+
# database
|
112
|
+
def custom_region_required?
|
113
|
+
!region_id && country && country.regions.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
# Ensures that the custom region is not set at the same time as the known
|
117
|
+
# region
|
118
|
+
def ensure_exclusive_references
|
119
|
+
self.custom_region = nil if known_region_required?
|
120
|
+
end
|
121
|
+
|
122
|
+
# Sets the attributes on the address based on the region (if available)
|
123
|
+
def set_region_attributes
|
124
|
+
self.country = region.country if region
|
125
|
+
end
|
84
126
|
end
|