address_concern 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,66 @@
1
+ module AddressConcern::AddressAssociations
2
+ extend ActiveSupport::Concern
3
+ module ClassMethods
4
+ # Creates a belongs_to +address+ association, named "address" by default but a different name may be
5
+ # provided. In the Address model, creates an inverse association unless you pass +inverse: false+.
6
+ #
7
+ # You can pass options to the belongs_to, like this:
8
+ # belongs_to_address :work_address, dependent: :destroy
9
+ #
10
+ # You can also pass options to the inverse has_one assocation in the Address model, via the
11
+ # +inverse+ option:
12
+ # belongs_to_address :home_address, inverse: {foreign_key: :physical_address_id}
13
+ def belongs_to_address(name = :address, inverse: nil, **options)
14
+ options.reverse_merge!({
15
+ class_name: 'Address'
16
+ })
17
+ raise "association :#{name} already exists on #{self}" if reflect_on_association(name)
18
+ belongs_to name, **options
19
+ #puts %(reflect_on_association(#{name})=#{reflect_on_association(name).inspect})
20
+
21
+ unless inverse == false
22
+ inverse ||= {}
23
+ inverse.reverse_merge!({
24
+ name: self.name.underscore.to_sym,
25
+ inverse_of: name,
26
+ class_name: self.name,
27
+ foreign_key: "#{name}_id",
28
+ })
29
+ name = inverse.delete(:name)
30
+ inverse_of = inverse.delete(:inverse_of)
31
+ Address.class_eval do
32
+ raise "association :#{name} already exists on #{self}" if reflect_on_association(name)
33
+ has_one name, inverse_of: inverse_of, **inverse
34
+ #puts %(reflect_on_association(#{name})=#{reflect_on_association(name).inspect})
35
+ end
36
+ end
37
+ end
38
+
39
+ # Creates a has_one +address+ association, representing the one and only address associated with the current record
40
+ def has_address
41
+ has_one :address, as: :addressable
42
+ create_addressable_association_on_address
43
+ end
44
+
45
+ # Creates a has_many +addresses+ association, representing all addresses associated with the current record
46
+ def has_addresses(options = {})
47
+ has_many :addresses, as: :addressable
48
+ (options[:types] || ()).each do |type|
49
+ has_one :"#{type}_address", -> { where({address_type: type}) }, class_name: 'Address', as: :addressable
50
+ end
51
+ create_addressable_association_on_address
52
+ end
53
+
54
+ def create_addressable_association_on_address
55
+ Address.class_eval do
56
+ unless reflect_on_association(:addressable)
57
+ belongs_to :addressable, :polymorphic => true
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ ActiveRecord::Base.class_eval do
65
+ include AddressConcern::AddressAssociations
66
+ end
@@ -0,0 +1,9 @@
1
+ require 'attribute_normalizer'
2
+ require 'facets/string/cleanlines'
3
+
4
+ AttributeNormalizer.configure do |config|
5
+ config.normalizers[:cleanlines] = lambda do |input, options|
6
+ input.to_s.cleanlines.to_a.join("\n")
7
+ end
8
+ end
9
+
@@ -0,0 +1,5 @@
1
+ module AddressConcern
2
+ def self.version
3
+ "2.0.0"
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails'
2
+ require 'carmen'
3
+ require 'active_record'
4
+ require 'active_record_ignored_attributes'
5
+
6
+ Carmen.i18n_backend.append_locale_path File.join(File.dirname(__FILE__), '../config/locale/overlay/en')
7
+
8
+ require 'address_concern/version'
9
+ require 'address_concern/attribute_normalizer'
10
+ require_relative 'address_concern/address'
11
+ require_relative 'address_concern/address_associations'
@@ -0,0 +1,23 @@
1
+ require 'rails/generators'
2
+ module AddressConcern
3
+ class InstallGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+
7
+ def self.source_root
8
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
9
+ end
10
+
11
+ def self.next_migration_number(dirname)
12
+ if ActiveRecord::Base.timestamped_migrations
13
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
14
+ else
15
+ "%.3d" % (current_migration_number(dirname) + 1)
16
+ end
17
+ end
18
+
19
+ def create_migration_file
20
+ migration_template 'migration.rb', 'db/migrate/create_addresses.rb'
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # This file controls what config variables you want to be able to allow your users
2
+ # to set, as well as those you'll be able to access from within the application.
3
+ #
4
+ # If you want to be able to access a string config[:site_title], for example:
5
+ #
6
+ # site_title:
7
+ # name: Site Title
8
+ # type: string
9
+ # default: My Site
10
+ #
11
+ # 'name' is the name that appears in the edit form
12
+ #
13
+ # 'type' can be 'string' for a text field, 'password' for a password field or 'text' for a text area
14
+ # 'type' defaults to 'string'
15
+ #
16
+ # 'default' is the default value to use if there's no entry in the database. Otherwise, nil will be returned
17
+ #
18
+ # Some Examples:
19
+ #
20
+ # site_title:
21
+ # name: Site Title
22
+ # default: My Site
23
+ # type: string
24
+ #
25
+ # site_description:
26
+ # name: Description for Google
27
+ # default: Lots of Awesomeness Here
28
+ # type: text
29
+ #
30
+ # secret:
31
+ # name: Secret Password for Accessing Secret Areas
32
+ # default: secret
33
+ # type: password
@@ -0,0 +1,37 @@
1
+ class CreateAddresses < ActiveRecord::Migration[4.2]
2
+ def self.up
3
+ create_table :addresses do |t|
4
+ t.references :addressable, :polymorphic => true
5
+ t.string :address_type # to allow shipping/billing/etc. address
6
+
7
+ t.string :name
8
+ t.text :address
9
+ t.string :city
10
+ t.string :state
11
+ t.string :postal_code
12
+ t.string :country
13
+ t.string :country_alpha2
14
+ t.string :country_alpha3
15
+ t.string :email
16
+ t.string :phone
17
+ t.timestamps
18
+ end
19
+
20
+ change_table :addresses do |t|
21
+ t.index :addressable_id
22
+ t.index :addressable_type
23
+ t.index :address_type
24
+ t.index :name
25
+ t.index :state
26
+ t.index :country
27
+ t.index :country_alpha2
28
+ t.index :country_alpha3
29
+ t.index :email
30
+ t.index :phone
31
+ end
32
+ end
33
+
34
+ def self.down
35
+ drop_table :addresses
36
+ end
37
+ end
@@ -0,0 +1,303 @@
1
+ require 'spec_helper'
2
+
3
+ describe Address do
4
+ describe 'setting country by name' do
5
+ let(:address) { Address.new }
6
+ specify 'setting to known country' do
7
+ address.country = 'Iceland'
8
+ address.country_name.should == 'Iceland'
9
+ address.country_code.should == 'IS'
10
+ end
11
+
12
+ specify 'setting to unknown country' do
13
+ # Set it to a known country first to show that it actually *clears* these fields if they were previously set
14
+ address.country = 'Iceland'
15
+
16
+ #expect { expect {
17
+ address.country = 'Fireland'
18
+ #}.to change(address, :country_name).to(nil)
19
+ #}.to change(address, :country_code).to(nil)
20
+ address.country_name.should eq nil
21
+ address.country_code.should eq nil
22
+
23
+ address.country = 'Northern Ireland'
24
+ address.country_name.should eq nil
25
+ address.country_code.should eq nil
26
+
27
+ address.country = 'USA'
28
+ address.country_name.should eq nil
29
+ address.country_code.should eq nil
30
+ end
31
+ end
32
+
33
+ describe 'setting country by code' do
34
+ let(:address) { Address.new }
35
+ specify 'setting to known country' do
36
+ address.country_code = 'IS'
37
+ address.country_name.should == 'Iceland'
38
+ address.country_code.should == 'IS'
39
+ end
40
+
41
+ specify 'setting to unknown country' do
42
+ # Set it to a known country first to show that it actually *clears* these fields if they were previously set
43
+ address.country_code = 'IS'
44
+
45
+ #expect { expect {
46
+ address.country = 'FL'
47
+ #}.to change(address, :country_name).to(nil)
48
+ #}.to change(address, :country_code).to(nil)
49
+ address.country_name.should eq nil
50
+ address.country_code.should eq nil
51
+ end
52
+
53
+ specify 'setting to a country that is part of another country (weird)' do
54
+ # Not currently possible using country_code=
55
+ end
56
+ end
57
+
58
+ describe 'country aliases:' do
59
+ let(:address) { Address.new }
60
+ ['The Democratic Republic of the Congo', 'Democratic Republic of the Congo'].each do |_| specify _ do
61
+ address.country = _
62
+ address.country_name.should == 'Congo, The Democratic Republic of the'
63
+ address.country_name_from_code.should == 'Congo, The Democratic Republic of the'
64
+ end; end
65
+ end
66
+
67
+ describe 'started_filling_out?' do
68
+ [:address, :city, :state, :postal_code].each do |attr_name|
69
+ it "should be true when #{attr_name} (and only #{attr_name}) is present" do
70
+ Address.new(attr_name => 'something').should be_started_filling_out
71
+ end
72
+ end
73
+ it "should be true when country (and only country) is present" do
74
+ Address.new(:country => 'Latvia').should be_started_filling_out
75
+ end
76
+ end
77
+
78
+ describe 'normalization' do
79
+ describe 'address' do
80
+ it { should normalize_attribute(:address).from(" Line 1 \n Line 2 \n ").to("Line 1\nLine 2")}
81
+ [:name, :city, :state, :postal_code, :country].each do |attr_name|
82
+ it { should normalize_attribute(attr_name) }
83
+ end
84
+ end
85
+ end
86
+
87
+ describe 'parts and lines' do
88
+ it do
89
+ address = Address.new(
90
+ address: '10 Some Road',
91
+ city: 'Watford', state: 'HRT', postal_code: 'WD25 9JZ',
92
+ country: 'United Kingdom'
93
+ )
94
+ address.parts.should == [
95
+ '10 Some Road',
96
+ 'Watford',
97
+ 'Hertfordshire',
98
+ 'WD25 9JZ',
99
+ 'United Kingdom'
100
+ ]
101
+ address.lines.should == [
102
+ '10 Some Road',
103
+ 'Watford WD25 9JZ',
104
+ 'United Kingdom'
105
+ ]
106
+ address.city_line.should == 'Watford WD25 9JZ'
107
+ address.city_state_name.should == 'Watford, Hertfordshire'
108
+ end
109
+ end
110
+
111
+ describe '#city_state/#city_state_country' do
112
+ context "when address doesn't have a state" do
113
+ let(:user) { User.create }
114
+ subject { user.build_physical_address(address: '123', city: 'Stockholm', country_name: 'Sweden') }
115
+ it { subject.city_state_code.should == 'Stockholm' }
116
+ it { subject.city_state_name.should == 'Stockholm' }
117
+ it { subject.city_state_country.should == 'Stockholm, Sweden' }
118
+ end
119
+ context "when address has a state abbrevitation in :state field" do
120
+ let(:user) { User.create }
121
+ subject { user.build_physical_address(address: '123', city: 'Nelspruit', state: 'MP', country_name: 'South Africa') }
122
+ it { subject.city_state_code.should == 'Nelspruit, MP' }
123
+ it { subject.city_state_name.should == 'Nelspruit, Mpumalanga' }
124
+ it { subject.city_state_country.should == 'Nelspruit, Mpumalanga, South Africa' }
125
+ end
126
+ context "when address has a state abbrevitation in :state field (Denmark)" do
127
+ let(:user) { User.create }
128
+ subject { user.build_physical_address(address: '123', city: 'Copenhagen', state: '84', country_name: 'Denmark') }
129
+ it { subject.city_state_name.should == 'Copenhagen, Hovedstaden' }
130
+ it { subject.state_name.should == 'Hovedstaden' }
131
+ end
132
+
133
+ # United States
134
+ context "when address has a state name entered for :state instead of an abbreviation" do
135
+ let(:user) { User.create }
136
+ subject { user.build_physical_address(address: '123', city: 'Ocala', state: 'Florida', country_name: 'United States') }
137
+ it { subject.city_state_code.should == 'Ocala, Florida' }
138
+ it { subject.city_state_name.should == 'Ocala, Florida' }
139
+ it { subject.city_state_country.should == 'Ocala, Florida, United States' }
140
+ end
141
+ context "when address has a state abbrevitation in :state field" do
142
+ let(:user) { User.create }
143
+ subject { user.build_physical_address(address: '123', city: 'Ocala', state: 'FL', country_name: 'United States') }
144
+ it { subject.city_state_code.should == 'Ocala, FL' }
145
+ it { subject.city_state_name.should == 'Ocala, Florida' }
146
+ it { subject.city_state_country.should == 'Ocala, Florida, United States' }
147
+ end
148
+ context "when address has a state name entered for :state instead of an abbreviation, but state is for different country" do
149
+ let(:user) { User.create }
150
+ subject { user.build_physical_address(address: '123', city: 'Ocala', state: 'FL', country_name: 'Denmark') }
151
+ it { subject.city_state_code.should == 'Ocala, FL' }
152
+ it { subject.city_state_name.should == 'Ocala, FL' }
153
+ it { subject.city_state_country.should == 'Ocala, FL, Denmark' }
154
+ end
155
+ end
156
+
157
+ describe 'same_as?' do
158
+ it 'should be true when country is only present attribute and it matches' do
159
+ Address.new(country: 'United States').should be_same_as(
160
+ Address.new(country: 'United States'))
161
+ end
162
+ it 'should be true when country and state are only present attributes and they match' do
163
+ Address.new(state: 'Washington', country: 'United States').should be_same_as(
164
+ Address.new(state: 'Washington', country: 'United States'))
165
+ end
166
+ it "not should be true when address attribute doesn't match" do
167
+ Address.new(address: '123 C St.', state: 'Washington', country: 'United States').should_not be_same_as(
168
+ Address.new(address: '444 Z St.', state: 'Washington', country: 'United States'))
169
+ end
170
+ end
171
+
172
+ describe '#carmen_country' do
173
+ it { Address.new(country: 'South Africa').carmen_country.should be_a Carmen::Country }
174
+ end
175
+ describe '#carmen_state' do
176
+ it { Address.new(country: 'United States', state: 'OH!').carmen_state.should be_nil }
177
+ it { Address.new(country: 'United States', state: 'OH').carmen_state.should be_a Carmen::Region }
178
+ it { Address.new(country: 'United States', state: 'AA').carmen_state.should be_a Carmen::Region }
179
+ it { Address.new(country: 'South Africa', state: 'MP').carmen_state.should be_a Carmen::Region }
180
+ end
181
+
182
+ describe '#states_for_country, etc.' do
183
+ it { Address.new(country: 'United States').states_for_country.map(&:code).should include 'AA' }
184
+ it { Address.new(country: 'United States').states_for_country.map(&:name).should include 'Ohio' }
185
+ it { Address.new(country: 'United States').states_for_country.map(&:name).should include 'District of Columbia' }
186
+ it { Address.new(country: 'United States').states_for_country.map(&:name).should include 'Puerto Rico' }
187
+ it { Address.new(country: 'South Africa').states_for_country.map(&:name).should include 'Mpumalanga' }
188
+ it { Address.new(country: 'Kenya').states_for_country.typed('province').should be_empty }
189
+ # At the time of this writing, it doesn't look like Carmen has been updated to
190
+ # include the 47 counties listed under https://en.wikipedia.org/wiki/ISO_3166-2:KE.
191
+ #it { Address.new(country: 'Kenya').states_for_country.map(&:name).should include 'Nyeri' }
192
+ it { Address.new(country: 'Denmark').state_options.should be_many }
193
+ it { Address.new(country: 'Denmark').state_options.map(&:name).should include 'Sjælland' }
194
+ it { Address.new(country: 'Denmark').state_possibly_included_in_postal_address?.should eq false }
195
+
196
+ # Auckland (AUK) is a subregion of the North Island (N) subregion
197
+ it { Address.new(country: 'New Zealand').state_options.map(&:code).should include 'AUK' }
198
+ it { Address.new(country: 'New Zealand').state_options.map(&:code).should_not include 'N' }
199
+ # Chatham Islands Territory (CIT) is a top-level region
200
+ it { Address.new(country: 'New Zealand').state_options.map(&:code).should include 'CIT' }
201
+
202
+ # Abra (ABR) is a subregion of the Cordillera Administrative Region (CAR) (15) subregion
203
+ it { Address.new(country: 'Philippines').state_options.map(&:code).should include 'ABR' }
204
+ it { Address.new(country: 'Philippines').state_options.map(&:code).should_not include '15' }
205
+ # National Capital Region (00) is a top-level region
206
+ it { Address.new(country: 'Philippines').state_options.map(&:code).should include '00' }
207
+
208
+ # https://en.wikipedia.org/wiki/Provinces_of_Indonesia
209
+ # The provinces are officially grouped into seven geographical units
210
+ # Jawa Barat (JB) is a subregion of the Jawa (JW) subregion
211
+ it { Address.new(country: 'Indonesia').state_options.map(&:code).should include 'JB' }
212
+ it { Address.new(country: 'Indonesia').state_options.map(&:code).should_not include 'JW' }
213
+ # The province is not called "Jakarta Raya" according to
214
+ # https://en.wikipedia.org/wiki/ISO_3166-2:ID and https://en.wikipedia.org/wiki/Jakarta — it's
215
+ # called 'DKI Jakarta', which is short for 'Daerah Khusus Ibukota Jakarta' ('Special Capital
216
+ # City District of Jakarta'),
217
+ it { Address.new(country: 'Indonesia').state_options.map(&:name).should include 'DKI Jakarta' }
218
+
219
+ # At the time of this writing, it doesn't look like Carmen has been updated to reflect the new 18
220
+ # regions of France.
221
+ it { Address.new(country: 'France').state_options.size.should eq 0 }
222
+ #it { Address.new(country: 'France').state_options.size.should eq 18 }
223
+ #it { Address.new(country: 'France').state_options.map(&:name).should include 'Auvergne-Rhône-Alpes' }
224
+ end
225
+
226
+ describe 'associations' do
227
+ # To do (or maybe not even necessary—seems to work with only the has_one side of the association):
228
+ #describe 'when we have a polymorphic belongs_to :addressable in Address' do
229
+ #belongs_to :addressable, :polymorphic => true
230
+
231
+ describe 'Company has_one :address' do
232
+ let(:company) { Company.create }
233
+
234
+ it do
235
+ company.address.should == nil
236
+ address = company.build_address(address: '1')
237
+ company.save!
238
+ company.reload.address.should == address
239
+ address.addressable.should == company
240
+ end
241
+
242
+ end
243
+
244
+ describe 'User has_many :addresses' do
245
+ let(:user) { User.create }
246
+
247
+ it do
248
+ user.addresses.should == []
249
+ address_1 = user.addresses.build(address: '1')
250
+ address_2 = user.addresses.build(address: '2')
251
+ user.save!; user.reload
252
+ user.addresses.should == [address_1, address_2]
253
+ end
254
+
255
+ specify 'should able to set and retrieve a specific address by type (shipping or billing)' do
256
+ physical_address = user.build_physical_address(address: 'Physical')
257
+ shipping_address = user.build_shipping_address(address: 'Shipping')
258
+ billing_address = user.build_billing_address( address: 'Billing')
259
+ vacation_address = user.addresses.build(address: 'Vacation', :address_type => 'Vacation')
260
+ user.save!; user.reload
261
+ user.physical_address.should == physical_address
262
+ user.shipping_address.should == shipping_address
263
+ user.billing_address. should == billing_address
264
+ user.addresses.to_set.should == [physical_address, shipping_address, billing_address, vacation_address].to_set
265
+ physical_address.addressable.should == user
266
+ shipping_address.addressable.should == user
267
+ billing_address .addressable.should == user
268
+ end
269
+ end
270
+
271
+ describe 'Employee belongs_to :home_address' do
272
+ subject!(:employee) { Employee.create! }
273
+
274
+ it do
275
+ employee.home_address.should == nil
276
+ employee.work_address.should == nil
277
+ home_address = employee.build_home_address(address: '1')
278
+ work_address = employee.build_work_address(address: '2')
279
+ employee.save!
280
+ employee.reload.home_address.should == home_address
281
+ employee.reload.work_address.should == work_address
282
+ home_address.employee_home.should == employee
283
+ work_address.employee_work.should == employee
284
+ end
285
+ end
286
+
287
+ describe 'Child belongs_to :secret_hideout' do
288
+ subject!(:child) { Child.create! }
289
+
290
+ it do
291
+ child.address. should == nil
292
+ child.secret_hideout.should == nil
293
+ address = child.build_address(address: '2')
294
+ secret_hideout = child.build_secret_hideout(address: '1')
295
+ child.save!
296
+ child.reload.address. should == address
297
+ child.reload.secret_hideout.should == secret_hideout
298
+ address. child. should == child
299
+ secret_hideout.child_for_secret_hideout.should == child
300
+ end
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,50 @@
1
+ require 'bundler'
2
+ require 'bundler/setup'
3
+ Bundler.require(:default, :development)
4
+
5
+ #---------------------------------------------------------------------------------------------------
6
+
7
+ __DIR__ = Pathname.new(__FILE__).dirname
8
+ $LOAD_PATH.unshift __DIR__
9
+ $LOAD_PATH.unshift __DIR__ + '../lib'
10
+
11
+ #---------------------------------------------------------------------------------------------------
12
+ # ActiveRecord
13
+
14
+ require 'active_record'
15
+
16
+ log_file_path = __DIR__ + 'test.log'
17
+ log_file_path.truncate(0) rescue nil
18
+ ActiveRecord::Base.logger = Logger.new(log_file_path)
19
+
20
+ driver = (ENV["DB"] or "sqlite3").downcase
21
+ database_config = YAML::load(File.open(__DIR__ + "support/database.#{driver}.yml"))
22
+ ActiveRecord::Base.establish_connection(database_config)
23
+
24
+ require __DIR__ + 'support/schema'
25
+ require 'generators/address_concern/templates/migration'
26
+ CreateAddresses.up
27
+
28
+ #---------------------------------------------------------------------------------------------------
29
+ # RSpec
30
+
31
+ require 'rspec'
32
+
33
+ require 'active_record_ignored_attributes/matchers'
34
+
35
+
36
+ RSpec.configure do |config|
37
+ config.include AttributeNormalizer::RSpecMatcher #, :type => :models
38
+ config.expect_with(:rspec) { |c| c.syntax = :should }
39
+ config.example_status_persistence_file_path = "tmp/rspec_status.txt"
40
+ end
41
+
42
+ require 'address_concern'
43
+
44
+ require_relative 'support/models/application_record'
45
+ require_relative 'support/models/address'
46
+
47
+ # Requires supporting ruby files in spec/support/
48
+ Dir[__DIR__ + 'support/**/*.rb'].each do |f|
49
+ require f
50
+ end
@@ -0,0 +1,3 @@
1
+ adapter: mysql2
2
+ database: address_concern_test
3
+ username: root
@@ -0,0 +1,2 @@
1
+ adapter: sqlite3
2
+ database: ":memory:"
@@ -0,0 +1,3 @@
1
+ class Address < ApplicationRecord
2
+ include AddressConcern::Address
3
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,4 @@
1
+ class Child < ApplicationRecord
2
+ belongs_to_address
3
+ belongs_to_address :secret_hideout, inverse: {name: :child_for_secret_hideout}
4
+ end
@@ -0,0 +1,3 @@
1
+ class Company < ApplicationRecord
2
+ has_address
3
+ end
@@ -0,0 +1,4 @@
1
+ class Employee < ApplicationRecord
2
+ belongs_to_address :home_address, foreign_key: :physical_address_id, inverse: {name: :employee_home, foreign_key: :physical_address_id}
3
+ belongs_to_address :work_address, dependent: :destroy, inverse: {name: :employee_work}
4
+ end
@@ -0,0 +1,3 @@
1
+ class User < ApplicationRecord
2
+ has_addresses :types => [:physical, :shipping, :billing]
3
+ end
@@ -0,0 +1,19 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :users, :force => true do |t|
3
+ t.string :name
4
+ end
5
+ create_table :employees, :force => true do |t|
6
+ t.string :name
7
+ t.belongs_to :physical_address
8
+ t.belongs_to :work_address
9
+ end
10
+ create_table :companies, :force => true do |t|
11
+ t.string :name
12
+ end
13
+ create_table :children, :force => true do |t|
14
+ t.string :name
15
+ t.belongs_to :address
16
+ t.belongs_to :secret_hideout
17
+ end
18
+ end
19
+
data/tmp/.gitkeep ADDED
File without changes