I have HABTM models Client and Book. Client model has a bookshelf_color attribute to indicate whether a client has a full set of books, part of them or none of them. Once the books on a bookshelf change, a callback set_bookshelf_color is supposed to reflect the change.
The question is why do I have to prefix bookshelf_color assignment in a private callback below with "self." to make it to work (as it does not otherwise)?
class Client < ActiveRecord::Base
has_and_belongs_to_many :books, autosave: true, uniq: true,
after_add: :set_bookshelf_color, after_remove: :set_bookshelf_color
attr_accessible :id, :book_ids, :bookshelf_color
private
def set_bookshelf_color(book)
if Book.pluck(:abbr).map{|b| books.map(&:abbr).map(&:to_s).include?(b.to_s)}.all?
self.bookshelf_color = "green"
elsif Book.pluck(:abbr).map{|b| books.map(&:abbr).map(&:to_s).include?(b.to_s)}.any?
self.bookshelf_color = "yellow"
else
self.bookshelf_color = "red"
end
end
# /private
end
Remember, your model properties are just instance variables behind the scenes – all we’re doing when we interact with them is calling the getter and setter methods set up by
attr_accessor. When we writeinstance.bookshelf_color = "red"we’re sending the methodbookshelf_color=, with the argument"red", to the receiver,instance.Cool. What happens when we write just
bookshelf_color = "red"?In Ruby, bareword assignment is used to define local variables. Putting
name = "value"in a method definition will definenamein the local scope, rather than calling thename=method onself, even if that method exists.If I had specified an explicit receiver,
self.bar = val, Ruby would have known I wanted to sendselfthe methodbar=, which would have had the desired result.There’s some good further reading on the joys of
self, and why you should use it, over on Joe Yates’ Blog.