Category Archives: ruby

A Java Developer’s Switch to Rails

As I read this account of transitioning from Java EE to Ruby on Rails,”Java Web Apps to Rails, A Developer’s Experience,” I thought the following:

Regarding the lack of a good Ruby/Rails IDE:

When it was time to look at the code, I was a bit disappointed.  TextMate looked like a popular choice but it fell short in many ways.  I struggled to navigate the code, link the methods across files and follow the code. Grep was my only savior.

I am surprised he did not find RubyMine. JetBrains makes awesome IDEs (IntelliJ IDEA for Java, too). I have to admit at being very impressed that the underlying model of the ruby code within the RubyMine IDE is amazingly capable of giving you the same sort of features that it can for a “compiled,” or more type-centric language like Java.

Ruby

Next he mentioned about learning the language…

My approach to learning Ruby was to start looking at code and figuring out pieces of syntax that did not make sense.  That turned out to be a bad idea. Constructs like “do |x| x+1” threw me off. … I found it very important to understand the motivation behind creating the new language.

There are some good books out there, and other resources…

Another comment rang true with me:

I found it very important to understand the motivation behind creating the new language.

More about the motivation behind Ruby… or at least, how I interpret things.

Ruby treats you like an adult. It doesn’t baby you, or hold your hand, or make you type all sorts of (obfuscating, in my opinion) fluff that a compiled language needs. Your code is more compact and obvious and “dense with meaning,” leading to less need for things like method return types and method parameter data types. Or semicolons.

However, I miss parameters list, curly braces, return statements, camel case naming conventions…

Be patient… soon you will grow weary of having to read all that extra stuff when you go back to Java. If you write well-formed classes, and very small, compact, and obvious methods, you don’t need the extra frills. You will run across the concept of “duck typing” which really means that classic C++/Java/C# style interfaces are not needed. If you create a method that expects a passed-in object to respond to a method call (e.g., “torque”), any object of any class will do — as long as it has that method call! And if you pass in an object that does not have that method, Rails will kindly throw an exception.

The concept of ‘open classes’ that allow developers to go in and change a base class sounds scary.

Go back to my opening statement. “Ruby treats you like an adult.” If you are naive, you can blow your code up and break stuff. If you do everything the Ruby way, you’ll be fine — because you will catch it with tests (right?). There are good rules of thumb for how and why and when you should consider adding to an existing class — or worse, monkey-patching. In Ruby you can actually alter the behavior of an existing method of an existing class. Do it poorly, and you will pay the price. And maybe not for a year or two. Mwahahahaha.

Ruby’s metaprogramming is incredibly powerful (even if the syntax takes a bit of getting used to).

Rails

Rails is incredibly opinionated about how the MVC code should look. It is basically a model-driven architecture approach. The framework expects the MVC stack to have a model, a view, and a controller for each domain concept. (As a long-time domain modeler, and UML guy, this is a real treat.) You can use the default rails generators (interesting when first starting, or for banging out a quick prototype), or write your own twist on a generator. Instead of focusing on the boring framework bits with repetitive, template-like code for each domain object, you can focus on quickly creating features that people want to use and pay for 🙂

Supporting Cast

Ruby on Rails has a great ecosystem of wonderfully talented folks providing 1000s of gems to do all sorts of useful things. For example:

The ecosystem of supporting tools is incredible. Some of my favorites:

These all make it so easy to “do the right thing” and write code with BDD/TDD to provide quality from the start.

Personally, my switch from J2EE to Rails (not to mention SQL to NoSQL/MongoDB) has been one of pure pleasure. The kinds of rich functionality and the ability to build stuff faster is imprerssive. I only wish I were better at writing kick-ass Ruby, Rails, CSS, Javascript, MongoDB…

Is it perfect? Of course not. There are other cool things to look at as well… Meteor, Node.js, MEAN, etc. So much to explore, so little time.

Back to my continuous learning!

 


The Cost of Using Ruby’s Rescue as Logic

[notice]
If you use this sort of technique, you may want to read on.

node = nodes.first rescue return

[/notice]

 

[important]

Nov 2012 Update:

Though this post was about the performance cost of using a ‘rescue’ statement, there is a more insidious problem with the overall impact of such syntax. The pros and cons of using a rescue are well laid out in Avdi’s free RubyTapas: Inline Rescue

[/important]

Code like this:

unless nodes.nil?
  nodes.first
else
  return
end

Can be written using the seemingly more elegant approach with this ruby trick:

node = nodes.first rescue return

But then, that got me to thinking… In many languages I have used in the past (e.g., Java and C++), Exception handling is an expensive endeavor.

So, though the rescue solution works, I am thinking I should explore whether there are any pros/cons to allowing a “rescue” to act as logic. So I did just that…

Here are the two methods I benchmarked, one with “if” logic, and one with “rescue” logic:

def without_rescue(nodes)
  return nil if nodes.nil?
  node = nodes.first
end
def with_rescue(nodes)
  node = nodes.first rescue return
end

Using method_1, below, I got the following results looping 1 million times:

                  user     system      total        real
W/out rescue  0.520000   0.010000   0.530000 (  0.551359)
With rescue  22.490000   0.940000  23.430000 ( 26.487543)

Yikes. Obviously, rescue is an expensive choice by comparison!

But, if we look at just one or maybe 10 times, the difference is imperceptible.

Conclusion #1 (Normal Usage)

  • It doesn’t matter which method you chose to use if the logic is invoked infrequently.

Looking a bit Deeper

But being a curious engineer at heart, there’s more… The above results are based on worst-case, assuming nodes is always nil. If nodes is never nil, then the rescue block is never invoked. Yielding this (rather obvious) timing where the rescue technique (with less code) is faster:

                  user     system      total        real
W/out rescue  0.590000   0.000000   0.590000 (  0.601803)
With rescue   0.460000   0.000000   0.460000 (  0.461810)

However, what if nodes were only nil some percentage of the time? What does the shape of the performance curve look like? Linear? Exponential? Geometric progression? Well, it turns out that the response (see method_2, below) is linear (R2= 0.99668):

Rescue Logic is Expensive

Rescue Logic is Expensive

Conclusion #2 (Large Data Set):

In this example use of over a million tests, the decision on whether you should use “rescue” as logic boils down to this:

  • If the condition is truly rare (like a real exception), then you can use rescue.
  • If the condition is going to occur 5% or more, then do not use rescue technique!

In general, it would seem that there is considerable cost to using rescue as pseudo logic over large data sets. Caveat emptor!

Sample Code:

My benchmarking code looked like this:

require 'benchmark'

include Benchmark

def without_rescue(nodes)
  return nil if nodes.nil?
  node = nodes.first
end

def with_rescue(nodes)
  node = nodes.first rescue return
end

TEST_COUNT = 1000000

def method_1
  [nil, [1,2,3]].each do |nodes|
    puts "nodes = #{nodes.inspect}"
    GC.start
    bm(12) do |test|
      test.report("W/out rescue") do
        TEST_COUNT.times do |n|
          without_rescue(nodes)
        end
      end
      test.report("With rescue") do
        TEST_COUNT.times do |n|
          with_rescue(nodes)
        end
      end
    end
  end
end

def method_2
  GC.start
  bm(18) do |test|
    nil_nodes = nil
    real_nodes = nodes = [1,2,3]
    likely_pct = 0
    10.times do |p|
      likely_pct += 10
      test.report("#{likely_pct}% W/out rescue") do
        TEST_COUNT.times do |n|
          nodes = rand(100) > likely_pct ? real_nodes : nil_nodes
          without_rescue(nodes)
        end
      end
      test.report("#{likely_pct}% With rescue") do
        TEST_COUNT.times do |n|
          nodes = rand(100) > likely_pct ? real_nodes : nil_nodes
          with_rescue(nodes)
        end
      end
    end
  end
end

method_1
method_2

Sample Output

                  user     system      total        real
W/out rescue  0.520000   0.010000   0.530000 (  0.551359)
With rescue  22.490000   0.940000  23.430000 ( 26.487543)
nodes = [1, 2, 3]
                  user     system      total        real
W/out rescue  0.590000   0.000000   0.590000 (  0.601803)
With rescue   0.460000   0.000000   0.460000 (  0.461810)
                        user     system      total        real
10% W/out rescue    1.020000   0.000000   1.020000 (  1.087103)
10% With rescue     3.320000   0.120000   3.440000 (  3.825074)
20% W/out rescue    1.020000   0.000000   1.020000 (  1.036359)
20% With rescue     5.550000   0.200000   5.750000 (  6.158173)
30% W/out rescue    1.020000   0.010000   1.030000 (  1.105184)
30% With rescue     7.800000   0.300000   8.100000 (  8.827783)
40% W/out rescue    1.030000   0.010000   1.040000 (  1.090960)
40% With rescue    10.020000   0.400000  10.420000 ( 11.028588)
50% W/out rescue    1.020000   0.000000   1.020000 (  1.138765)
50% With rescue    12.210000   0.510000  12.720000 ( 14.080979)
60% W/out rescue    1.020000   0.000000   1.020000 (  1.051054)
60% With rescue    14.260000   0.590000  14.850000 ( 15.838733)
70% W/out rescue    1.020000   0.000000   1.020000 (  1.066648)
70% With rescue    16.510000   0.690000  17.200000 ( 18.229777)
80% W/out rescue    0.990000   0.010000   1.000000 (  1.099977)
80% With rescue    18.830000   0.800000  19.630000 ( 21.634664)
90% W/out rescue    0.980000   0.000000   0.980000 (  1.325569)
90% With rescue    21.150000   0.910000  22.060000 ( 25.112102)
100% W/out rescue   0.950000   0.000000   0.950000 (  0.963324)
100% With rescue   22.830000   0.940000  23.770000 ( 25.327054)

MongoMapper Query Overview

There was a question on the MongoMapper Google Group from a Mongoid user about how MongoMapper handles associations. Brandon was surprised that this query returned an Array:

Product.first.releases.where(something)

Let’s break it down, one bit at a time and clear things up:

# This would be an instance of Product
Product.first # Class.

This simply gets the first element in the Array that is returned by the default “All” query on Product. Of course, without sorting, you probably would not want to do this.

# This would be a return value of an array, assuming Product <>----> * Release
Product.first.releases # Array.

In Brandon’s example, I assume “releases” is a many association. That means, an Array. Unless the association has been tweaked to have default sorting via an Association Extension, getting the “first” one might be adventurous.

# This doesn't change the above... merely adds a restrictive query clause
Product.first.releases.where(something) # Array.

Here we simply get the first element of the releases array, narrowed down by the “something” query.

Capisce?

I am not sure why, but for me it seems more logical to start my clauses with the where, and narrow them down further, or modify them… In MongoMapper, I find querying rigor is much more “loose” than say a SQL SELECT query that requires things in proper order… I would tend to write my queries in more or less this fashion:

ModelClass.where(some criteria).[sort | order | another where clause | fields | limit].[all | first | paginate]

In addition, it is important to note that MongoMapper returns a query and does not actually perform the query until you add something that needs the results. For example: all, first, paginate, sort, etc.

I can picture one of those “man page” or SQL style of fancy ways to show you how you can construct a mongomapper query given all the combinations of options for each “position” in the query…

My (unsolicited) advice is to make the query look as “natural” as possible in terms of how you might read it aloud.

Product.releases.where(:major.gt => 1).sort(:minor.desc).first # Get the latest 1.x release

(And, if the releases where clause query is common, you can create an Association Extension)

Use the Console

You can always just output the queries to the console:

>> Patient.where(:last_name=>/john/i).class
=> Plucky::Query
>> Patient.where(:last_name=>/john/i).all.class
=> Array
>> Patient.where(:last_name=>/john/i).all.count
=> 1
>> Patient.where(:last_name=>/john/i).first.class
=> Patient
>> Patient.sort(:created_at.desc).first.class
=> Patient

Association Extension

And to show an example of an extension (when you use it frequently, for example):

class Encounter
  include MongoMapper::Document
  ...
  # Associations :::::::::::::::::::::::::::::::::::::::::::::::::::::
  many :events, :limit => 30, :order => 'msg_timestamp desc' do
    ...
    def images
      where(:type => [EventConstants::EventType.to_text(EventConstants::EventType::IMAGE)]).order(:created_at.desc).all
    end

    def charts
      where(:type => [EventConstants::EventType.to_text(EventConstants::EventType::ED_SUMMARY)],
            :file_version.in => ["P", "F"]).order(:created_at.desc).all
    end

    def admits
      all(:type => [EventConstants::EventType.to_text(EventConstants::EventType::ADMIT)])
    end
  end
  ...
end

# For a given encounter
enc=Encounter.find('4dadad188951a20727000160')
>> enc.events.images.count
=> 7
>> enc.events.images.class
=> Array
>> enc.events.images.first
=> #

Named Scope

If you will need dynamic querying, you could use a Named Scope as follows:

scope :by_days_old,  lambda { |age| where(:msg_timestamp.gt => age.days.ago) }

This can be used as follows:

Encounter.by_days_old(10)
=> #Fri Apr 15 03:35:53 UTC 2011}>

Looking at Gem Code is Easy with GemEdit

Ever want to pop into the source code of some gem that you are using?

Gem Edit is a nice and easy way to do this (if you aren’t using RubyMine, for example):

[sudo] gem install gemedit

In the terminal, simply type:

[develop*]$  jonsmac2-2:source jon$  gem edit -e mate mongo_mapper

And you will see:

Found gem for 'mongo_mapper' with version >= 0
Opening the following gems with mate:
  mongo_mapper-0.8.6 /Library/Ruby/Gems/1.8/gems/mongo_mapper-0.8.6
Running `mate "/Library/Ruby/Gems/1.8/gems/mongo_mapper-0.8.6"`

and Voila, Mongo Mapper appears in TextMate:

Mongo Mapper

Mongo Mapper Source Code

In an IDE, you can often get to the source code for a single method, but not as “completely” as with gemedit and TextMate, IMHO.

 

Mongo Mapper from IDE

Mongo Mapper from IDE

 

@martinstreicher gave me a good tip when using bundler (which I use on my Rails 3 apps):

export EDITOR=mate;bundle open <gem>

Factory Girl and MongoMapper

You were probably hoping for some Rosey the Riveter poster…

Factory Folder

Factory Folder

Instead, I am going to extend my small MongoMapper example to include Factory Girl. The steps are pretty simple:

  1. Go here to install…
  2. Create your factories
  3. Use the factories in Cucumber/RSpec

Factory Construction

I created a new “factories” folder under the spec folder:

The factories for User and Event are quite simple:

Factory.define :user do |u|
  u.name ('a'..'z').to_a.shuffle[0..7].join.capitalize
end

and

require 'factory_girl'
def dummy_word(len=6)
  ('a'..'z').to_a.shuffle[0..len].join.capitalize
end

def dummy_date
  secs_in_day = 24*60*60
  Time.now + (rand(60)*secs_in_day - 30)
end

Factory.define :event do |e|
  e.title "#{dummy_word} #{dummy_word 3} #{dummy_word 10}"
  e.date  dummy_date
end

Refactor Original Setup

Instead of using this style of test data creation:

@event = Event.create(:title => "Code Retreat Timbuktoo", :user => @fred)

We will use the new factory as follows:

@event = Factory(:event, :title => "Code Retreat Timbuktoo", :user => @fred)

Refactor Cucumber

The given went from this:

Given /^A set of events$/ do
  fred = User.find_or_create_by_name("fred")
  (1..10).each do
    Event.create(:title=>"#{dummy_word} #{dummy_word 3} #{dummy_word 10}",
                 :date => dummy_date,
                 :user => fred)
  end
  harry = User.find_or_create_by_name("harry")
  (1..10).each do
    Event.create(:title=>"#{dummy_word} #{dummy_word 3} #{dummy_word 10}",
                 :date => dummy_date,
                 :user => harry)
  end
  Event.count.should == 20
end

to this – including refactoring out dummy_title, and reducing it to one loop:

Given /^A set of events$/ do
  fred = User.find_or_create_by_name("fred")
  harry = User.find_or_create_by_name("harry")
  (1..10).each do
    evt = Factory(:event, :title => dummy_title,
                          :date  => dummy_date,
                          :user  => fred)
    evt = Factory(:event, :title => dummy_title,
                          :date  => dummy_date,
                          :user  => harry)
  end
  Event.count.should == 20
end

Subtle Details

The beauty of having tests is that I could easily mess around with getting some of the Factory Girl configuration stuff in the right place. Try something, run the test, adjust as needed until all are back to green.

The file features/support/env.rb got some additions so that Cucumber could find the factories:

$LOAD_PATH << File.expand_path('../../../app/model' , __FILE__)
require 'user'
require 'event'
require 'spec/factories/events.rb'
require 'spec/factories/users.rb'
load 'config/mongo_db.rb'

All the tests still pass!

More Complicated Example

For a project I work on, my factories look like this, with auto-creation of random IDs:

def random_months(months)
  day_in_secs = (24*60*60)
  (1+rand(months))*30*day_in_secs
end

# ----------- GROUP -----------
Factory.sequence :group_num do |n|
  "99#{n}#{rand(n)}"
end

Factory.define :group do |g|
    g.group_num {Factory.next(:group_num)}
    g.name "Greatest Group"
end
# ----------- ACCOUNT -----------
Factory.sequence :doctor_num do |n|
  "999992#{n}#{rand(200+n)}"
end

Factory.sequence :login do |n|
  "AB#{rand(n*68)}bx#{rand(200+n)}"
end

Factory.sequence :msid do |n|
  "CQ987Z12#{n}#{rand(n)}"
end

Factory.define :account do |a|
  pw = 'password'
  a.msid { Factory.next(:msid) }
  a.doctor_num { Factory.next(:doctor_num) }
  a.first_name "James"
  a.last_name "Jones"
  a.role 'user'
  a.password pw
  a.password_confirmation pw
  a.email Setting.get("AutoEmail")
  a.login { Factory.next(:login) }
end

# ----------- PATIENT -----------
Factory.sequence :patient_num do |n|
  "#{n}#{rand(300+n)}"
end

Factory.define :patient do |pt|
#  pt.patient_num "10000009"
  pt.patient_num {Factory.next(:patient_num)}
  pt.emr_num "1853286"
  pt.first_name "John"
  pt.last_name "Johnson"
  pt.dob {(Time.now - random_months(36))}
  pt.count_public_encounters 1
  pt.count_public_events 2
end

 

Pow — A Great Server for your Mac Bat Cave

Somehow I stumbled across this great little tool  — oh yeah, John Nunemaker mentioned “powder,” which was “Syntactic sugar for http://pow.cx/

Huh? Syntactic what? For “pow” what? John hasn’t steered me wrong yet, so I decided I needed to look at what Pow was. In 37Signals own words:

Pow: Zero-configuration Rack Server for Mac OS X

In other words:

You can run a bunch of your rails apps all at once, with ease.

This is worth the effort to try and get running to see if it will help ease how you work with multiple Rails apps.

Since my experience was a bit off from the awesome-looking Screencast teaser, I figured I would share it in case others might benefit from the troubleshooting.

Once I solved the Pow-not-working-like-magic-from-the-start problems, it worked right out of the box with a Rails3 App. Not so much with my Rails2 apps. So, you can either try to do it the Rails3 way from the start (see below for instructions), or barge ahead and see how far you get (knowing now what I do for Rails2, barging is probably a pretty safe bet).

Yes, I am repeating what is in the main website… but then aggregating common troubleshooting experiences ad fixes below, to save you (and my colleagues) time.

Install Pow:

$ curl get.pow.cx | sh

Be certain you see “*** Installed” or it is not! (See Troubleshooting section below if need be.)

jonsmac2-2:.pow jon$ curl get.pow.cx | sh
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100  2722  100  2722    0     0  17322      0 --:--:-- --:--:-- --:--:-- 34455
*** Installing Pow 0.2.2...
*** Installing local configuration files...
*** Starting the Pow server...
*** Installed

If you get upset, just uninstall:

$ curl get.pow.cx/uninstall.sh | sh
Symbolic Link to Rails3 App

Symbolic Link to Rails3 App

Configure:

$ cd ~/.pow
$ ln -s /path/to/myapp

To the right is an example. Name the link if you do not want to use the default folder name. For example:

ln -s ~/railsprojects/first_app jkfirstapp

This allows you to then enter the following into the browser, using the symbolic link name (or the default folder name):

http://jkfirstapp.dev

If that didn’t work, continue onward, your journey will be short (I hope).

Troubleshooting

I was disappointed when the magic didn’t happen right away. Boy does the Ruby community spoil us! So a hunting I will go…

Create a Rails3 App

If you are only doing Rails2, see below for the config.ru fix. Otherwise, creating a vanilla Rails3 app is a good way to test that Pow is properly configured. Here is a simple Rails3 app (from Michael Hartl’s Ruby on Rails3 Tutorial):

mkdir ~/railsprojects
rails new first_app

edit the Gemfile:

gem 'rails', '3.0.1'
gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'

Then execute:

bundle install
rails server

It should be running with the default server page.

I got Nothing!

If pow did not install, or you get seemingly NOTHING when going to the browser webpage, look no further than here. The following worked for me (and others). Seems that the permissions are not quite right sometimes:

before:

-rw-rw-r--   1 jon  staff   518B Apr 18 13:42 cx.pow.powd.plist

So I ran this:

jonsmac2-2:.pow jon$ launchctl load -Fw ~/Library/LaunchAgents/cx.pow.powd.plist
jonsmac2-2:.pow jon$ chmod 644 ~/Library/LaunchAgents/cx.pow.powd.plist
jonsmac2-2:.pow jon$ curl get.pow.cx | sh

And, after the re-install, the perms ended up as you might expect:

-rw-r--r--   1 jon  staff   518B Apr 18 13:42 cx.pow.powd.plist

Checking It’s Running

This is admittedly odd output for a “working” install, but it gives you at least one data point to compare what you get versus this. Do the following:

cd ~/Library/Application Support/Pow/Current/bin
jonsmac2-2:bin jon$ ./pow

On my Mac it results in:

node.js:134
    throw e; // process.nextTick error, or 'error' event on first tick
    ^
Error: EADDRINUSE, Address already in use
at HttpServer._doListen (net.js:1089:5)
at net.js:1060:14
at Object.lookup (dns.js:159:5)
at HttpServer.listen (net.js:1054:20)
at Array.<anonymous> (/Users/jon/Library/Application Support/Pow/Versions/0.2.2/lib/daemon.js:34:27)
at EventEmitter._tickCallback (node.js:126:26)

Domain Isn’t Setup

If you see this:

No such app

No such application can be found!

Then you know you have screwed up the symbolic link.

Try again, and be careful and exact.

Trouble Accessing Dev domains

Some folks needed this simple fix:

touch /etc/resolver/dev

Rails2: Cannot GET /? –> config.ru is Missing!

That’s right, you see: Cannot GET / in the browser. Bet you never saw that before!

This means that you are missing the Rackup config.ru file in your application’s root. Try adding this file (you can see it in the folder screenshot above):

# This file is used by Rack-based servers to start the application.
require "./config/environment"
use Rails::Rack::LogTailer
use Rails::Rack::Static
run ActionController::Dispatcher.new

Pow Can’t Start Your App

This is most likely, genuinely, truly your error… Sorry to say!

The error below is because the config.ru was completely wrong (I was trying the Rails3 project file (duh — it invokes a class in a module named after the app itself)):

Pow can’t start your application.

/Users/jon/railsprojects/track_my_league/tml raised an exception during boot.
NameError: uninitialized constant FirstApp
/Users/jon/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:443:in `load_missing_constant'
/Users/jon/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:80:in `const_missing'
/Users/jon/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:92:in `const_missing'
/Users/jon/railsprojects/track_my_league/tml/config.ru:4
/Users/jon/Library/Application Support/Pow/Versions/0.2.2/node_modules/nack/lib/nack/builder.rb:4:in `instance_eval'
/Users/jon/Library/Application Support/Pow/Versions/0.2.2/node_modules/nack/lib/nack/builder.rb:4:in `initialize'
/Users/jon/railsprojects/track_my_league/tml/config.ru:1:in `new'
/Users/jon/railsprojects/track_my_league/tml/config.ru:1

Here the error is that I do not have the gem environment set up properly with RVM (using .rvmrc). Easily diagnosed because I can’t even start the app manually!

SystemExit: exit
./config/boot.rb:66:in `exit'
./config/boot.rb:66:in `load_rails_gem'
./config/boot.rb:54:in `load_initializer'
./config/boot.rb:38:in `run'
./config/boot.rb:11:in `boot!'
./config/boot.rb:110
./config/environment.rb:7:in `require'
./config/environment.rb:7
/Users/jon/railsprojects/tml/TML/config.ru:3:in `require'
/Users/jon/railsprojects/tml/TML/config.ru:3
/Users/jon/Library/Application Support/Pow/Versions/0.2.2/node_modules/nack/lib/nack/builder.rb:4:in `instance_eval'
/Users/jon/Library/Application Support/Pow/Versions/0.2.2/node_modules/nack/lib/nack/builder.rb:4:in `initialize'
/Users/jon/railsprojects/tml/TML/config.ru:1:in `new'
/Users/jon/railsprojects/tml/TML/config.ru:1

Hope this helps!

RVM Install Troubleshooting

All I wanted to do was sit down with Mike Hartl’s “Ruby on Rails 3 Tutorial” yesterday. Hours later, with nothing to show other than a zillion open terminals, I punted and watched the Flyers beat the Sabres despite playing like girls (I know, unfair to girls!). Then I went to bed.

So I must have had a dorked up installation of RVM or something, because when I tried to install Ruby 1.9.2 and Rails 3 and upgrade RVM I ran into <ahem> “issues.” I was getting weird errors like:

error installing rails i18n requires rubygems version 1.3.5
gem_runner.rb:85:in `<top (required)>': undefined method `load_plugins'

After exploring all sorts of similar woes that other people had and not really getting that magic googullet (Google Bullet), I decided to go all nuclear (apologies to Japanese readers). I was able to run:

rvm implode

Which made “rvm -v” fail to find rvm — a good thing. It meant I had uninstalled RVM. (Or so I thought!)

I also deleted the RVM stuff from the bash_profile file(s). Just to attempt to eliminate voodoo dolls.

But I still ran into stupid errors (stupid voodoo dolls!):

error installing RVM fatal: Not a git repository (or any of the parent directories): .git

After much pain and suffering (well, only in a wimpy software relative sense), my guess is that I installed rvm as sudo at one point or installed the gem, or somehow had things “cross-eyed.” I don’t know…

I guess I should be more diligent, because in general you get spoiled by Ruby and gems and Rails and the general euphoria of how easy things are. Until they aren’t.

Today, I ended up fixing my system by looking for “rvm” on my system, and cleaning up leftovers. The crumbs from my sloppy eating at the trough of Ruby, I suppose.

I saw some directories holding rvm docs. I whacked them.

I saw some directories with rvm source, archives… Gone.

I uninstalled the rvm gem. (Oops. Who put that there?)

Finally, I retried the install:

jonsmac$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

And added this to ~/.bash_profile And I restarted terminal because I always forget how to do that source ~/.bash_profile thing…

jonsmac2-2:~ jon$ rvm -v
rvm 1.6.2 by Wayne E. Seguin (wayneeseguin@gmail.com) [https://rvm.beginrescueend.com/]

Shazam! No errors. Thanks Wayne!

Now I can get past page 15 in Mike’s tutorial book!

Ruby Metaprogramming in the Small

I have been thoroughly enjoying working with Ruby this past year (thanks Lee!). However, only recently have I been getting brave/comfortable/wise enough to try out some metaprogramming. Okay, so maybe I am a little slow… The scourge of making deadlines and releasing software meant I sometimes just had to give up trying to get to an elegant solution that I thought was possible, but was unable to make work in the time allotted. I try to be pragmatic, if nothing else.

But here is a little example of how easy it is to exploit Ruby’s omniscient metaprogramming system.

Background

The project is a Rails app using MongoMapper. I needed to enhance the way we create User Accounts to accommodate importing a user from a CSV file (dumped from the hospital’s account management system).

First up: “Insert New Record” — which went pretty smoothly. Next, I wanted to permit merging import data with an existing account. (For example, changing the last name when married status changes.)

Round 1: Get it to work

Using a TDD approach and RSpec to flesh out the low-level class behavior, I “snuck up on the answer,” one small test at a time. I tend to use a “get it to work with brute force” approach at the outset. Leading to sometimes bulky code as can be seen below. So, one-by-one, I kept adding to the merge code for each new attribute that would allow updating.

NOTE: I added some (all?) of the code comments below for this blog post. Plus I took liberties to not show everything…

  class Account
    include MongoMapper::Document
    # Rest of class omitted
    def self.create_or_merge(fields)
      raise ArgumentError if fields.nil?

      # Some code omitted

      # There are 2-3 legal ways to identify a unique account :-(
      account = Account.find_by_identifiers(login, msid, doctor_num)

      if account.nil?
        # Create
        if login.blank?
          login = generate_login(fields[:first_name], fields[:last_name], msid, doctor_num)
          fields[:login] = login
        end
        account = Account.create(fields)
        account.save!
      else
        # Merge
        first_name = fields[:first_name]
        last_name  = fields[:last_name]
        phone      = fields[:phone]
        email      = fields[:email]
        doctor_num = fields[:doctor_num]

        account.last_name = last_name unless last_name.blank?
        account.first_name = first_name unless first_name.blank?
        account.phone = phone unless phone.blank?
        account.email = email unless email.blank?
        account.save!
      end
      account
    end

  end

Round 2: Extract Method

The first step to making “create_or_merge” simpler was to yank out the blob of merge code into it’s own method. So the logic in create_or_merge looks a bit cleaner:

  • If can’t find account,
    • create new one;
  • else
    • merge this data into existing account.
  class Account
    include MongoMapper::Document
    # Rest of class omitted
    def self.create_or_merge(fields)
      raise ArgumentError if fields.nil?
      # Some code omitted

      # There are 2-3 legal ways to identify a unique account :-(
      account = Account.find_by_identifiers(login, msid, doctor_num)

      if account.nil?
        #Create
        if login.blank?
          login = generate_login(fields[:first_name], fields[:last_name], msid, doctor_num)
          fields[:login] = login
        end
        account = Account.create(fields)
        account.save!
      else
        #merge
        account.merge(fields)
      end
      account
    end
    def merge(fields)
      puts "Merging #{fields.inspect}"
      first_name = fields[:first_name]
      last_name  = fields[:last_name]
      phone      = fields[:phone]
      email      = fields[:email]
      doctor_num = fields[:doctor_num]

      self.last_name = last_name unless last_name.blank?
      self.first_name = first_name unless first_name.blank?
      self.phone = phone unless phone.blank?
      self.email = email unless email.blank?
      self.doctor_num = doctor_num unless doctor_num.blank?
      save!
    end
  end

Round 3: Introduce dynamic method calls

It is plain to see the repeating nature…based on the fields being passed in:

self.KEY = VALUE unless VALUE.blank?

If only there were a way to not have to write out repeating lines of code for each attribute we need to merge. Well, enter the ability to invoke instance methods by name, and passing in parameters:

Normal:

self.last_name = last_name unless last_name.blank?

Metaprogramming:

self.send("last_name", "Franklin")

And getting the name from the fields hash

self.send("#{k.to_s}=", v)  unless v.blank?

Also, there is a need to tailor which fields are allowed to be merged, or overwritten; hence, the introduction of this bit of “allow_overwrite” complexity.

  def merge(merge_fields)
    allow_overwrite = [:first_name, :last_name, :doctor_num, :phone, :email]
    # Only merge fields that are permitted... tossing out any illegal fields
    fields = merge_fields.each {|k,v| merge_fields.delete(k) unless allow_overwrite.include?(k.to_sym)}
    fields.each_pair do |k,v|
      # Update the field with new data, if available
      self.send("#{k.to_s}=", v)  unless v.blank?
    end
    save!
  end

Round 4: Compress slightly, removing one iteration loop

Instead of looping twice through the fields, I reduced it to a single pass.

  def merge(merge_fields)
    allow_overwrite = [:first_name, :last_name, :doctor_num, :phone, :email]
    merge_fields.each do |k,v|
      next unless allow_overwrite.include?(k.to_sym)
      self.send("#{k.to_s}=", v)  unless v.blank?
    end
    save!
  end

The Tests

Here is a snippet from my RSpec tests:

  # Uses metaprogramming too...
  def check_merging(field, new_value)
    @fields[field.to_sym] = new_value
    expect {
      account = Account.create_or_merge(@fields)
    }.to_not change { Account.count }.by(1)
    act = Account.find(@account.id)
    act.instance_eval(field).should == new_value
  end

  describe "being merged" do
    before do
      @group_num = "009015"
      group_name = "Country Doctor Pediatrics"
      grp = Group.find_by_group_num_or_create(@group_num, group_name)
      @doctor_num = "6709#{rand(20)}"
      @fields = {:login      => "jmadison",
                 :email      => "johns@CountryPedDocs.com",
                 :doctor_num => @doctor_num,
                 :name       => 'Dr. John Madison',
                 :first_name => "John",
                 :last_name  => "Madison",
                 :group_name => group_name,
                 :group_num  => @group_num
      }
      @account = nil
      expect {
        @account = Account.create_or_merge(@fields)
      }.to change{ Account.count }.by(1)
    end

    it "should merge new last name" do
      new_value = "Mattson"
      field = "last_name"
      check_merging(field, new_value)
    end

    it "should merge new first name" do
      new_value = "Mary Lou"
      field = "first_name"
      check_merging(field, new_value)
    end

    it "should merge new phone" do
      new_value = "123-321-1234"
      field = "phone"
      check_merging(field, new_value)
    end

    it "should merge new email" do
      new_value = "some_good_email@humptyfratz.biz"
      field = "email"
      check_merging(field, new_value)
    end

    it "should merge new doctor_num" do
      new_value = @doctor_num.reverse
      field = "doctor_num"
      check_merging(field, new_value)
    end

    after do
      if @account
        @account.destroy
        @account.save
      end
      Account.hard_delete
    end
  end

MongoMapper vs MongoDB Cursor Stats

I just love developing with MongoMapper and MongoDB… This weekend I had an easy opportunity to test out the performance between iterating through a collection via MongoMapper or MongoDB cursor. (I had to fix up a field that I munged by screwing up some production code — oops)

My findings showed that the cursor approach was ~1.8x faster.

There’s probably some underlying “but of course” comment waiting to come out of John Nunemaker (creator of the amazing MongoMapper) or Kyle Banker (MongoDB expert).

Like, “but of course letting the database server manage the work is always better than returning a big hunk of documents!”

The two flavors of iteration look basically like this:

  • cursor = coll.find({:doctor_num => /^staff_id_numberd{6}/})
  • error_accounts = Account.all(:doctor_num => /^staff_id_numberd{6}/)

The findings were based on “correcting” 5,929 of the 9,002 total accounts.

Time Memory
Cursor 166 sec 104K
MM Array 293 sec 175K


The scientist in me says do a test across a larger number of accounts: 90K, 900K 9M — and see what the trend looks like for the cursor — I would expect pretty flat. The pragmatist says I got more important work to do on our V2 of the production app <g>.

From this little bit of data (see the second figure), it seems that the cursor’s lead in the speed department diminished with increasing record counts. However, the memory consumption stays pretty flat for the cursor approach. I’m sure that the array approach will run out of memory at some point when you try and process a lot of records — never a good thing. (Maybe I should do some research on our message log — ~400k per month.)

Compare Processing Speed and Memory Usage for Cursor and Array Approach

The code for the Cursor way is shown here, with the lines of interest highlighted:

def self.fix_errors_cursor_style
  coll = MongoMapper.database['accounts']
  error_accounts = coll.find({:doctor_num => /^staff_id_numberd{6}/})
  error_accounts.each do |rec|
    new_doctor_num = rec["doctor_num"].match(/(d{6})/).to_s
    accounts = Account.find_dupes(new_doctor_num)
    if accounts.size > 1
      Account.merge_accounts new_doctor_num
    else
      coll.update({"_id" => rec["_id"]}, {"$set" => {"doctor_num" => new_doctor_num}})
    end
  end
end

The code for the MongoMapper way looked like this:

def self.fix_errors_array_style
  error_accounts = Account.all(:doctor_num => /^staff_id_numberd{6}/)
  error_accounts.each do |a|
    new_doctor_num = a.doctor_num.match(/(d{6})/).to_s
    accounts = Account.find_dupes(new_doctor_num)
    if accounts.size > 1
      Account.merge_accounts new_doctor_num
    else
      a.update_attributes( :doctor_num => new_doctor_num )
      result = a.save
      if a.errors
        a.errors.each_pair {|k,e| puts ">>> #{k}: #{e}"}
      end
    end
  end
end

In case this counts for completeness of information presented… The rough numbers of the collection look like this:

  • “count”=>9002,
  • “size”=>5829364,
  • “avgObjSize”=>647.56,
  • “storageSize”=>13880064,
  • “numExtents”=>5,
  • “nindexes”=>4,
  • “lastExtentSize”=>10420224,
  • “paddingFactor”=>1.01,
  • “flags”=>1,
  • “totalIndexSize”=>1679360,
  • “indexSizes”=>{“_id_”=>385024, “login_1″=>352256, “msid_1″=>352256, “doctor_num_1″=>589824}, “ok”=>1.0}

Don’t Fight Ruby!

In this blog post, “Adding Type Checking to Ruby,” I spit up a little bit in my mouth. I don’t want to be harsh… I can appreciate Daniel’s different approach to Ruby.

Mind you, I’m no Ruby whiz (yet), but look at this example:

def create_name(fname, lname)
  raise "fname must be a String" unless fname.kind_of? String
  raise "lname must be a String" unless lname.kind_of? String

  fname + " " + lname
end

I really couldn’t get past this first example. We’re seriously in dire need of checking that a first and last name are passed in as String types? I know it is just an example, but it is a lousy real-world example. It’s a bit like saying we need TDD and proceeding to show test-driving an accessor. I need more red meat!

Okay, so then there is some “Type” library that bundles up some helper methods to make the ugliness above go away:

typesig String, String
def create_name(fname, lname)
  fname + " " + lname
end

Except, the ugliness didn’t go away because it is the concept that I find hard to embrace.

I think the author is attempting to coerce Ruby to act as if it was statically typed. Static typing can be a helpful crutch, but more often than not, it just gets in the way. And if you like it that much, then use a language where it is built-in already. But certainly don’t pollute your Ruby code with pseudo-static type checking on every method!

I would advise against focusing on that static typing retro-fit effort.

In doing Ruby development, I have found it better (and easier) to put your labor into:

  1. writing clear, object-oriented code
  2. driving your code test-first (BDD/TDD)

Let your tests tell you things are working right. For example, if you wanted to ensure the class that had the first and last name in it behaved properly, you could write RSpec tests:

it "should show patient name as 'first last'" do
  @patient.name.should == 'John Johnson'
end

And if you really wanted to test that the values were Strings you could protect the creation of that object. For example, in a “validate” method that ensures that if a “Setting” instance is to represent a Boolean, than its value better be set to “true” or “false:”

# Settings to be used in the program
# Access in the program as follows: s("Company")
class Setting
 include MongoMapper::Document
...
 # Attributes ::::::::::::::::::::::::::::::::::::::::::::::::::::::
 # What the user sees as a label
 key :label, String, :unique => true, :required => true
 # How we reference it in code
 key :identifier, String, :unique => true, :required => true
 # Data type
 key :field_type, String,  :default => "String"
 # Store any kind of object in the value field.
 # This is what we will use to substitute for the setting
 key :value, Object,  :serialize => true

 # Validations :::::::::::::::::::::::::::::::::::::::::::::::::::::
 validates_inclusion_of :field_type,  :within => FIELD_TYPES
 validate :validates_as_boolean

  def validates_as_boolean
    if field_type == 'Boolean'
      errors.add(:value, "must be set to true or false for Boolean type") unless value == true || value == false
    end
  end
...

Adding a contract into every method as a matter of course, is just a waste of time and a polluting of the code “forest.”

IMHO, let Ruby breathe the freedom it was meant to have.