简体   繁体   中英

Rails delegate method with more than one association

The delegate method seems like a great alternative to doing nil checks all over the place in rails apps, but I am having trouble applying it to situations where I have more than one association.

Consider these associations:

#app/models/user.rb
class User < ActiveRecord::Base
  belongs_to :blog
end

#app/models/blog.rb
class Blog < ActiveRecord::Base
  belongs_to :hash_tag
  has_one :user
end

#app/models/hash_tag.rb
class HashTag < ActiveRecord::Base
  has_one :blog
end

I grab a user:

@user = User.find(1)

And I want to find his blog:

@user.blog
  => nil

It returns nil here because this user happens to have no associated blog , so the following code would break the application if I did something like this for this user :

@user.blog.title
  => undefined method `title' for nil:NilClass

So I could do this:

@user.blog.title if @user.blog.present?

But this is a nil check, and we want to avoid nil checks because otherwise they will be absolutely everywhere in the app.

So you can do this which applies the law of demeter and works great:

# open up app/models/user.rb
class User < ActiveRecord::Base
  belongs_to :blog
  delegate :title, to: :blog, prefix: true, allow_nil: true #add this
end

And now we can do this, which is great because if the user does not have a blog then nil is just returned as opposed to that error: undefined method 'title' for nil:NilClass :

@user = User.find(1)
@user.blog_title

Great, that works and we avoided the nil check. But what if we want to grab the associated hash_tag.tag_name ? If nil was not an issue we could do this:

@user = User.find(1)
@user.blog_title.hash_tag.tag_name

Not only would this break the law of demeter, but because nil is an issue and the associated hash_tag object may not exist, we again will one day run into the error: undefined method 'title' for nil:NilClass .

I attempted to again open up the User class and add in a nested target for a delegate, but it was not working for me:

# again: open up app/models/user.rb
class User < ActiveRecord::Base
  belongs_to :blog
  delegate :title, to: :blog, prefix: true, allow_nil: true
  delegate :tag_name, to: :'blog.hash_tag', prefix: true, allow_nil: true #doesn't work
end

Apparently my syntax is off because it does not like my specifying this nested target as: :'blog.hash_tag' .

What I want to do is to be able to say: @user.blog_hash_tag_tag_name . Is this possible with delegate?

I did review the Delegate documentation . I did not see it mentioning more than one association as is my current situation.

As a personal opinion, @user.blog_hash_tag_tag_name is horrible to look at.

That said, wanting to define both delegates at the user level is also a violation of LoD because you are using knowledge of the inner workings of blog (the fact that it belongs to a hash_tag) from the user class.

If you want to use delegates, you should add to class User

delegate :hash_tag_tag_name, to: :blog, prefix: true, allow_nil: true

and to class Blog

delegate :tag_name, to: :hash_tag, prefix: true, allow_nil: true

我建议您使用Null Object Pattern ,它在https://github.com/avdi/naught gem(检查其文档,很糟糕!)及其ActiveRecord对应的https://github.com/Originate中很好地实现了/ active_null

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM