Do ActiveRecord relations defy DRY?

Why do we need to specify both sides of a relationship?
eg.
class Category < ActiveRecord::Base
has_many :recipes
end

class Recipe < ActiveRecord::Base
belongs_to :category
end

Doesn’t “has_many :recipes” in Category imply “belongs_to :category” in
Recipe?

I am sure there is a good explanation, but at the moment it looks like
not(DRY) to me.

Best regards,
Thushan

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Thushan wrote:

Doesn’t “has_many :recipes” in Category imply “belongs_to :category” in
Recipe?

I am sure there is a good explanation, but at the moment it looks like
not(DRY) to me.

Ruby reads it source files once and evaluates the code as it goes, you
need both calls so you can let each model know how it’s relationship to
another model.

has_many/belongs_to are simply class methods called on each model class.
They are responsible for defining the relationship of one model to
another. Part of this responsibility entails that they generate instance
methods for the relationship. This is how Category#recipes and
Recipe#category methods exist.

If you remove one of these calls then you are relying on the order in
which model files are read to be able to define the relationship and add
additional methods to the other model class.

ie:
class Category < ActiveRecord::Base
has_many :recipes
end

 class Recipe < ActiveRecord::Base

What do you expect to happen in the above? At the time of the call to
“has_many” Recipe doesn’t exist, so you can’t let Recipe know about it’s
relationship to Category. Now you could change how ActiveRecord works so
Recipe would query a “Association” object to pull in any relationships
it needs to when it is defined as a subclass from ActiveRecord, but then
you have to have two ways to define association related behavior.
Imagine if the classes were loaded in the below order:

ie:
 class Category < ActiveRecord::Base
    has_many :recipes
 end

 class Recipe < ActiveRecord::Base

 class Group < ActiveRecord::Base
    has_many :recipes
 end

By the time Group’s relationship is defined to Recipe, Recipe has
already been defined, so Group has to directly modify Recipe itself to
add the relationship information. Not a good idea IMO.

Now, you could do everything dynamically, so that the methods
“categories” and “recipe” are handled via method_missing, and then each
object looks up relationships (associations) via a
AssociationReflection.lookup, but that is not a very good solution IMO
either because you’d be having models relying on class methods of
another object to do their job. I think it is better to store this
information on the model itself, and let the model take care of it’s
relationships, as it does now with the “reflections” method.

Lastly, has_many/belongs_to are clear in their behavior. You can look at
Category and Recipe and know what relationship they have to other
models. If you remove one-side of this then you lose this. IMO that is a
big loss.

Zach

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFFZw7TMyx0fW1d8G0RAul8AJ41cfFH9OaF+uz/nKB7q3DfieedYQCeNTTY
SZ/fwbH6etGgssWT+dyiFo8=
=jTpT
-----END PGP SIGNATURE-----

Thanks Zach for the insight into the problem.

IMO, except for the reason mentioned in the last paragraph, all reasons
are due to internal issues of Rails, from which the programmer should
be insulated. I think it’s not good if you expect the programmer to
cover up for issues you have inside the framework.

But you have a good point in the last paragraph (ie. just by looking at
a model class, you will know all its relationships). That’s good enough
for me. :wink:

Best regards,
Thushan