Typing Less in Irb with method_missing
First off, Happy New Year! Now back to our regularly scheduled program.
Being the console-efficient prick I am, let’s look at a method_missing
that let’s us type less where underscored methods are involved. This means that instead of typing some_really_long_ass_method
we can type the shortcut method s_r_l_a_s
. Instant aliasing! As you can see this works by typing the beginning letter of each word separated by underscores.
So why do this if we have autocompletion? For starters, try typing the Object
method instance_variable_get
in five keystrokes (tabs included). Or for more inefficiency, try typing ActiveRecord
class methods that start with before_*
and validate_*
.
Explanation
To write this method_missing
, we’re going to need a method that finds methods given a shortcut method we type:
def underscore_search(input, list)
if input.include?("_")
underscore_regex = input.split('_').map {|e| Regexp.escape(e) }.join("([^_]+)?_")
list.select {|e| e.to_s =~ /^#{underscore_regex}/ }
else
escaped_input = Regexp.escape(input)
list.select {|e| e.to_s =~ /^#{escaped_input}/ }
end
end
If you try this out in irb, it works as follows:
# Doing the same as autocompletion would >> underscore_search 'si', %w{set_trace_func singleton_methods select}% => ['singleton_methods'] # Doing something autocompletion can't do >> underscore_search 'i_v_g', %w{instance_of? instance_variables instance_variable_get instance_eval}% => ['instance_variable_get'] # If our shortcut method is too broad we get multiple results >> underscore_search 'i_v', %w{instance_of? instance_variables instance_variable_get instance_eval}% => ['instance_variables', 'instance_variable_get'] # Multiple letters can be typed per underscore >> underscore_search 're_o_ag', %w{reflect_on_aggregation reflect_on_association raise_on_association} => ['reflect_on_aggregation']
Now that we have a way of querying underscore methods, let’s wrap a method_missing around it:
def method_missing(meth, *args, &block)
possible_methods = self.methods.map {|e| e.to_s }.sort
meths = underscore_search(meth.to_s, possible_methods)
if meths.size > 1
puts "Multiple methods match: #{meths.join(', ')}"
elsif (meths.size == 1)
send(meths[0], *args, &block)
else
super
end
end
After pasting method_missing()
and underscore_search()
in irb:
# Calls private_methods() >> pri_m => [...] # Method queries that match multiple methods don't get executed >> pr_m Multiple methods match: private_methods, protected_methods => nil # method_missing works as normal for method queries that don't match anything >> blah NameError: undefined local variable or method `blah' for main:Object from /Users/bozo/.boson/commands/public/underscore_alias.rb:57:in `method_missing' from (irb):8
Finished Snippet
To make this snippet easy to apply to any object or class of objects, I wrapped method_missing
into a module and did a little more meta programming. The result is this
module.
If you use boson to manage your ruby snippets: boson install
https://github.com/cldwalker/irbfiles/raw/master/boson/commands/public/underscore_alias.rb
.
Here’s how I use the finished snippet in a Rails console session:
bash> script/console # Load UnderscoreAlias >> load 'path/to/underscore_alias.rb' # If using boson >> load_library 'underscore_alias' # Let's give an ActiveRecord model class (i.e. Url) underscore aliasing abilities >> Url.extend UnderscoreAlias => Url # Call Url.original_table_name >> Url.o_t => 'urls' # Call Url.columns_hash >> Url.col_h => {"name"=> #< ActiveRecord::ConnectionAdapters::MysqlColumn:0x23766d8 @type=:string, @precision=nil ...>, "updated_at"=> ...} # Now let's give ActiveRecord objects underscore aliasing abilities >> Url.send :include, UnderscoreAlias => Url # Call Url#to_json >> Url.first.t_j => "{\"url\":{\"name\":\"http://github.com\", ... }" # Call Url#clear_association_cache >> Url.first.c_as => [...] # Call Url#has_attribute? >> Url.first.h_a 'name' => true
When using the above snippet, you may find it annoying that sometimes a shortcut method results in a multiple choices that vary only by ending i.e. updated_at? updated_at updated_at=
. If only there was a way to autocomplete like this … Actually, bond accomplished this a while ago.
Now a general question to you all: Would you find it useful to apply this same instant aliasing technique to your rake/thor commandline tasks? For example, typing r:u:s instead of rails:update:scripts? I have found this instant aliasing technique to be quite handy with boson’s commands, arguments and options.