I'm sure it does, but I can write the same class in C# just as easily:
class Person
{
public int Sanity { get; set; }
public Person()
{
Sanity = 50;
}
}
And to call it:
var programmer = new Person();
programmer.Sanity += 1000000;
Console.WriteLine(programmer.Sanity);
I don't see much difference between Person.new and new Person();.
What are the real ways in which it will knock my socks off?
I'm honestly asking. Even though I program C# at work, I'm very interested in the advantages of other languages.
Getters and setters that have no logic are pointless. In Go I would write:
type Person struct {
Sanity int
}
func NewPerson() *Person {
return &Person{Sanity: 50}
}
This is good enough 90% of the time (and faster), but if I was writing a library package and I wanted to care about protecting with some logic and care about my export signature:
type Person interface {
Sanity()
SetSanity(val int)
}
type person struct {
sanity int
}
func NewPerson() Person {
return &person{sanity: 50}
}
func (p *person) Sanity() {
return p.sanity
}
func (p *person) SetSanity(val int) {
if val > SOME_FOO1 && val < SOME_FOO2 {
p.sanity = val
}
}
Ruby really is a great language. The author just posted a poor example that is frankly NOT idiomatic Ruby. The proper way to create a getter and setter method on an instance variable is like so:
class Person
attr_accessor :sanity
def initialize
@sanity = 50
end
end
All the attr_* class methods do is define precisely the instance methods the author wrote by hand. Indeed, if attr_reader, attr_writer, and attr_accessor weren't part of Ruby's Module class you could write them yourself, like so: https://gist.github.com/jfarmer/6b4deeb8bcfbe030f876
If using an "eval" method seems smelly to you, you can achieve the same result in pure Ruby using define_method, instance_exec, and instance_variable_get. There are good practical reasons to use module_eval, though.
Regardless, I think the author's point was more that there's nothing "special" about getters and setters in Ruby. They're just plain ol' methods. As a class of methods we write them often enough that we've also defined a higher-order method that takes an instance variable name as input and dynamically defines those getters and setters on the underlying object.
We wouldn't "lose" anything by not having attr_reader and friends, though. Our code would just be slightly more verbose.
Not needing getters/setters makes Ruby rock? Lots of languages allow this, but Ruby's allowing you to elide parentheses means you can't assume that a property is a property or a function. In other languages, the property versus accessor distinction is more limited and less confusing.
method_missing, which is a bit of a hack and is at least a double edged sword, makes Ruby rock? Sure, it's handy magic, but it's a pretty dangerous form of magic.
I'm not sure I could think of less significant attributes for a language.
you can't assume that a property is a property or a function. In other languages, the property versus accessor distinction is more limited and less confusing.
Ruby does not have "properties." All data is private and you interact with objects using messages.
As a user of an object's interface all you need to know is how something behaves when sent various messages. Whether "foo = 12" is really setting an instance variable named "@foo" or something else entirely should be irrelevant. It's an implementation detail.
I realize this sounds possibly condescending and pedantic, but I've seen too many Ruby tutorials that try to present the language in terms of behavior found in other languages where the result is a broken mental model of how Ruby actually works. Hence, confusion.
Unfortunately the conflation of methods that happen to get or set instance variables with the notion of properties (AKA "attributes") has become ingrained.
method_missing, which is a bit of a hack and is at least a double edged sword, makes Ruby rock? Sure, it's handy magic, but it's a pretty dangerous form of magic.
Thinking in terms of messages sent to a receiver, "method_missing" is far less magical. It's more something you'd expect in a message-handling system.
Well, as you say, you work on a large Ruby codebase, so I apologize if the following comes off as condescending. I don't follow this first bit, though.
You say, "Ruby's allowing you to elide parentheses means you can't assume that a property is a property or a function."
There's no such "thing" as a property in Ruby, yeah? Everything+ is an object and objects respond to methods. IMO this is one of the places where Ruby is not being magical. attr_reader and friends are built on top of this to give programmer's a convenient shorthand way of defining boilerplate getters and setters when you need them.
In this regard Ruby is more homoiconic. "getters" and "setters" are just plain ol' methods!
It'd be much more confusing, IMO, if there were now two types of "things" that could be attached to an object: methods and properties/attributes. You'd have to develop a separate convention, syntactic or otherwise, for information hiding, e.g., how would private properties work? You're now in a funny situation where one kind of variable (instance variables) might differ from other kinds of variables in a dimension other than scope.
Python's answer is "neither properties nor methods are ever really private" which is internally consistent but very un-Ruby.
+: I know this isn't literally true. Methods aren't objects, for example, and there are plenty of keywords like case, when, if, etc. which are neither methods nor objects but units of pure syntax.
I personally love the fact that it's all method-based. IME at least it shouldn't matter whether something is a property or a method. All I care about is the state change or return value or exception thrown.
To add to this: Both of these are things we used to do extensively in PHP, years ago. Not only does that pretty much kill the 'unique' angle the article wants to take, it wasn't a good idea or readable practice back then.
I don't doubt that there really are great reasons to use Ruby; hopefully, when the author figures out what they are, he'll revisit this and contrast the real benefits with this unfortunate cruft.
It is my impression that Python does exactly the things listed here, but more explicitly. With the python approach, instance variables are public by default (as attributes). There is never any confusion between when you are calling a method, or referencing an attribute. If you are doing something something more complicated than getting or setting an attribute, you must either call a method (this is so you and other developers actually know what is going on), or alternatively you use a property decorator.
You could do something similar to Ruby's missing_method, by setting the class's __getattr__, but you'd really have to ask yourself why. What kind of creation are you building that you are expecting to not know which methods are available while running your program?
These are the kinds of things that drive me batty with Ruby. Perhaps if I spent about double or triple the hours on Ruby that I have, I might have an "aha" moment. However, from what I've seen I can't help but feel that what it's really trying to accomplish is much more comfortable in functional form (say Lisp?).
Try listening to Sandi Metz on the Ruby Rogues podcast. It's a longish episode but she'll give you an appreciation for message-passing OO and how it makes an application design more pliable.
In a pinch the (free?) "Barewords" episode of Ruby Tapas will get you close in five minutes.
"What kind of creation are you building that you are expecting to not know which methods are available while running your program?"
You normally wouldn't use method_missing in this way. As an example, for better or worse, look at RSpec. I can write code like this:
require 'rspec'
class Person
def initialize
@happy = true
end
def happy?
@happy
end
end
describe Person do
describe '.new' do
it 'is happy by default' do
expect(Person.new).to be_happy
end
end
end
RSpec doesn't know about the Person#happy? method, obviously, so how the heck is there a be_happy method? It's using method_missing to intercept the call to the otherwise-undefined be_happy method, pull out the "happy" part, translate it to "happy?", and then construct the appropriate expectation by delegating the "happy?" message to the underlying Person object and analyzing the response.
It also enables RSpec to generate much more useful testing messages. Instead of saying "Expected person.happy? to be true, but got false" it can say "Expected person to be happy, but happy? returned false."
This intercept-and-delegate or intercept-and-translate mechanism is probably the most common use of method_missing. It makes writing DSLs much easier. See, e.g., Nokogiri::XML::Builder for generating XML documents (http://nokogiri.org/Nokogiri/XML/Builder.html).
What kind of creation are you building that you are expecting to not know which methods are available while running your program?
Well, metaprogramming is all about exactly that kind of thing. Python is a fine language, but it's not cut from the same cloth as Ruby. That's not a value judgement; plenty of sane, creative, and intelligent people use Python. They're simply not the same kind of language.
You can do this sort of metaprogramming in Python just fine, and then some. Things like metaclasses and abstract base classes give a lot of power to hook and modify the type system.
Something I feel like I frequently read in these discussions is that Ruby is nice "because it looks almost like English". When I think of people speaking English, I think of people equating quantity with quality, exchanging clarity for context. This is why I vastly prefer Python even though they are really not that dissimilar.
> What kind of creation are you building that you are expecting
> to not know which methods are available while running your program?
`method_missing` is more useful for dynamically responding to method calls than actually not knowing how to respond. A good example of this is rails' (version 3.2 and lower) dynamic finders:
User.find_by_username('a_username')
`find_by_username` is never defined anywhere, it is dynamically responded to using method missing. this allows code to be much more precise and DRY (though at the expense of readability if you don't know what's going on)
I started programming in Ruby in early 2000 and I really liked it back then, but when I look at it today, the language feels very old.
I don't know if it's the lack of types, the fact that you see streams of "end end end" like Pascal used to do, the abysmal performance of the default VM or just the heavy reliance on monkey patching, but Ruby is not aging very well.
In my opinion all the dynamically typed languages were just a stopgap on the way to statically typed languages with more flexible type systems. These days I'll easily take a statically typed language over a dynamically typed language for anything but quick prototypes or knock-off scripts.
Ruby is a dynamic language, so no, there are no types as such. There are ways to create types, but if you're doing that a lot, you're probably doing Ruby wrong. It's not meant to have types. Different way of thinking.
Monkey patching is generally considered an anti-pattern. I'm not sure what in particular has "heavy reliance" on it, but it's not Ruby's libraries themselves, and it's not Rails. (Metaprogramming is not the same thing as monkey patching.)
Ruby is not aging very well.
IMHO Ruby is just now coming fully into its own, and is, for a young language, just reaching maturity.
Speaking of Ruby performance, we haven't received a whole lot of pull requests improving the performance of the Ruby implementations on our benchmarks project [1]. If you feel Ruby isn't getting a fair shake on performance measurements, we'd love to see some pull request love.
I asked about this in another thread that you responded to, and appreciated your answer. (Which had a bunch of links and a strong implication of "well, since you ASKED, here's what you need to go do something about it..." :) )
Given how little code there is in the Ruby/Rails tests there, it doesn't look like a lot of opportunity exists for speeding up the benchmarks by changing that code... but maybe it could be sped up by changing the gems. There are several JSON gems, at least one of which claims to be faster by 2-3x.
In the PHP universe there's Phalcon, a C plugin to handle a lot of the work that otherwise would be done in raw PHP. It has good performance versus the other PHP frameworks (not up to the raw PHP numbers). Why hasn't this become the norm?
I'm not asking bhauer, folks: why not replace chunks of the code that are slow but have been pretty stable (Rack comes to mind, but other chunks of Rails are probably good contenders) with C plugins/gems? We already commonly use Nginx or another server for static content, why not push that further? Hell, how much similar code could be put to use in other frameworks if the core was done in C/C++ or Java? The focus on the singular language as the only way to do any given framework seems to waste an amazing amount of code, and performance.
We've measured the performance of the fundamentals of web applications on two Ruby implementations (MRI, JRuby) and both perform poorly, as compared to the rest of the field. area51org says that Ruby's reputation of slowness is undeserved. I feel our data confirms the reputation.
In fact, I don't think there's much room for improvement by tweaking our tests, but I figured I'd make the solicitation anyway to keep an open mind about it.
I'm not asking you to fix it or shut up, especially since I feel your point of view is corroborated by the data. I am asking, however, for those who say Ruby's reputation is undeserved to consider providing some evidence for that point of view. And one conceivable way to do that would be to improve our Ruby tests.
That's a myth, a pervasive one. I'll point you to one
source that debunks this, but there are many.
http://www.unlimitednovelty.com/2012/06/ruby-is-faster-than-python-php-and-perl.html
(Rails startup does a lot of the mentioned things, for example, but then so do a lot of apps' controller actions unless they're careful. The OpenStruct example is particular egregious.)
1. The drama that surrounds Rails makes a lot of people just throw their hands up and quit.
2. There are very few Ruby shops that aren't just Rails shops.
I agree. I tried web development professionally; loved Ruby, thought Rails was pretty decent, but hated web development (and ultimately wasn't at all inspired by the product we were building).
Now every Ruby meetup I attend, 99% of the presentations are from web devs about rails and associated infrastructure.
> 2. There are very few Ruby shops that aren't just Rails shops.
If you think about all the shops out there using Puppet and/or Chef, there is still a lot of non-Rails Ruby out there (in a roundabout way, I'll admit).
Perhaps I have not delved into the right parts of puppet (e.g. I have only scratched the surface of custom types) but I have spent a LOT of time using it for general configuration management and I can say that it doesn't even bear a passing resemblance to ruby. You really can't make the argument that using Puppet makes you a ruby shop.
They are. (And generated behind the scenes by "attr_accessor :sanity", if you'd prefer the short version.)
The benefit everything-is-a-message is that you can swap out the implementation of sanity (or sanity=) if needed, eg.
Before:
attr_accessor :radius, :diameter
After:
# Refactored radius to be the new source of truth.
def diameter
radius * 2
end
def diameter=(val)
self.radius = val / 2
end
In PHP (for example), MyClass->diameter and MyClass->diameter() are completely different things. Any existing code (including code you don't control) needs updating to change references from the former to the latter when moving logic into something that was previously static.
This article is flawed by holding up those six lines in themselves as something great, but I think the point the author is trying to get across is valid.
I suppose the author was trying to be explicit for the sake of a reader unfamiliar with Ruby. The typical way to avoid that boilerplate is by using 'attr_accessor :sanity', which defines those getter and setter methods (and it is possible to implement attr_accessor or similar directly in Ruby, should you need more boilerplate-reducing helpers).
This is exactly it. I would commonly use the shorthand for something like this, but I felt that it would only deter from the point I was illustrating, which is that a couple methods can seamlessly act like a variable.
I'm not sure this really sells what's best about Ruby. Python and many other languages offer very similar tools, and the example given is nowhere near idiomatic Ruby anyway - it should be using attr_accessor as others have pointed out. I can't see that this is markedly different from many other languages which now have shortcuts for accessors.
method_missing is interesting because it has been criticised in projects like Rails, precisely because it leaves you with an API which isn't documented explicitly in the code. As Rails matures they've moved away from dynamic finders, and more towards defined methods which take options, so instead of
Post.find_all_by_xxx or Post.find_by_xxx
People often use:
Post.where(xxx)
I've never been tempted to use it explicitly myself, in quite a few years of using Ruby, it just feels too hacky somehow and too akin to monkey-patching, another early Ruby practice which Rails has moved away from.
In contrast I do love the automatic accessors (not as shown in this post) in Ruby and the philosophy that everything should be as simple and predictable as possible, just having to edit header files is so jarring when going back to something like Obj-C. Ruby feels comfortable to me mostly because of the attitude of the language and the standard library which is pretty surprise free and covers most of the bases, but it's not markedly superior or different IMHO from other languages like Python, and choosing between them is really a matter of taste.
I admit I'm being myopic here but that's exactly why I ask. In what scenario is a genuine message passing/arbitrary methods useful?
I tend to look through everything in an OOP/type safe way, so the idea of having an unlimited, highly dynamic vocabulary for method calling is very... Foreign to me.
When does this pattern outshine static interfaces?
One spot when it's very useful is in making objects that are proxies for external resources (file data, executable utlities, etc.). Particularly when those resources can be inspected, it can be much better from a DRY perspective to let the interface of your proxy object automatically adapt even as the underlying resource changes.
Seems like a pretty weak set of arguments - I'm not for or against Ruby but is that really a good "five paragraph" description of why I should learn it and love it?
They give the illusion that they can be set in any order, but most meaningful classes have invariants that are ambiguous to preserve with single-value setters. Object Oriented designs jump through some serious hoops to preserve the pretty `object.property = value` syntax. Besides, there are considerable advantages to immutability even in the absence of concurrency. `new_object = frob(object, value)` is generally preferable in the long run.
Every time I start learning Python, after a few hours I remember "I already know Ruby", and that's the end of that.
I already know it, so it rocks. Python is fun but I already have Ruby. It's just a language and there's nothing I can really do in Python that I can't do it Ruby, and vice-versa (at my level, anyway).
I guess I'm just lucky I have the option of using either instead of say, ______.
While I do appreciate the OP's enthusiasm, there is a lot more to love about Ruby than its simplicity. Spend some time learning about metaprogramming, and you may really be impressed.
What are the real ways in which it will knock my socks off? I'm honestly asking. Even though I program C# at work, I'm very interested in the advantages of other languages.