As I write today’s journal, I am struck with how I’m not the ideal tour guide of the Rails source. Having never contributed to the code and coming at it with fresh eyes, I’m bound to get things wrong in my notes here. Hopefully when I do, you fabulous readers out there can provide some corrections in the comments section which I can fold back into the body of the post.
Ok, back to the source!
Deprecations
I know you have seen the output of this code before…you’re booting your app when all of the sudden you see something like:
1
DEPRECATION WARNING: ActiveSupport::BufferedLogger is deprecated! Use ActiveSupport::Logger instead.
This helpful (though generally frustrating) warning message is emitted by some pretty cool code, code that uses a technique we haven’t seen yet in this series. Let’s take a look.
The more time I spend with the Rails source, the more I appreciate it. I’ve always appreciated it for what it could do, but now I’m appreciating it directly, for what it is. I find it to be quite elegant, pragmatic, aesthetically pleasing, and educational. Inside the black box, it’s really quite nice.
So…thank you to all of the people that continue to make it better and to DHH and 37Signals for leading the charge. It’s a really great framework to use and study.
Hash#to_param
The first source fragment for today is a method responsible for URL generation of query strings from a Hash…something which I’m confident I’ve rolled my own at least once. It’s important to realize that many of the core objects have methods like to_param and to_query and that they all work in concert to handle the arbitrary structure we might throw at it, but we are just examining this one. Check out to_param.rb and to_query.rb for more type specific implementations.
(from core_ext/object/to_param.rb)
1234567891011121314151617
classHash# Returns a string representation of the receiver suitable for use as a URL# query string:## {name: 'David', nationality: 'Danish'}.to_param# # => "name=David&nationality=Danish"## The string pairs "key=value" that conform the query string# are sorted lexicographically in ascending order.## This method is also aliased as +to_query+.defto_param(namespace=nil)collectdo|key,value|value.to_query(namespace?"#{namespace}[#{key}]":key)end.sort!*'&'endend
This next post is going to be about one macro only…and it is a very special one if you like to obey the Law of Demeter. It has 130 lines of comments explaining it’s usage before it’s finally declared. It is, the one and only, Module#delegate.
You might wonder why this is on Module instead of Class. Well, Class inherits from Module, so it will get all the constants and methods declared there. Matt Aimonetti has a good write up on the distinction between the two, culminating in, “whenever you don’t create instances of a class, please don’t use a class.” Since delegate is really just method passing, it doesn’t need an instance so Module scope it is.
Module#delegate usage
By way of reminder, delegate is used like this (from the 130 lines of comments):
1234567891011121314151617
classGreeter<ActiveRecord::Basedefhello'hello'enddefgoodbye'goodbye'endendclassFoo<ActiveRecord::Basebelongs_to:greeterdelegate:hello,to::greeterendFoo.new.hello# => "hello"Foo.new.goodbye# => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
At RailsConf this year, Aaron Patterson talked a bit about “aggressively trimming negativity” from his life and focusing on the positive things. I’m working on that as well, which is part of the genesis of this Rails source journal. People like to complain about things, the more familiar the more often it sometimes seems. Before I started this project, I’d fill gaps in time with reading Hacker News, but the negativity really wore on me. Now, I instead get to go digging for really cool stuff in Rails that informs my every day work. I love it.
Recursive Hash Merging
For instance, last night I came across this cool bit of recursion in the deep merge extension to Hash:
Reading the source of Rails gives insight into the minds of the contributors over the years. It lets you know how they like to write code (of course), but it also gives you little glimpses into the personalities of particular people who shaped Rails in big and small ways. As a reader, you walk away from comment reading sessions knowing just a little bit more about the code, and the people behind it.
Reading code is fun
For instance, I learned that DHH thinks like a human:
(from array/prepend_and_append.rb)
123
classArray# The human way of thinking about adding stuff to the end of a list is with appendalias_method:append,:<<
This is something I really enjoy about the Ruby community: a clever sense of humor and a goal to be nice like Matz. If you’re looking for evidence of ‘nice’, you only need to take a cursory glance at the source comments…very generous in their detail, very helpful.
It seems like such a simple thing, but I’m really enjoying reading the source of Rails. I’m finding all sorts of hidden gems (ouch).
For instance, check out this interesting extension added to Array in “core_ext/array/extract_options.rb”
123456789101112131415161718
classArray# Extracts options from a set of arguments. Removes and returns the last# element in the array if it's a hash, otherwise returns a blank hash.## def options(*args)# args.extract_options!# end## options(1, 2) # => {}# options(1, 2, a: :b) # => {:a=>:b}defextract_options!iflast.is_a?(Hash)&&last.extractable_options?popelse{}endendend
I have decided to read the whole source of Ruby on Rails and make notes as I work my way through it. I have used the framework for far too long without doing such an obvious step and have just accepted the “magic” of how things work. Well, I’d like to see how the magic is made, and perhaps in so doing, I can become more useful as a Rubyist. These posts are going to read more like a journal, as that’s precisely what they are.
First step, setup an environment in which to really dig into Rails, in this case, I’m going to use the Rails-Dev-Box which is recommended for contributors.
12345
λ: Development $> git clone https://github.com/rails/rails-dev-box.git
# stuff happensλ: Development $>cd rails-dev-box
λ: rails-dev-box - master $> vagrant up
# vagrant and puppet stuff happens
When it comes to javascript frameworks, there’s really no substitute for reading the source. So in this series of posts, I’m going to dig into the source for Batman.js, and hopefully come away with a better understanding of how it all works so that I can make better use of it.
Not long after building something cool in Batman, you’ll show it to a friend and they will invariably ask about the weird /#!/ in the URL (it could just be the people I hang out with).
The hash-bang is quite easy to get rid of, but you need to be a little bit careful, as there are some things that can trip you up.