Ruby Bare Names
I’ve been slowly making my way through The Ruby Programming Language book and I came across something interesting.
With Ruby variables, you can scope variable names by adding a prefix symbol to the beginning of the name. For example:
type = 'Local Variable' TYPE = 'Constant' $type = 'Global Variable' @type = 'Instance Variable' @@type = 'Class Variable'
In Ruby1, local variables that don’t have a prefix symbol (i.e. local variables) have a bit of peculiar behavior that leads to some interesting patterns. If the Ruby interpreter comes across a variable name, it checks for 1) a value assigned to that variable name, and if that fails it checks for 2) a method with that name.
def ambiguous_number 2 end ambiguous_number = 1 puts ambiguous_number
If you copy and paste that bit of code into a Ruby file and execute it, the
output will be
1. However, this piece of code is different.
def ambiguous_number 2 end puts ambiguous_number
In most other programming languages (I think), you will get some variation of a
NameError to tell you that
ambiguous_number isn’t defined. In Ruby, the
program will output
This is, superficially, a nifty quirk of the language, but when you get into some deeper coding, there are some interesting patterns that use this quirk.
The first place I noticed this pattern was in Rails strong parameters. Before strong parameters were introduced in Rails 4, you could write your controller like this:
class UsersController < ActionController::Base def create @user = User.new(params[:user]) .... end end
create method creates a new user from the
:user parameters hash passed to
the controller from the form in the view.
The strong parameters way of writing this controller is:
class UsersController < ActionController::Base def create @user = User.new(user_params) .... end private def user_params params.require(:user).permit(:name) end end
If you look at this, you would think the
create action is making a new User from
the values in the local variable
user_params, but you would be wrong. Look
closely, and you will notice the
private method below called
The Ruby interpreter sees the call to user_params and first looks for a local
variable with that name, but when it doesn’t find one, it uses the return value
of the method user_params. Handy.
I’ve heard this feature of Ruby described as “Bare Names.” The real value of bare names, that I’ve found so far, is within the context of the Extract Method refactoring pattern.
In short, extracting a method would entail taking functionality out of a method and creating a new method that contains that functionality in order to reduce the amount of responsibility of any one method. An easy way to do this is to use bare names to extract fairly complicated logic using local variables into their own methods.
This example is taken from a Rails app I’ve been building as just a learning/example app from an intermediate Rails series of screencasts. Its basically just a twitter clone. The controller below is for a FollowingRelationship model, which controls the follower/followee relationship between users.
class FollowingRelationshipsController < ApplicationController def create user = User.find(params[:user_id]) current_user.followed_users << user redirect_to user end end
Not the most complex controller, but its ripe for refactor.
class FollowingRelationshipsController < ApplicationController def create current_user.follow user redirect_to user, notice: "Now following user." end private def user @_user ||= User.find(params[:user_id]) end end
We extracted out the core functionality of one line, mainly setting a local
variable from the parameters and put that in a method called
refactor uses the bare names functionality of Ruby, to extract a method out of a
(albeit simple) overly complicated controller action.2
This was a delightful discovery. I’ve seen the bare words pattern used before (especially in the context of strong params) but it was exciting to find a concrete explanation for the behavior.