33

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?

Ripon Al Wasim
  • 36,924
  • 42
  • 155
  • 176
Dane O'Connor
  • 75,180
  • 37
  • 119
  • 173

6 Answers6

46

Edit: three-years later; the times, they are a-changin'…

Julik's answer is the simplest and best way to tackle the problem these days:

class Foo
  attr_accessor :dead
  alias_method :dead?, :dead # will pick up the reader method
end

My answer to the original question follows, for posterity…


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 conceptually 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!


Edit, two years later, after a recent comment:

  1. Ruby enforces certain naming conventions. Symbols in Ruby can't have question marks. Thus invocations of :my_boolean_attribute? both will fail with a NameError. Edit: not correct, just use the quoted syntax for a symbol, e.g., :"my_attribute?"
  2. Symbols are immutable, attempting to assign to one will throw a SyntaxError.
Community
  • 1
  • 1
Nick Zadrozny
  • 7,906
  • 33
  • 38
  • 7
    Rubyist usually doesn't use the is_ prefix neither in variable names nor in method names. Just awesome? instead of is_awesome? – Simone Carletti Aug 11 '09 at 22:23
  • Good point, weppos. Edited out the 'is_' to avoid confusing posterity ;-) – Nick Zadrozny Aug 11 '09 at 22:25
  • From this answer I gather there isn't a real convention? :/ – Steven Jeuris Apr 27 '11 at 22:17
  • 2
    You can do `:"my_boolean_attribute?"`, or even `:"my boolean attribute?"` (good luck calling the latter without `send` though!) – Andrew Grimm Apr 28 '11 at 00:28
  • It's not just syntactic sugar, there's performance gains, as mentioned in http://stackoverflow.com/questions/5046831/why-use-rubys-attr-accessor-attr-reader-and-attr-writer/5806371#5806371 – Andrew Grimm Apr 28 '11 at 00:31
  • 1
    To be pedantic about your edit-answer, symbols can have question marks, however variable names (like the instance-var) cannot. See @Julik's answer for what I consider the best way to do this. – Excalibur Aug 13 '14 at 18:35
43

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
  • 7,676
  • 2
  • 34
  • 48
6

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
  • 45,269
  • 12
  • 110
  • 134
  • ty for the symbol catch. self.my_boolean_attribute would work correct? – Dane O'Connor Aug 11 '09 at 22:10
  • 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 Aug 12 '09 at 18:49
5

? 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
  • 363,768
  • 54
  • 674
  • 675
  • 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. – Dane O'Connor Aug 11 '09 at 22:08
3

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
  • 5,107
  • 2
  • 28
  • 29
  • +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! – Dane O'Connor Aug 11 '09 at 22:38
  • 1
    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 Aug 11 '09 at 23:00
0

I looked through the answers, and while the accepted answer is on-target, it introduces "extra" noise in the class. The way I'd suggest solving this issue is:

class Animal
  attr_writer :can_swim
  def initialize(animal_type_name)
    @can_swim = true
    @animal_type_name = animal_type_name
  end


  def can_swim?
    @can_swim
  end

  def to_s
    @animal_type_name
  end
end

dog = Animal.new('Dog in a bag')
dog.can_swim = false
puts "Can this #{dog} Swim? --- [#{dog_without_legs.can_swim? ? 'YEP!' : 'NOPE!'}]"
Vlad
  • 3,866
  • 1
  • 24
  • 20