From: "Avdi B. Grimm" Date: 2001-09-26T09:00:19+09:00 Subject: [ruby-talk:21701] strange behaviors Hello, In the process of a frighteningly productive spate of Ruby coding today, I ran across some seeming quirks in the language. I'm curious whether these are bugs, features, known eccentricities, or what, so I thought I'd post them and see if anyone else is familiar with the behaviors I encountered. Quirk #1: Inconsistent name resolution in instance methods Ruby looks up unqualified names it finds in method bodies in the method list of 'self' - usually. For example: class Foo def bar @bar end def bar=(val) @bar = val end def implicit_fetch bar # Ruby interprets this as Foo#bar, defined above. end end f = Foo.new f.bar = "test 1 2 3" puts f.implicit_fetch This yields the expected response: test 1 2 3 Ruby correctly interprets the bare reference to 'bar' as a call to Foo.bar. However, this behavior doesn't hold true for 'setter' methods. If I expand the class above thus: class Foo def bar @bar end def bar=(val) @bar = val end def implicit_assign bar = "implicitly assigned" # doesn't call bar=() end def explicit_assign self.bar = "explicitly assigned" end def implicit_fetch bar end end and then I exercise it: f = Foo.new f.implicit_assign # fails puts f.implicit_fetch f.explicit_assign # works puts f.implicit_fetch I get the following output: nil explicitly assigned As far as I can tell, ruby is interpreting the line bar = "implicitly assigned" to mean "assign the string on the right to a local variable named 'bar'". Unlike for the 'getter' method, Ruby doesn't interpret "bar =" to mean "call the method of self named bar=()". This despite the fact that calling "f.bar = some_string" /outside/ the class would result in a call to bar=(). This was startling inconsistency at first; but as you can see it's easily gotten around by explicitly referencing "self.bar =" inside a method that wants to call bar=(). However, that's not the end of the story. While slightly more cumbersome, the workaround above works fine - for public or protected attributes. But what about private 'setter' methods? Private methods *can't* be explicitly referenced via "self.method_name"; that's what makes them private. So if I modify the above class to privatize bar=(), I find that there's no way at all (that I know of) to access it: class Foo def bar @bar end def bar=(val) @bar = val end private :bar= def implicit_assign bar = "implicitly assigned" end def explicit_assign self.bar = "explicitly assigned" end def implicit_fetch bar end end f = Foo.new f.implicit_assign puts f.implicit_fetch f.explicit_assign puts f.implicit_fetch the above code yields the error: -:19:in `explicit_assign': private method `bar=' called for # (NameError) from -:31 nil So, as far as I can tell, private setter methods are completely inaccessible. This seems sub-optimal, but maybe I'm missing something. Quirk #2: Constants aren't dynamically resolved Apparently, each class gets it's own private copy of constants defined within it, unlike class variables which are shared between parent and child classes. This useful feature is demonstrated with the following code: class Parent CONSTANT = "foo" class Child < Parent CONSTANT = "bar" end puts Parent::CONSTANT # prints "foo" puts Child::CONSTANT # prints "bar" So far, so good. However, when I assume that Ruby will dynamically figure out which constant to use based on the class of self, I run into trouble. E.g.: class Parent CONSTANT = "foo" def Parent.implicit CONSTANT # always resolves to Parent::CONSTANT end def Parent.explicit self::CONSTANT # resolves dynamically end end class Child < Parent CONSTANT = "bar" end class Parent CONSTANT = "foo" def Parent.implicit CONSTANT end def Parent.explicit self::CONSTANT end end class Child < Parent CONSTANT = "bar" end puts Child.implicit # prints "foo" puts Child.explicit # prints "bar" The implicit internal reference to CONSTANT always yields the value of CONSTANT for the class in which the current method was defined. To get the value of CONSTANT for the current class, I must explicitly ask for it with self::CONSTANT. Again, this is just a minor annoyance; but it seems to go against the Principle of Least Surprise. Anyone have any comments on either of these two quirks? - Avdi Grimm