I’m thinking through the URL structure of a new app. I want the routes to have a very flat hierarchy, similar to Quora’s:
I find this intellectually interesting, I’ve heard in some cases it can be good for SEO, and I can think of a few potential objects in this new app that I’d like to share the top-level namespace like this.
It seems to me URLs are to resources as keys are to values. Maybe this is a job for a key-value store. Redis seems cool, so let’s try that.
Getting Redis on OS X:
brew install redis
Gemfile:
gem "redis"
config/initializers/redis.rb:
REDIS = Redis.connect(:url => ENV["REDISTOGO_URL"])
config/environments/development.rb
ENV["REDISTOGO_URL"] ||= 'redis://localhost:6379'
I’m setting this up so it will work with the “Redis To Go” Heroku add-on if I want to deploy it later.
heroku addons:add redistogo
Each time any object I want to share this namespace is saved, I’ll notify Redis. For example, users:
class User < ActiveRecord::Base
validates_presence_of :handle
validates_uniqueness_of :handle
before_save do
REDIS.set handle, "User"
end
end
I’m going to set their handle as the key and the value as the object type. If we also stored the ActiveRecord primary key, we could do the fastest-possible lookup later. That would change this data structure, though… any suggestions on the most Redis-y way to do it that way?
When a new request comes in, let’s have Redis do what it’s good at (quickly find the key), and if a record is found, mutate the route for my Rack endpoint (a Rails app in this case).
config.ru:
require ::File.expand_path('../config/environment', __FILE__)
use RedisRouter
run MyRails::Application
app/middleware/redis_router.rb:
class RedisRouter
def initialize(app)
@app = app
end
def call(env)
intended_resource = env["REQUEST_PATH"].gsub("/", "")
if type = REDIS.get(intended_resource)
new_route = "/#{type.underscore.pluralize}/#{intended_resource}"
env["REQUEST_PATH"] = env["PATH_INFO"] = env["REQUEST_URI"] = new_route
end
@app.call(env)
end
end
If a record isn’t found, Rack will pass through normally.
config/routes.rb:
MyRails::Application.routes.draw do
get "/users/:handle", :to => "users#show"
get "/places/:handle", :to => "places#show"
get "/lists/:handle", :to => "lists#show"
end
app/controllers/users_controller.rb:
class UsersController < ApplicationController
def show
@user = User.find_by_handle!(params[:handle])
end
end
This approach introduces a dependency on Redis and makes two requests to two separate databases. We could have had one SQL query that queried multiple tables. And, this is Ruby, land of dynamic typing… what’s with the explicit storing of type in a Redis database?
I like the idea of separating the concern of determining which type of object we want. That query should be super-fast and we can avoid messing with database indexes on our SQL database for this one case.We can separate this concern and if necessary, scale this component of the application independently.
Over the past few months of slinging Ruby here at Thoughtbot, I’ve picked up quite a few stupid ruby tricks smart ruby techniques that really help out your code. If you’ve got your own, feel free to leave a comment.
def touch_down
yield [3, 7]
puts "touchdown!"
end
touch_down do |(first_down, second_down)|
puts "#{first_down} yards on the run"
puts "#{second_down} yards passed"
end
=> "3 yards on the run"
=> "7 yards passed"
=> "touchdown!"
At first glance, this barely looks like valid Ruby. But somehow, it just makes sense: it splits up the array. If you’re going to pull out the values of the array inside of the block, why not just do it when you’re defining the block-level variables? This doesn’t seem to work nicely (in 1.8.7 at least) for Hashes, though.
>> args = [1, 2, 3]
>> first, *rest = args
>> first
=> 1
>> rest
=> [2, 3]
I knew about splitting up arrays before into individual arguments, but I didn’t know that you could easily get an array of the rest. Perhaps this is Lisp inspired?
>> items = { :apples => 2, :oranges => 3 }
=> items = {:apples=>2, :oranges=>3}
>> items.fetch(:apples)
=> 2
>> items.fetch(:bananas) { |key| "We don't carry #{key}!"}
=> We don't carry bananas!
This is just a nice little way to provide some default behavior that might be nicer than checking if the value exists in the hash first.
>> smash = Hash.new { |hash, key| hash[key] = "a #{key} just got SMASHED!" }
=> {}
>> smash[:plum] = "cannot smash."
=> {:plum=>"cannot smash."}
>> smash[:watermelon]
=> {:plum=>"cannot smash.", :watermelon=>"a watermelon just got SMASHED!"}
This is a really neat way to cache unknown values for Hashes (read: memoization!) I also heard it’s awesome for implementing a Fibonacci sequence.
>> cars = %w[beetle volt camry]
=> ["beetle", "volt", "camry"]
>> cars.sort_by { |car| car.size }
=> ["volt", "camry", "beetle"]
So, Array#sort_by sorts based on the return value of the block. It’s like a built in #map and #sort that rules even more with some Symbol#to_proc magic.
>> "brain".present?
=> true
>> "".present?
=> false
I’m sure most Rails developers know about blank? from ActiveSupport, but what about present?. Yeah, it blew my mind too. I like being as positive as possible in conditionals, so toss out those !something.blank? calls today and start using this.
At thoughtbot we’ve recently started to redo our outdated coding standards. We’ve taken a “consider everything” approach. Making the easy decisions by fiat and then opening up some of the decisions to the discussion of the entire group (with one person given ultimate responsibility for making the decision).
Its going well, but slowly, so far. When we did our original coding guidelines a few years ago, it was decided that we would use a different curly brace style than I was used to at the time. It took about two days to get used to the new format, but then after that, I couldn’t see them any other way.
My main point is that when it comes to guidelines, its important to be flexible. What may seem the “wrong” way to do something one day, can easily be the right way the next, if you commit to the change. And as programmers, what we may think are permanently ingrained ways of doing things, can almost certainly be changed.
Our new coding guidelines are pretty much entirely Ruby and Ruby on Rails focused. Anyone out there have any helpful hints or interesting stories about your Ruby and Rails coding guidelines?
I’ve always had a hard time separating work and home. The problem with being paid to think is that you don’t need your tools in front of you to keep working; you have your brain, you’re good to go. The problem comes in where you can’t stop thinking about a particular difficulty in your job.
I attended Mike’s 30th birthday party on Friday, but was completely unable to get a problem out of my head that had been there since the beginning of November or so (and before you ask, no I didn’t work it out Friday either). Unfortunately, the inability to stop thinking (which is one of my big personal faults), plus the fact that I didn’t know anyone else there, plus the fact that I’m a habitual pacer meant that for the first hour or so I was just walking around a room of people I don’t know, not talking to anyone. I must have looked like a complete loon!
And to think, this could have been avoided if Java would have allowed multiple inheritance. But that’s a post for another time.