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.