Zack Hubert

ActiveSupport: Self Deprecation and Deprecations

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.

ActiveSupport: Thanks, Try, Tap

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

ActiveSupport: Delegate for Demeter

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>

ActiveSupport: Hash Extensions

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

ActiveSupport: Humor, Defaults, Security

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.

ActiveSupport: Array Extensions

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.