Zack Hubert

Rails Source: ActiveSupport Part 6

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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Hash
  # 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+.
  def to_param(namespace = nil)
    collect do |key, value|
      value.to_query(namespace ? "#{namespace}[#{key}]" : key)
    end.sort! * '&'
  end
end

Rails Source: ActiveSupport Part 5

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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Greeter < ActiveRecord::Base
  def hello
    'hello'
  end

  def goodbye
    'goodbye'
  end
end

class Foo < ActiveRecord::Base
  belongs_to :greeter
  delegate :hello, to: :greeter
end

Foo.new.hello   # => "hello"
Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>

Rails Source: ActiveSupport Part 4

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:

(from core_ext/hash/deep_merge.rb)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Hash
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
  #
  #   h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
  #   h2 = { x: { y: [7,8,9] }, z: 'xyz' }
  #
  #   h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"}
  #   h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
  def deep_merge!(other_hash, &block)
    other_hash.each_pair do |k,v|
      tv = self[k]
      if tv.is_a?(Hash) && v.is_a?(Hash)
        self[k] = tv.deep_merge(v, &block)
      else
        self[k] = block && tv ? block.call(k, tv, v) : v
      end
    end
    self
  end
end

Rails Source: ActiveSupport Part 3

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)

1
2
3
class Array
  # The human way of thinking about adding stuff to the end of a list is with append
  alias_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.

Rails Source: ActiveSupport Part 2

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”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Array
  # 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}
  def extract_options!
    if last.is_a?(Hash) && last.extractable_options?
      pop
    else
      {}
    end
  end
end

Reading the Source of Ruby on Rails

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.

1
2
3
4
5
λ: 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

The Bat Cave, Part 1: Objects

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.

Batman.js PushState

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.

Watches Benchmark: A Ruby Gem for Avoiding Performance Regressions

I have a pretty normal workflow…I work on something and then move on to something else. While I always strive to minimize coupling, it’s inevitable that sometimes those new things I work on effect the old. Having good test coverage running under continuous integration catch the situations where I’ve literally broken functionality, but what about the times when I’ve simply made that old thing sluggish?

While there are all sorts of heavyweight tools for performance analysis, they mostly rely on me looking at them (a fatal flaw when what I want to look at is the new code I’m writing). Of course, I watch production performance like a hawk, but I’d love to avoid the mad scramble to patch production due to an unforeseen performance regression. What would be great would be an exception driven notification that radiates the information to me, rather than me having to dig through graphs to find it…in development when I’m making the mistakes.

So, I made a super simple gem for the development environment that helps me keep my performance promises. When I’m building new functionality where performance is a sensitive issue, I simply assign that method an SLA and the simple gem watches_benchmark will tell me via Logger and Growl when I’ve made a change that has broken the SLA. Then I have causality to the performance regression and can fix it right away.

It’s pretty rough cut and an imperfect solution (development environments have a tendency for variability), but I find that it has been helping me on my current project.