Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Reading the Ruby Source to Understand Rails Idiosyncrasies (pivotallabs.com)
58 points by cmdrcoriander on July 20, 2013 | hide | past | favorite | 24 comments


Well, first of all, you aren't reading the Ruby source to understand Rails' idiosyncrasies, you're reading the Ruby source to understand Ruby's idiosyncrasies applied to Rails.

Secondly, this article is far more verbose than necessary when compared to the succinct 5-paragraph explanation provided by David Flanagan in "The Ruby Programming Language" p. 69-70 [1]

It's great that you took a chance to dive into the source of Ruby. Nonetheless, it's times like these when I wish people would understand that the best way to learn a programming language is by example AND by documentation (whether official docs or books). Those that ONLY learn by example fail to understand or unlock the true power of a language until it's too late.

[1] http://www.amazon.com/Ruby-Programming-Language-David-Flanag...


Well, I learned about the cover? Method, so that's a big plus for a typical Saturday...but I'm confused as to why include? for ranges doesn't just act as an alias for cover?

I can't think of a Range that is not meant to be continuous...I would test out some cases right now if my computer weren't grinding away at a database insertion process...

edit: OK, tried it out....as I suspected, Ranges that consists of Strings will have different behavior:

    [16] pry(main)> ('aa'..'zz').include?('c')
    => false
    [17] pry(main)> ('aa'..'zz').cover?('c')
    => true
    [18] pry(main)> ('aa'..'zz').include?('cc')
    => true
    [19] pry(main)> ('aa'..'zz').cover?('cc')
    => true
    [20] pry(main)> ('aa'..'zz').cover?('cccccc')


Ranges are always continuous, but `cover?` uses the comparison operators, while `include?` iterates over the items in the range in some cases. `include?` is practical in cases where you don't want to implement the comparison operators.

See https://news.ycombinator.com/item?id=6076608


I was going to write that this was a failing of the Ruby documentation. But actually, it's pretty well documented if you take the time to read it (http://ruby-doc.org/core-2.0/Range.html).

There's absolutely no need to go hitchhiking in the C source in this case.


While I agree that it's documented well enough that once you hit the situation and read the doc you should be able to figure out why without diving into the C, I think the documentation still fails in the sense that it doesn't give you any guidance as to why or when you would choose which one.

That said, I really don't consider this magic as another subthread suggests. include? just has an optimization for numeric ranges, but otherwise behaves exactly as Enumerable#include? should.


If there's only one thing I've learnt from trying different languages out: the less magic, the better.


This is an easy and thus superficial bit of insight to add. A more reliable truism would be that "unnecessary magic is bad", but that brings us no closer to evaluating Rails. Ultimately, you have to be able to point to specific magical behaviors and make a case for what makes them gratuitous.


Without turning this into a debate about particular bits of magic, my general conclusion from using Rails (especially when projects are in maintenance mode) is that it frequently makes it overly complicated to do things differently than how the framework wants you to, making you resort to monkey patching and other magical strategies of your own.

It's wonderful for prototyping but I'm becoming increasingly wary of it for production systems that are going to have a long life (at which point the whole perf/scaling thing comes into play too).

Which is all a very roundabout way of saying "use the right tool for the right job," I suppose.


> my general conclusion from using Rails is that it frequently makes it overly complicated to do things differently than how the framework wants you to

I can't agree with this at all.

Rails has default and reasonably sensible idioms for expressing common concepts. They work in most cases, but there are of course situations where you want to implement something in a way that results in an impedance mismatch with the framework.

In these cases, it's embarrassingly easy to compose different behaviours, or indeed to write entirely custom ones, in order to achieve the functionality you require. At the end of the day, it's Ruby with some helper libraries, and developers are free to use as much or as little of these as is required.

If there's a problem in this vein with Rails, it's that the magic makes it almost too tempting to twist apps into the "Rails way", even when it's not appropriate. Good developers will be aware of this, and use the correct approach.


I think this observations was much more true of Rails 2.x and before than Rails 3 forward.

People forget that "Rails 3" was actually Rails merging with Merb. A lot of hard-coded inflexibility of Rails 2 had to be extracted out, and the framework became much more malleable as a result.

When I hear people talk about being stuck to a certain path with Rails, I wonder what they are talking about. Then I think back to Rails 2 and think, "oh, right....".

Rails still encourages a certain path by convention, but it is much easier to deconstruct than before.


It's wonderful for prototyping but I'm becoming increasingly wary of it for production systems that are going to have a long life (at which point the whole perf/scaling thing comes into play too).

It's interesting because I've had exactly the opposite experience with Rails in production environments. I prefer to prototype with something simpler, but have found Rails pretty solid for delivering full websites with a complex back-end and tens of thousands of daily users (hardly web-scale, but not insignificant either - probably most sites fall in this bracket). Not that it's the only solution in that space, there are lots of other suitable frameworks, but I'd say it's not bad. It will use more resources than some other solutions, so it's definitely not the most efficient in machine time, but it makes up for that IMHO in being efficient to work with.

We're running some relatively busy sites with Rails and have seen no issues with maintenance or performance (due to caching), and I don't foresee any problems simply because for most web applications you can get acceptable performance up to very high scale by caching selectively. The support for russian-doll style caching and automatic expiration on db changes is pretty good, they're not bad about getting rid of cruft, and over several years I haven't run into any significant maintenance issues. My biggest worry with Rails is security, but that has improved, and is probably better than roll-your-own frameworks anyway.

Having come from Rails 1 and quite a few other web frameworks/CMS solutions, I don't really see Rails as having a lot of magic nowadays, though it does have lots of little shortcuts like scopes, and lots of helpers for everyday tasks like mailing, querying, dates, rendering templates etc. So I've found it a help rather than a hindrance and one of the few frameworks which stays out of your way enough to be worth the trade-off. In particular I like that the conventions mean that when you come back to a project after a few months the decisions on naming and placing functionality will all make sense, if you've followed those conventions.

It would be interesting to know which specific areas you've seen problems on, if only as a warning for others using Rails. I can imagine for example if you didn't want to accept the database naming conventions, or the file naming conventions it'd be a pain to work with and constantly tripping you up.


What is necessary magic?


Compilers, to start. What's unnecessary magic?


Serialization/deserialization logic that automatically will instantiate objects that require dynamic initialization, without making it clear to the programmers that this happened.

That's both unnecessary, and extremely pertinent in the case of Rails this year.


I agree, but would also point out that the pertinent cases you're referring to weren't a part of the mainstream Rails API.


I vastly prefer magic, so long as it works.


Magic is great, it's hugely entertaining and can be used for great effect. However, knowing how to deconstruct the magic is also very useful so you know how the sleight of hand has taken place.


Indeed - the KISS principle was once commonly cited in this industry.


Define magic.


Well, sufficiently advanced technology, wasn't it?


how about Ruby with less magic? http://rubyluwak.com/


I wonder if looking up the source is really necessary. I did some tests with Rails console just now. It seems that Ruby and Rails are handling the case quite logically.

"3.days.ago" at least by default is a TimeWithZone object in Rails.

  3.days.ago.class
   => ActiveSupport::TimeWithZone
And then the way Ruby iterates Range is through the "succ" method. So even this will give you a wall of errors:

  (3.days.ago..2.days.ago).to_a
Sending "succ" to a TimeWithZone object will print a warning because the method is deprecated. So basically a warning will show for every second in the range.

Calling .include? should be equivalent to .to_a.include? in effect.


One uses a loop and one doesn't? Not sure if there's so much to get out of this post.


`cover?` uses the comparison operators to check if the value is within the range. `include?`, on the other hand, will behave like `cover?` only if the range values are "numeric". If they aren't, it will iterate through the range to see if it contains the value. This is possible because range values are required to implement the `succ` method which gives the next value.




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

Search: