Hacker News new | past | comments | ask | show | jobs | submit login
Why Ruby rocks (skofo.github.io)
54 points by Skoofoo on July 10, 2013 | hide | past | favorite | 67 comments



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


And with C# 4 dynamics you can even pull off that method_missing routine.



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.


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

Ah, that's a good point. That seems to be a more accurate interpretation of what the author was trying to express.


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.

Note: I work on a large Ruby codebase...


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.


Awesome, will do.


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


Yes, that all may be true, but white space has semantic meaning in Python. I just can't get past that. ;-)


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.


Completely agree.


I don't know if it's the lack of types

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.

the abysmal performance of the default VM

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

the heavy reliance on monkey patching

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.


I would hardly say the performance of the default VM is a myth... just look at performance characteristics under multi-threading and GC pressure.


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.

[1] http://www.techempower.com/benchmarks/


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.


That's quite a weird angle. Surely I can express unhappiness about a product's performance without being asked to fix it or to shut up?


I think we're on the same side here.

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
It's unfortunately compromised by some boneheaded things in the MRI implementation. Method caching is a great example: https://charlie.bz/blog/things-that-clear-rubys-method-cache

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


> IMHO Ruby is just now coming fully into its own, and is, for a young language

Young? Ruby is 18 years old, it's certainly not a young language.


> Young? Ruby is 18 years old, it's certainly not a young language.

Well, its not young compared to Go, but its young compared to C or Lisp or Smalltalk.


> and it's not Rails.

Yes it is. ActiveSupport (part of Rails) monkey-patches the core library.


It does, but it doesn't heavily rely on this, as the poster was contending. It merely adds some helper methods.


Ruby is a pleasure to program in, however...

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.


Now every Ruby meetup I attend, 99% of the presentations are from web devs about rails and associated infrastructure.

That's pretty much why I lost interest in not just local groups but most online activity.

I use Ruby and JRuby all the time, but for Web dev I'll start with some simple Rack stuff and evolve to a Ramaze app if needed.

Listening to someone discuss installation and assorted config params for a gem that only works with a specific framework is not terribly interesting.

There are some bright spots online, Greg Brown's Practicing Ruby site being a solid example.

https://practicingruby.com


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


  def sanity
    @sanity
  end

  def sanity=(val)
    @sanity = val
  end
how are those six lines not boilerplate?


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.


They are, which is why Ruby's Module (class comes from module) has attr_accessor method.

http://ruby-doc.org/core-1.9.3/Module.html#method-i-attr_acc...

The above can be written as

  attr_accessor :sanity


I believe it'd be idiomatic to use

  attr_accessor :sanity

  def initialize
    @sanity = 0
  end
On that note I like Go's default values for primitive types. An int sanity would be zero by default.

Edit: seeing five simultaneous posts saying the same thing makes me feel trolled a bit :)


Nah, Rails devs are just really excited to finally have their chance to shine in the HN comments ;)


You might want to participate in https://bugs.ruby-lang.org/issues/8564


  attr_accessor :sanity
Would be a neater way to write this.


  attr_accessor :sanity


attr_accessor :sanity


Showing people boilerplate instead of attr_accessor isn't really a way to convince people that Ruby's great.


What I like about Ruby: Bundler, Gems, Enumerable, concise syntax, Ruby2, Rspec, RubyMine, Scripting, rbenv/rvm

What I would like to have: better support for concurrency, less memory consumption in production


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.


How is this even on the front page?


Ruby has many awesome features, like metaprogramming. But methods without parenthesis, method definitions with operators, etc.?

[edit] I am NOT a native English speaker. Is OP's blog supposed to be a joke and I don't get it?


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?


Setters are an anti-feature.

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.



So the takeaway here is that Ruby is great because it has core OOP concepts built into it? Color me unimpressed.


What year is this, 2005?


On front page? Really?


OK, I'll be more constructive.

>There are various interwoven reasons for this, but the biggest thing that makes Ruby shine in my eyes is its objects

Learn Smalltalk-80. Its been around a long time, and has a rich history.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: