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?

6 Answers 6

47

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.
Sign up to request clarification or add additional context in comments.

6 Comments

Rubyist usually doesn't use the is_ prefix neither in variable names nor in method names. Just awesome? instead of is_awesome?
Good point, weppos. Edited out the 'is_' to avoid confusing posterity ;-)
From this answer I gather there isn't a real convention? :/
You can do :"my_boolean_attribute?", or even :"my boolean attribute?" (good luck calling the latter without send though!)
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.
|
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 

3 Comments

awesome! I was doing it long hand.
By using this you can use both dead and dead?, right? But only allows setting through dead?
Setting stays through "dead="
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.

2 Comments

ty for the symbol catch. self.my_boolean_attribute would work correct?
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.
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.

1 Comment

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.
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

2 Comments

+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!
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.
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!'}]"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.