schema_associations 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 757e462b9762015289cb5f2fe1bbbf6bb1c566a4
4
+ data.tar.gz: 344a36f0f484b1c929e84f38a4c2c1daa9dbacbf
5
+ SHA512:
6
+ metadata.gz: 67b28e20707ccac8c97fb0713face550b9c44adbca8102de6149c2fe8caf5326c389a5d9030df1f6ad23e0b3d43c3e1faf9db8d98c47ccd3d1adea67f142d118
7
+ data.tar.gz: 5d19049e8483c2751f48bb39bf2f09989185a80bfa8dc1d6536e79be60d1ceaab26f3dd5b8f91812d31f1f3cc3b6759a7a98f2896fd91f51c0a2014bbc3b5148
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  rvm:
2
2
  - 1.9.2
3
3
  - 1.9.3
4
+ - 2.0.0
4
5
  gemfile:
5
6
  - gemfiles/Gemfile.rails-3.2
6
7
  notifications:
data/README.md ADDED
@@ -0,0 +1,385 @@
1
+ # SchemaAssociations
2
+
3
+ SchemaAssiciations is an ActiveRecord extension that keeps your model class
4
+ definitions simpler and more DRY, by automatically defining associations based
5
+ on the database schema.
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/schema_associations.png)](http://badge.fury.io/rb/schema_associations)
8
+ [![Build Status](https://secure.travis-ci.org/lomba/schema_associations.png)](http://travis-ci.org/lomba/schema_associations)
9
+ [![Dependency Status](https://gemnasium.com/lomba/schema_associations.png)](https://gemnasium.com/lomba/schema_associations)
10
+
11
+ ## Overview
12
+
13
+ One of the great things about Rails (ActiveRecord, in particular) is that it
14
+ inspects the database and automatically defines accessors for all your
15
+ columns, keeping your model class definitions simple and DRY. That's great
16
+ for simple data columns, but where it falls down is when your table contains
17
+ references to other tables: then the "accessors" you need are the associations
18
+ defined using `belongs_to`, `has_one`, `has_many`, and
19
+ `has_and_belongs_to_many` -- and you need to put them into your model class
20
+ definitions by hand. In fact, for every relation, you need to define two
21
+ associations each listing its inverse, such as
22
+
23
+ class Post < ActiveRecord::Base
24
+ has_many :comments, :inverse_of => :post
25
+ end
26
+
27
+ class Comment < ActiveReocrd::Base
28
+ belongs_to :post, :inverse_of => :comments
29
+ end
30
+
31
+ ....which isn't so DRY.
32
+
33
+ Enter the SchemaAssociations gem. It extends ActiveRecord to automatically
34
+ define the appropriate associations based on foreign key constraints in the
35
+ database. SchemaAssociations builds on the
36
+ [schema_plus](http://rubygems.org/gems/schema_plus) gem that automatically
37
+ defines foreign key constraints. So the common case is simple -- if you have
38
+ this in your migration:
39
+
40
+ create_table :posts do |t|
41
+ end
42
+
43
+ create_table :comments do |t|
44
+ t.integer post_id
45
+ end
46
+
47
+ Then all you need for your models is:
48
+
49
+ class Post < ActiveRecord::Base
50
+ end
51
+
52
+ class Comment < ActiveRecord::Base
53
+ end
54
+
55
+ and SchemaAssociations defines the appropriate associations under the hood.
56
+
57
+ ### What if I want something special?
58
+
59
+ You're always free to define associations yourself, if for example you want to
60
+ pass special options. SchemaAssociations won't clobber any existing
61
+ definitions.
62
+
63
+ You can also control the behavior with various options, globally via
64
+ SchemaAssociations::setup or per-model via
65
+ SchemaAssociations::ActiveRecord#schema_associations, such as:
66
+
67
+ class Post < ActiveRecord::Base
68
+ schema_associations :concise_names => false
69
+ end
70
+
71
+ See the[SchemaAssociations::Confg](http://rubydoc.info/gems/schema_associations/SchemaAssociations/Config) for the available options.
72
+
73
+ ### This seems cool, but I'm worried about too much automagic
74
+
75
+ You can globally turn off automatic creation in
76
+ `config/initializers/schema_associations.rb`:
77
+
78
+ SchemaAssociations.setup do |config|
79
+ config.auto_create = false
80
+ end
81
+
82
+ Then in any model where you want automatic associations, just do
83
+
84
+ class Post < ActiveRecord::Base
85
+ schema_associations
86
+ end
87
+
88
+ You can also pass options as per above.
89
+
90
+ ## Full Details
91
+
92
+ ### The basics
93
+
94
+ The common cases work entirely as you'd expect. For a one-to-many
95
+ relationship using standard naming conventions:
96
+
97
+ # migration:
98
+
99
+ create_table :comments do |t|
100
+ t.integer post_id
101
+ end
102
+
103
+ # schema_associations defines:
104
+
105
+ class Post < ActiveRecord::Base
106
+ has_many :comments
107
+ end
108
+
109
+ class Comment < ActiveReocrd::Base
110
+ belongs_to :post
111
+ end
112
+
113
+ For a one-to-one relationship:
114
+
115
+ # migration:
116
+
117
+ create_table :comments do |t|
118
+ t.integer post_id, :index => :unique # (using the :index option provided by schema_plus )
119
+ end
120
+
121
+ # schema_associations defines:
122
+
123
+ class Post < ActiveRecord::Base
124
+ has_one :comment
125
+ end
126
+
127
+ class Comment < ActiveReocrd::Base
128
+ belongs_to :post
129
+ end
130
+
131
+ And for many-to-many relationships:
132
+
133
+ # migration:
134
+
135
+ create_table :groups_members do |t|
136
+ integer :group_id
137
+ integer :member_id
138
+ end
139
+
140
+ # schema_associations defines:
141
+
142
+ class Group < ActiveReocrd::Base
143
+ has_and_belongs_to_many :members
144
+ end
145
+
146
+ class Member < ActiveRecord::Base
147
+ has_and_belongs_to_many :groups
148
+ end
149
+
150
+ ### Unusual names, multiple references
151
+
152
+ Sometimes you want or need to deviate from the simple naming conventions. In
153
+ this case, the `belongs_to` relationship name is taken from the name of the
154
+ foreign key column, and the `has_many` or `has_one` is named by the
155
+ referencing table, suffixed with "as" the relationship name. An example
156
+ should make this clear...
157
+
158
+ Suppose your company hires interns, and each intern is assigned a manager and
159
+ a mentor, who are regular employees.
160
+
161
+ create_table :interns do |t|
162
+ t.integer :manager_id, :references => :employees
163
+ t.integer :mentor_id, :references => :employees
164
+ end
165
+
166
+ SchemaAssociations defines a `belongs_to` association for each reference,
167
+ named according to the column:
168
+
169
+ class Intern < ActiveRecord::Base
170
+ belongs_to :manager, :class_name => "Employee", :foreign_key => "manager_id"
171
+ belongs_to :mentor, :class_name => "Employee", :foreign_key => "mentor_id"
172
+ end
173
+
174
+ And the corresponding `has_many` association each gets a suffix to indicate
175
+ which one relation it refers to:
176
+
177
+ class Employee < ActiveRecord::Base
178
+ has_many :interns_as_manager, :class_name => "Intern", :foreign_key => "manager_id"
179
+ has_many :interns_as_mentor, :class_name => "Intern", :foreign_key => "mentor_id"
180
+ end
181
+
182
+ ### Special case for trees
183
+
184
+ If your forward relation is named "parent", SchemaAssociations names the
185
+ reverse relation "child" or "children". That is, if you have:
186
+
187
+ create_table :nodes
188
+ t.integer :parent_id # schema_plus assumes it's a reference to this table
189
+ end
190
+
191
+ Then SchemaAssociations will define
192
+
193
+ class Node < ActiveRecord::Base
194
+ belongs_to :parent, :class_name => "Node", :foreign_key => "parent_id"
195
+ has_many :children, :class_name => "Node", :foreign_key => "parent_id"
196
+ end
197
+
198
+ ### Concise names
199
+
200
+ For modularity in your tables and classes, you might use a common prefix for
201
+ related objects. For example, you may have widgets each of which has a color,
202
+ and might have one base that has a top color and a bottom color, from the same
203
+ set of colors.
204
+
205
+ create_table :widget_colors |t|
206
+ end
207
+
208
+ create_table :widgets do |t|
209
+ t.integer :widget_color_id
210
+ end
211
+
212
+ create_table :widget_base
213
+ t.integer :widget_id, :index => :unique
214
+ t.integer :top_widget_color_id, :references => :widget_colors
215
+ t.integer :bottom_widget_color_id, :references => :widget_colors
216
+ end
217
+
218
+ Using the full name for the associations would make your code verbose and not
219
+ quite DRY:
220
+
221
+ @widget.widget_color
222
+ @widget.widget_base.top_widget_color
223
+
224
+ Instead, by default, SchemaAssociations uses concise names: shared leading
225
+ words are removed from the association name. So instead of the above, your
226
+ code looks like:
227
+
228
+ @widget.color
229
+ @widget.base.top_color
230
+
231
+ i.e. these associations would be defined:
232
+
233
+ class WidgetColor < ActiveRecord::Base
234
+ has_many :widgets, :class_name => "Widget", :foreign_key => "widget_color_id"
235
+ has_many :bases_as_top, :class_name => "WidgetBase", :foreign_key => "top_widget_color_id"
236
+ has_many :bases_as_bottom, :class_name => "WidgetBase", :foreign_key => "bottom_widget_color_id"
237
+ end
238
+
239
+ class Widget < ActiveRecord::Base
240
+ belongs_to :color, :class_name => "WidgetColor", :foreign_key => "widget_color_id"
241
+ has_one :base, :class_name => "WidgetBase", :foreign_key => "widget_base_id"
242
+ end
243
+
244
+ class WidgetBase < ActiveRecord::Base
245
+ belongs_to :top_color, :class_name => "WidgetColor", :foreign_key => "top_widget_color_id"
246
+ belongs_to :bottom_color, :class_name => "WidgetColor", :foreign_key => "bottom_widget_color_id"
247
+ belongs_to :widget, :class_name => "Widget", :foreign_key => "widget_id"
248
+ end
249
+
250
+ If you like the formality of using full names for the asociations, you can
251
+ turn off concise names globally or per-model, see [SchemaAssociations::Config](http://rubydoc.info/gems/schema_associations/SchemaAssociations/Config)
252
+
253
+ ### Ordering `has_many` using `position`
254
+
255
+ If the target of a `has_many` association has a column named `position`,
256
+ SchemaAssociations will specify `:order => :position` for the association.
257
+ That is,
258
+
259
+ create_table :comments do |t|
260
+ t.integer post_id
261
+ t.integer position
262
+ end
263
+
264
+ leads to
265
+
266
+ class Post < ActiveRecord::Base
267
+ has_many :comments, :order => :position
268
+ end
269
+
270
+ ## Table names and model class names
271
+
272
+ SchemaAssociations determins the mode class name from the table name using the same convention (and helpers) that ActiveRecord uses. But sometimes you might be doing things differently. For example, in an engine you might have a prefix that goes in front of all table names, and the models might all be in a namespace.
273
+
274
+ To that end, SchemaAssociations lets you configure mappings from a table name prefix to a model class name prefix to use instead. For example, suppose your database had tables:
275
+
276
+ hpy_campers
277
+ hpy_go_lucky
278
+
279
+ The default model class names would be
280
+
281
+ HpyCampers
282
+ HpyGoLucky
283
+
284
+ But if instead you wanted
285
+
286
+ Happy::Campers
287
+ Happy::GoLucky
288
+
289
+ You could set up this mapping in `config/initializers/schema_associations.rb`:
290
+
291
+ SchemaPlus.setup do |config|
292
+ config.table_prefix_map["hpy_"] = "Happy::"
293
+ end
294
+
295
+ Tables names that don't start with `hpy_` will continue to use the default determination.
296
+
297
+ You can set up multiple mappings. E.g. if you're using several engines they can each set up the mapping for their own moduels.
298
+
299
+ You can set up a mapping from or to the empty string, in order to unconditionally add or remove prefixes from all model class names.
300
+
301
+
302
+ ## How do I know what it did?
303
+
304
+ If you're curious (or dubious) about what associations SchemaAssociations
305
+ defines, you can check the log file. For every assocation that
306
+ SchemaAssociations defines, it generates an info entry such as
307
+
308
+ [schema_associations] Post.has_many :comments, :class_name "Comment", :foreign_key "comment_id"
309
+
310
+ which shows the exact method definition call.
311
+
312
+
313
+ SchemaAssociations defines the associations lazily, only creating them when
314
+ they're first needed. So you may need to search through the log file to find
315
+ them all (and some may not be defined at all if they were never needed for the
316
+ use cases that you logged).
317
+
318
+ ## Compatibility
319
+
320
+ SchemaAssociations supports all combinations of:
321
+ * rails 3.2
322
+ * MRI ruby 1.9.2 or 1.9.3
323
+
324
+
325
+ Note: As of version 1.0.0, ruby 1.8.7 and rails < 3.2 are no longer supported.
326
+ The last version to support them is 0.1.2
327
+
328
+ ## Installation
329
+
330
+ Install from http://rubygems.org via
331
+
332
+ $ gem install "schema_associations"
333
+
334
+ or in a Gemfile
335
+
336
+ gem "schema_associations"
337
+
338
+ ## Testing
339
+
340
+ SchemaAssociations is tested using rspec, sqlite3, and rvm, with some hackery
341
+ to test against multiple versions of rails and ruby. To run the full combo of
342
+ tests, after you've forked & cloned:
343
+
344
+ $ cd schema_associations
345
+ $ ./runspecs --install # do this once to install gem dependencies for all versions (slow)
346
+ $ ./runspecs # as many times as you like
347
+
348
+ See `./runspecs --help` for more options. In particular, to run rspec on a
349
+ specific file or example (rather than running the full suite) you can do, e.g.
350
+
351
+ $ ./runspecs [other options] --rspec -- spec/association_spec.rb -e 'base'
352
+
353
+ If you're running ruby 1.9, code coverage results will be in
354
+ coverage/index.html -- it should be at 100% coverage.
355
+
356
+ ## Release notes:
357
+
358
+ ### 1.1.0
359
+
360
+ * New feature: `config.table_prefix_map`
361
+
362
+ ### 1.0.1
363
+
364
+ * Bug fix: use singular :inverse_of for :belongs_to of a :has_one
365
+
366
+
367
+ ### 1.0.0
368
+
369
+ * Use :inverse_of in generated associations
370
+
371
+ * Drop support for ruby 1.8.7 and rails < 3.2
372
+
373
+
374
+ ## History
375
+
376
+ * SchemaAssociations is derived from the "Red Hill On Rails" plugin
377
+ foreign_key_associations originally created by harukizaemon
378
+ (https://github.com/harukizaemon)
379
+
380
+ * SchemaAssociations was created in 2011 by Michal Lomnicki and Ronen Barzel
381
+
382
+
383
+ ## License
384
+
385
+ This gem is released under the MIT license.
@@ -66,13 +66,20 @@ module SchemaAssociations
66
66
  # +:has_and_belongs_to_many+, or +nil+. Default is +nil+.
67
67
  has_value :only_type, :default => nil
68
68
 
69
+ ##
70
+ # :attr_accessor: table_prefix_map
71
+ #
72
+ # Hash whose keys are possible matches at the start of table names, and
73
+ # whose corresponding values are the prefix to use in front of class
74
+ # names.
75
+ has_value :table_prefix_map, :default => {}
76
+
69
77
  def dup #:nodoc:
70
78
  self.class.new(Hash[attributes.collect{ |key, val| [key, Valuable === val ? val.class.new(val.attributes) : val] }])
71
79
  end
72
80
 
73
81
  def update_attributes(opts)#:nodoc:
74
82
  opts = opts.dup
75
- opts.keys.each { |key| self.send(key).update_attributes(opts.delete(key)) if self.class.attributes.include? key and Hash === opts[key] }
76
83
  super(opts)
77
84
  self
78
85
  end
@@ -102,8 +102,8 @@ module SchemaAssociations
102
102
  references_name = fk.references_table_name.singularize
103
103
  referencing_name = referencing_table_name.singularize
104
104
 
105
- referencing_class_name = referencing_name.classify
106
- references_class_name = references_name.classify
105
+ referencing_class_name = _get_class_name(referencing_name)
106
+ references_class_name = _get_class_name(references_name)
107
107
 
108
108
  names = _determine_association_names(column_name.sub(/_id$/, ''), referencing_name, references_name)
109
109
 
@@ -221,6 +221,16 @@ module SchemaAssociations
221
221
  return true
222
222
  end
223
223
 
224
+ def _get_class_name(name) #:nodoc:
225
+ name = name.dup
226
+ found = schema_associations_config.table_prefix_map.find { |table_prefix, class_prefix|
227
+ name.sub! %r[\A#{table_prefix}], ''
228
+ }
229
+ name = name.classify
230
+ name = found.last + name if found
231
+ name
232
+ end
233
+
224
234
  def _method_exists?(name) #:nodoc:
225
235
  method_defined?(name) || private_method_defined?(name) and not (name == :type && [Object, Kernel].include?(instance_method(:type).owner))
226
236
  end
@@ -1,3 +1,3 @@
1
1
  module SchemaAssociations
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
data/runspecs CHANGED
@@ -5,7 +5,7 @@ require 'ostruct'
5
5
  require 'shellwords'
6
6
  require 'tempfile'
7
7
 
8
- RUBY_VERSIONS = %W[1.9.2 1.9.3]
8
+ RUBY_VERSIONS = %W[1.9.3 2.0.0]
9
9
  RAILS_VERSIONS = %W[3.2]
10
10
 
11
11
  o = OpenStruct.new
@@ -321,6 +321,19 @@ describe ActiveRecord::Base do
321
321
  end
322
322
  end
323
323
 
324
+ it "maps table prefix" do
325
+ with_associations_config(:table_prefix_map => { "wooga_" => "Happy"} ) do
326
+ create_tables(
327
+ "wooga_posts", {}, {},
328
+ "wooga_comments", {}, { :wooga_post_id => { :references => :wooga_posts} }
329
+ )
330
+ class HappyPost < ActiveRecord::Base ; self.table_name = 'wooga_posts' ; end
331
+ class HappyComment < ActiveRecord::Base ; self.table_name = 'wooga_comments' ; end
332
+ Kernel.warn HappyPost.reflect_on_all_associations.inspect
333
+ HappyComment.reflect_on_association(:post).class_name.should == "HappyPost"
334
+ HappyPost.reflect_on_association(:comments).class_name.should == "HappyComment"
335
+ end
336
+ end
324
337
 
325
338
  context "with position" do
326
339
  before(:each) do
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schema_associations
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ronen Barzel
@@ -10,118 +9,104 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2013-01-21 00:00:00.000000000 Z
12
+ date: 2013-05-30 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: schema_plus
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
- - - ! '>='
18
+ - - '>='
21
19
  - !ruby/object:Gem::Version
22
20
  version: '0'
23
21
  type: :runtime
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
- - - ! '>='
25
+ - - '>='
29
26
  - !ruby/object:Gem::Version
30
27
  version: '0'
31
28
  - !ruby/object:Gem::Dependency
32
29
  name: rake
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
- - - ! '>='
32
+ - - '>='
37
33
  - !ruby/object:Gem::Version
38
34
  version: '0'
39
35
  type: :development
40
36
  prerelease: false
41
37
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
38
  requirements:
44
- - - ! '>='
39
+ - - '>='
45
40
  - !ruby/object:Gem::Version
46
41
  version: '0'
47
42
  - !ruby/object:Gem::Dependency
48
43
  name: rdoc
49
44
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
45
  requirements:
52
- - - ! '>='
46
+ - - '>='
53
47
  - !ruby/object:Gem::Version
54
48
  version: '0'
55
49
  type: :development
56
50
  prerelease: false
57
51
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
52
  requirements:
60
- - - ! '>='
53
+ - - '>='
61
54
  - !ruby/object:Gem::Version
62
55
  version: '0'
63
56
  - !ruby/object:Gem::Dependency
64
57
  name: rspec
65
58
  requirement: !ruby/object:Gem::Requirement
66
- none: false
67
59
  requirements:
68
- - - ! '>='
60
+ - - '>='
69
61
  - !ruby/object:Gem::Version
70
62
  version: '0'
71
63
  type: :development
72
64
  prerelease: false
73
65
  version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
66
  requirements:
76
- - - ! '>='
67
+ - - '>='
77
68
  - !ruby/object:Gem::Version
78
69
  version: '0'
79
70
  - !ruby/object:Gem::Dependency
80
71
  name: sqlite3
81
72
  requirement: !ruby/object:Gem::Requirement
82
- none: false
83
73
  requirements:
84
- - - ! '>='
74
+ - - '>='
85
75
  - !ruby/object:Gem::Version
86
76
  version: '0'
87
77
  type: :development
88
78
  prerelease: false
89
79
  version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
80
  requirements:
92
- - - ! '>='
81
+ - - '>='
93
82
  - !ruby/object:Gem::Version
94
83
  version: '0'
95
84
  - !ruby/object:Gem::Dependency
96
85
  name: simplecov
97
86
  requirement: !ruby/object:Gem::Requirement
98
- none: false
99
87
  requirements:
100
- - - ! '>='
88
+ - - '>='
101
89
  - !ruby/object:Gem::Version
102
90
  version: '0'
103
91
  type: :development
104
92
  prerelease: false
105
93
  version_requirements: !ruby/object:Gem::Requirement
106
- none: false
107
94
  requirements:
108
- - - ! '>='
95
+ - - '>='
109
96
  - !ruby/object:Gem::Version
110
97
  version: '0'
111
98
  - !ruby/object:Gem::Dependency
112
99
  name: simplecov-gem-adapter
113
100
  requirement: !ruby/object:Gem::Requirement
114
- none: false
115
101
  requirements:
116
- - - ! '>='
102
+ - - '>='
117
103
  - !ruby/object:Gem::Version
118
104
  version: '0'
119
105
  type: :development
120
106
  prerelease: false
121
107
  version_requirements: !ruby/object:Gem::Requirement
122
- none: false
123
108
  requirements:
124
- - - ! '>='
109
+ - - '>='
125
110
  - !ruby/object:Gem::Version
126
111
  version: '0'
127
112
  description: SchemaAssociations extends ActiveRecord to automatically create associations
@@ -139,7 +124,7 @@ files:
139
124
  - .gitignore
140
125
  - .travis.yml
141
126
  - MIT-LICENSE
142
- - README.rdoc
127
+ - README.md
143
128
  - Rakefile
144
129
  - gemfiles/Gemfile.rails-3.2
145
130
  - gemfiles/Gemfile.rails-3.2.lock
@@ -155,27 +140,26 @@ files:
155
140
  - spec/spec_helper.rb
156
141
  homepage: https://github.com/lomba/schema_associations
157
142
  licenses: []
143
+ metadata: {}
158
144
  post_install_message:
159
145
  rdoc_options: []
160
146
  require_paths:
161
147
  - lib
162
148
  required_ruby_version: !ruby/object:Gem::Requirement
163
- none: false
164
149
  requirements:
165
- - - ! '>='
150
+ - - '>='
166
151
  - !ruby/object:Gem::Version
167
152
  version: '0'
168
153
  required_rubygems_version: !ruby/object:Gem::Requirement
169
- none: false
170
154
  requirements:
171
- - - ! '>='
155
+ - - '>='
172
156
  - !ruby/object:Gem::Version
173
157
  version: '0'
174
158
  requirements: []
175
159
  rubyforge_project: schema_associations
176
- rubygems_version: 1.8.24
160
+ rubygems_version: 2.0.3
177
161
  signing_key:
178
- specification_version: 3
162
+ specification_version: 4
179
163
  summary: ActiveRecord extension that automatically (DRY) creates associations based
180
164
  on the schema
181
165
  test_files:
data/README.rdoc DELETED
@@ -1,330 +0,0 @@
1
- = SchemaAssociations
2
-
3
- SchemaAssiciations is an ActiveRecord extension that keeps your model class
4
- definitions simpler and more DRY, by automatically defining associations
5
- based on the database schema.
6
-
7
- {<img src="https://secure.travis-ci.org/lomba/schema_associations.png"/>}[http://travis-ci.org/lomba/schema_associations]
8
- {<img src="https://gemnasium.com/lomba/schema_associations.png" alt="Dependency Status" />}[https://gemnasium.com/lomba/schema_associations]
9
-
10
- == Overview
11
-
12
- One of the great things about Rails (ActiveRecord, in particular) is that
13
- it inspects the database and automatically defines accessors for all your
14
- columns, keeping your model class definitions simple and DRY. That's great
15
- for simple data columns, but where it falls down is when your table
16
- contains references to other tables: then the "accessors" you need are the
17
- associations defined using +belongs_to+, +has_one+, +has_many+, and
18
- +has_and_belongs_to_many+ -- and you need to put them into your model class
19
- definitions by hand. In fact, for every relation, you need to define two
20
- associations each listing its inverse, such as
21
-
22
- class Post < ActiveRecord::Base
23
- has_many :comments, :inverse_of => :post
24
- end
25
-
26
- class Comment < ActiveReocrd::Base
27
- belongs_to :post, :inverse_of => :comments
28
- end
29
-
30
- ....which isn't so DRY.
31
-
32
- Enter the SchemaAssociations gem. It extends ActiveRecord to automatically
33
- define the appropriate associations based on foreign key constraints in the
34
- database. SchemaAssociations builds on the
35
- {+schema_plus+}[http://rubygems.org/gems/schema_plus] gem that
36
- automatically defines foreign key constraints. So the common case is simple -- if you have this in your migration:
37
-
38
- create_table :posts do |t|
39
- end
40
-
41
- create_table :comments do |t|
42
- t.integer post_id
43
- end
44
-
45
- Then all you need for your models is:
46
-
47
- class Post < ActiveRecord::Base
48
- end
49
-
50
- class Comment < ActiveRecord::Base
51
- end
52
-
53
- and SchemaAssociations defines the appropriate associations under the hood.
54
-
55
- === What if I want something special?
56
-
57
- You're always free to define associations yourself, if for example you want
58
- to pass special options. SchemaAssociations won't clobber any existing
59
- definitions.
60
-
61
- You can also control the behavior with various options, globally via
62
- SchemaAssociations::setup or per-model via SchemaAssociations::ActiveRecord#schema_associations, such as:
63
-
64
- class Post < ActiveRecord::Base
65
- schema_associations :concise_names => false
66
- end
67
-
68
- See SchemaAssociations::Config for the available options.
69
-
70
- === This seems cool, but I'm worried about too much automagic
71
-
72
- You can globally turn off automatic creation in <tt>config/initializers/schema_associations.rb</tt>:
73
-
74
- SchemaAssociations.setup do |config|
75
- config.auto_create = false
76
- end
77
-
78
- Then in any model where you want automatic associations, just do
79
-
80
- class Post < ActiveRecord::Base
81
- schema_associations
82
- end
83
-
84
- You can also pass options as per above.
85
-
86
- == Full Details
87
-
88
- === The basics
89
-
90
- The common cases work entirely as you'd expect. For a one-to-many
91
- relationship using standard naming conventions:
92
-
93
- # migration:
94
-
95
- create_table :comments do |t|
96
- t.integer post_id
97
- end
98
-
99
- # schema_associations defines:
100
-
101
- class Post < ActiveRecord::Base
102
- has_many :comments
103
- end
104
-
105
- class Comment < ActiveReocrd::Base
106
- belongs_to :post
107
- end
108
-
109
- For a one-to-one relationship:
110
-
111
- # migration:
112
-
113
- create_table :comments do |t|
114
- t.integer post_id, :index => :unique # (using the :index option provided by schema_plus )
115
- end
116
-
117
- # schema_associations defines:
118
-
119
- class Post < ActiveRecord::Base
120
- has_one :comment
121
- end
122
-
123
- class Comment < ActiveReocrd::Base
124
- belongs_to :post
125
- end
126
-
127
- And for many-to-many relationships:
128
-
129
- # migration:
130
-
131
- create_table :groups_members do |t|
132
- integer :group_id
133
- integer :member_id
134
- end
135
-
136
- # schema_associations defines:
137
-
138
- class Group < ActiveReocrd::Base
139
- has_and_belongs_to_many :members
140
- end
141
-
142
- class Member < ActiveRecord::Base
143
- has_and_belongs_to_many :groups
144
- end
145
-
146
- === Unusual names, multiple references
147
-
148
- Sometimes you want or need to deviate from the simple naming conventions. In
149
- this case, the +belongs_to+ relationship name is taken from the name of the
150
- foreign key column, and the +has_many+ or +has_one+ is named by the referencing
151
- table, suffixed with "as" the relationship name. An example should make this clear...
152
-
153
- Suppose your company hires interns, and each intern is assigned a manager and a
154
- mentor, who are regular employees.
155
-
156
- create_table :interns do |t|
157
- t.integer :manager_id, :references => :employees
158
- t.integer :mentor_id, :references => :employees
159
- end
160
-
161
- SchemaAssociations defines a +belongs_to+ association for each reference, named according to
162
- the column:
163
-
164
- class Intern < ActiveRecord::Base
165
- belongs_to :manager, :class_name => "Employee", :foreign_key => "manager_id"
166
- belongs_to :mentor, :class_name => "Employee", :foreign_key => "mentor_id"
167
- end
168
-
169
- And the corresponding +has_many+ association each gets a suffix to indicate which one relation it refers to:
170
-
171
- class Employee < ActiveRecord::Base
172
- has_many :interns_as_manager, :class_name => "Intern", :foreign_key => "manager_id"
173
- has_many :interns_as_mentor, :class_name => "Intern", :foreign_key => "mentor_id"
174
- end
175
-
176
- === Special case for trees
177
-
178
- If your forward relation is named "parent", SchemaAssociations names the reverse
179
- relation "child" or "children". That is, if you have:
180
-
181
- create_table :nodes
182
- t.integer :parent_id # schema_plus assumes it's a reference to this table
183
- end
184
-
185
- Then SchemaAssociations will define
186
-
187
- class Node < ActiveRecord::Base
188
- belongs_to :parent, :class_name => "Node", :foreign_key => "parent_id"
189
- has_many :children, :class_name => "Node", :foreign_key => "parent_id"
190
- end
191
-
192
- === Concise names
193
-
194
- For modularity in your tables and classes, you might use a common prefix for
195
- related objects. For example, you may have widgets each of which has a color,
196
- and might have one base that has a top color and a bottom color, from the same
197
- set of colors.
198
-
199
- create_table :widget_colors |t|
200
- end
201
-
202
- create_table :widgets do |t|
203
- t.integer :widget_color_id
204
- end
205
-
206
- create_table :widget_base
207
- t.integer :widget_id, :index => :unique
208
- t.integer :top_widget_color_id, :references => :widget_colors
209
- t.integer :bottom_widget_color_id, :references => :widget_colors
210
- end
211
-
212
- Using the full name for the associations would make your code verbose and not quite DRY:
213
-
214
- @widget.widget_color
215
- @widget.widget_base.top_widget_color
216
-
217
- Instead, by default, SchemaAssociations uses concise names: shared leading
218
- words are removed from the association name. So instead of the above, your
219
- code looks like:
220
-
221
- @widget.color
222
- @widget.base.top_color
223
-
224
- i.e. these associations would be defined:
225
-
226
- class WidgetColor < ActiveRecord::Base
227
- has_many :widgets, :class_name => "Widget", :foreign_key => "widget_color_id"
228
- has_many :bases_as_top, :class_name => "WidgetBase", :foreign_key => "top_widget_color_id"
229
- has_many :bases_as_bottom, :class_name => "WidgetBase", :foreign_key => "bottom_widget_color_id"
230
- end
231
-
232
- class Widget < ActiveRecord::Base
233
- belongs_to :color, :class_name => "WidgetColor", :foreign_key => "widget_color_id"
234
- has_one :base, :class_name => "WidgetBase", :foreign_key => "widget_base_id"
235
- end
236
-
237
- class WidgetBase < ActiveRecord::Base
238
- belongs_to :top_color, :class_name => "WidgetColor", :foreign_key => "top_widget_color_id"
239
- belongs_to :bottom_color, :class_name => "WidgetColor", :foreign_key => "bottom_widget_color_id"
240
- belongs_to :widget, :class_name => "Widget", :foreign_key => "widget_id"
241
- end
242
-
243
- If you like the formality of using full names for the asociations, you can turn off concise names globally or per-model, see SchemaAssociations::Config
244
-
245
- === Ordering +has_many+ using +position+
246
-
247
- If the target of a +has_many+ association has a column named +position+, SchemaAssociation will specify <tt>:order => :position</tt> for the association. That is,
248
-
249
- create_table :comments do |t|
250
- t.integer post_id
251
- t.integer position
252
- end
253
-
254
- leads to
255
-
256
- class Post < ActiveRecord::Base
257
- has_many :comments, :order => :position
258
- end
259
-
260
-
261
- == How do I know what it did?
262
-
263
- If you're curious (or dubious) about what associations SchemaAssociations defines, you can check the log file. For every assocation that SchemaAssociations defines, it generates an info entry such as
264
-
265
- [schema_associations] Post.has_many :comments, :class_name "Comment", :foreign_key "comment_id"
266
-
267
- which shows the exact method definition call.
268
-
269
- SchemaAssociations defines the associations lazily, only creating them when
270
- they're first needed. So you may need to search through the log file to find
271
- them all (and some may not be defined at all if they were never needed for the
272
- use cases that you logged).
273
-
274
- == Compatibility
275
-
276
- SchemaAssociations supports all combinations of:
277
- * rails 3.2
278
- * MRI ruby 1.9.2 or 1.9.3
279
-
280
- Note: As of version 1.0.0, ruby 1.8.7 and rails < 3.2 are no longer supported. The last version to support them is 0.1.2
281
-
282
- == Installation
283
-
284
- Install from http://rubygems.org via
285
-
286
- $ gem install "schema_associations"
287
-
288
- or in a Gemfile
289
-
290
- gem "schema_associations"
291
-
292
- == Testing
293
-
294
- SchemaAssociations is tested using rspec, sqlite3, and rvm, with some
295
- hackery to test against multiple versions of rails and ruby. To run the
296
- full combo of tests, after you've forked & cloned:
297
-
298
- $ cd schema_associations
299
- $ ./runspecs --install # do this once to install gem dependencies for all versions (slow)
300
- $ ./runspecs # as many times as you like
301
-
302
- See <tt>./runspecs --help</tt> for more options. In particular, to run
303
- rspec on a specific file or example (rather than running the full suite)
304
- you can do, e.g.
305
-
306
- $ ./runspecs [other options] --rspec -- spec/association_spec.rb -e 'base'
307
-
308
- If you're running ruby 1.9, code coverage results will be in coverage/index.html -- it should be at 100% coverage.
309
-
310
- == Release notes:
311
-
312
- === 1.0.1
313
-
314
- * Bug fix: use singular :inverse_of for :belongs_to of a :has_one
315
-
316
- === 1.0.0
317
-
318
- * Use :inverse_of in generated associations
319
-
320
- * Drop support for ruby 1.8.7 and rails < 3.2
321
-
322
- == History
323
-
324
- * SchemaAssociations is derived from the "Red Hill On Rails" plugin foreign_key_associations originally created by harukizaemon (https://github.com/harukizaemon)
325
-
326
- * SchemaAssociations was created in 2011 by Michal Lomnicki and Ronen Barzel
327
-
328
- == License
329
-
330
- This gem is released under the MIT license.