views:

899

answers:

5

Learning ruby. I'm under the impression that boolean attributes should be named as follows:

my_boolean_attribute?

However, I get syntax errors when attempting to do the following:

class MyClass
  attr_accessor :my_boolean_attribute?

  def initialize
    :my_boolean_attribute? = false
  end
end

Apparently ruby is hating the "?". Is this the convention? What am I doing wrong?

+2  A: 

? is convention for methodnames, not variables. You can't use an instance variable named @foo?, however you could use a variable named @foo and name the (manually created) getter method foo? if you wanted to.

sepp2k
hmm, so the attr_accessor helper won't strip the '?' off the end for me huh. Glad to hear its actually not the convention for instance vars too. Thought I was going crazy.
TheDeeno
+3  A: 

The attr_accessor symbol implies that the variable name is @my_boolean_attribute, so that's what you should be setting (not the symbol).

Also, you can't use ? for variables, just method names.

Platinum Azure
ty for the symbol catch. self.my_boolean_attribute would work correct?
TheDeeno
Yeah, it should.
Platinum Azure
Oh, let me add-- only within instance methods. In class scope it will look for a class-scope variable (@@my_boolean_attribute). Not that I needed to tell you, of course, but I wanted to make sure what I said was technically correct.
Platinum Azure
+6  A: 

The short version:

You can't use a question mark in the name of an instance variable.

The longer version:

Take, for example, attr_accessor :foo — it's simply a bit of syntactic sugar for the following:

def foo
  @foo
end

def foo=(newfoo)
  @foo = newfoo
end

Furthermore, the question-mark suffix is mostly just a convention to indicate that the return value of a method is a boolean.

The best approximation I can make of what you're going for here…

class MyClass

  def initialize
    @awesome = true
  end

  def awesome?
    @awesome
  end

end

In this case, there may be a case to be made for using attr_accessor — after all, it may be explicit that you're working directly with a boolean attribute. Generally, I save the question-mark suffix for when I am implementing a method whose boolean return value is based on slightly more complex conditions than just the value of an attribute.

Cheers!

Nick Zadrozny
Rubyist usually doesn't use the is_ prefix neither in variable names nor in method names. Just awesome? instead of is_awesome?
Simone Carletti
Good point, weppos. Edited out the 'is_' to avoid confusing posterity ;-)
Nick Zadrozny
+2  A: 

Monkey-patching metaprogramming - maybe it can be made more elegant, this is only a quick draft, and I haven't done metaprogramming for a little while...

 # inject the convenience method into the definition of the Object class
 class Object
   def Object::bool_attr(attrname)
     class_eval { define_method(attrname.to_s,
          lambda { instance_variable_get('@' + attrname.to_s.chop) }) }
     class_eval { define_method(attrname.to_s.chop+"=",
          lambda { |x| instance_variable_set('@'+attrname.to_s.chop, x) }) }
   end
 end

 ### somewhere later

 class MyClass

   bool_attr :my_boolean_attribute?

   def initialize
     @my_boolean_attribute = true
   end
 end

 # yet even more later

 foo = MyClass.new
 bar = MyClass.new

 foo.my_boolean_attribute = 1
 puts foo.my_boolean_attribute?
 puts bar.my_boolean_attribute?

With this approach, you can be DRY and get the nice questionmark too. You just might need to pick a better name than "*bool_attr*", like, "*bool_attr_accessor*" or something similar.

The definitions that I made are a bit cranky, in a sense that the question mark is present in the original symbol. Probably a cleaner approach would be to avoid the questionmark in the symbol name and append it during the definition of the method - should be less confusing.

Oh, and almost forgot to include the obligatory link: Seeing metaclasses clearly

Andrew Y
+1 for the link! Probably won't add the convenience method in this case since its not the convention. I don't want to confuse others. The ideas will be happily cannibalized by other problems though!
TheDeeno
If you are maintaining it in a team, I guess this kind of questions should be discussed indepth at reviews - in some scenarios it might make sense, in some scenarios it won't. I did not code Ruby in a team, so I tend to go for an approach that gives me the least LOC to stare at.
Andrew Y
+1. If only for the meta-cleverness...
Mike Woodhouse
+4  A: 

The easiest way to quickly add a "question method" is to use aliasing for your reader method

class Foo
  attr_accessor :dead
  alias_method :dead?, :dead # will pick up the reader method
end
Julik
awesome! I was doing it long hand.
TheDeeno