schema_associations 1.0.1 → 1.1.0
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.
- checksums.yaml +7 -0
- data/.travis.yml +1 -0
- data/README.md +385 -0
- data/lib/schema_associations.rb +8 -1
- data/lib/schema_associations/active_record/associations.rb +12 -2
- data/lib/schema_associations/version.rb +1 -1
- data/runspecs +1 -1
- data/spec/association_spec.rb +13 -0
- metadata +22 -38
- data/README.rdoc +0 -330
    
        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
    
    
    
        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 | 
            +
            [](http://badge.fury.io/rb/schema_associations)
         | 
| 8 | 
            +
            [](http://travis-ci.org/lomba/schema_associations)
         | 
| 9 | 
            +
            [](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.
         | 
    
        data/lib/schema_associations.rb
    CHANGED
    
    | @@ -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 | 
| 106 | 
            -
                    references_class_name = references_name | 
| 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
         | 
    
        data/runspecs
    CHANGED
    
    
    
        data/spec/association_spec.rb
    CHANGED
    
    | @@ -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 | 
| 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- | 
| 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. | 
| 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:  | 
| 160 | 
            +
            rubygems_version: 2.0.3
         | 
| 177 161 | 
             
            signing_key: 
         | 
| 178 | 
            -
            specification_version:  | 
| 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.
         |