Advanced Rails Recipes

Almost a year ago, I submitted three recipes to Advanced Rails Recipes. Unfortunately, one which was originally selected back in June has been dropped from the book as it’s not really advanced. So I thought I’d post it here for everyone to enjoy.

Using AJAX with REST

Problem:

You have finally mastered REST (Representational State Transfer) and wish to enhance your application with JavaScript but don’t know which of the resource URLs to call.

Ingredients:

This recipe requires Rails 1.2 or later as it relies on RJS for the Ajax portion and REST for the controller design.

Solution:

In this example we need users to be able to register (and unregister) for meetings so that we can properly report attendance. So we have three models: User, Meeting and Registration.

Since Registrations are associated with a specific meeting, we’ve created them as a nested resource, like so:

~~~~ {lang=“ruby”} map.resources :meetings do |meetings|

meetings.resources :registrations

end ~~~~

To register for a meeting we want to create a new Registration instance tied to the Meeting and User. Unregistering from a meeting should destroy the Registration object. So our RegistrationsController looks like this:

~~~~ {lang=“ruby”} # POST /registrations def create

@meeting = Meeting.find(params[:meeting_id])
current_user.register_for(@meeting)
respond_to do |format| 
  format.html { redirect_to meetings_url }
  format.js   # create.rjs
  format.xml  { head :ok }
end

end

# DELETE /registrations/1 def destroy

@registration = Registration.find(params[:id])
@registration.destroy
respond_to do |format|
  format.html { redirect_to meetings_url }
  format.js   # destroy.rjs
  format.xml  { head :ok }
end

end ~~~~

In the view we want to use Ajax to allow the user to modify their registration, so we need to use link_to_remote:

~~~~ {lang=“ruby”} <% if !current_user.registered_for(meeting) –%>

<%= link_to_remote "Register!", :url => registrations_path(meeting), 
                              :meeting_id => meeting.id, :method => :post %>

<% else –%>

<%= link_to_remote "Unregister", 
                              :url => registration_path(meeting, 
                                         current_user.registration_for(meeting)),  
                              :confirm => 'Are you sure?', :method => :delete %>

<% end –%> ~~~~

The RJS to update the page looks like this:

~~~~ {lang=“ruby”} page[“ meeting_#{@meeting.id}”.to_sym].replace_html

   :partial => 'shared/meeting', :object => @meeting

page[“ meeting_#{@meeting.id}”.to_sym].visual_effect :highlight, :duration => 5 ~~~~

Discussion:

The resource URLs provided by Rails are extremely useful, but they can be confusing. Tools like FireBug make it easy to see what is going on behind the scenes. When developing Rails applications, it is usually easier to develop without Ajax and then go back and add Ajax to those portions of the application where it would make sense. With this approach, you can gracefully degrade your service back to the original mode for those who don’t have or don’t enable JavaScript. It also allows you to click through your application with FireBug enabled and watch which URLs are hit and with which methods (GET, POST, PUT, or DELETE).

The table below outlines which resource URLs to use with your remote helpers:

Intent Action Resource URL Method


Creating a new instance create plural form of model name (i.e. registrations_path) POST Retrieving an instance show singular form of model name with id (i.e. registration_path(registration) GET Retrieving all instances index plural form of model name (i.e. registrations_path) GET Modifying an existing instance update singular form of model name with id (i.e. registration_path(registration)) PUT Deleting an existing instance destroy singular form of model name with id (i.e. registration_path(registration)) DELETE

Once you are comfortable with the resource URLs, adding Ajax to your RESTful is much easier.

Further Reading:

If you are unfamiliar with REST, read Chapter 20 of Agile Web Development with Rails, 2nd Edition by Dave Thomas and David Heinemeier Hansson (Pragmatic Programmers, 2006). There is also a nice screencast on the subject from PeepCode.

For more on Ajax in Rails, you can check out Ajax on Rails by Scott Raymond (O’Reilly & Associates, 2007).

NSCoderNight DC

Tonight I was able to attend NSCoderNight DC in Tysons Corner, VA and finally met Jose Vazquez in person. Unfortunately, turn out is pretty spotty, with Jose being the only continuous attendee. Despite it being just the two of us, I came away energized and started playing around with the new APIs we spoke about: Core Audio/AudioQueues and Quartz Composer.

I was shocked when Jose explained that the Core Audio and AudioQueues tools are all Carbon (i.e. plain C) instead of Cocoa. Mistakenly, I was under the impression that Carbon had been officially deprecated by Apple and that only Cocoa would be supported going forward. The AudioQueueTools (/Developer/Examples/CoreAudio/SimpleSDK/AudioQueueTools) example which comes with the Apple Developer Tools shows how to create command-line tools for recording and playing back sounds, but trying to integrate this with you Cocoa application is a different story. Luckily, we found this post on Mark Darlymple’s blog which explains how to use C callbacks in Objective-C. I’m still playing around with this and also looking at the QuickTime API support for recording sound.

Quartz Composer is an amazingly cool app, that appears deceptively simply to throw something together with. I was blown away by Jose’s demo of it, having never seen it before. The visual programming environment, Quartz Composer Editor, has a fairly straight-forward GUI and allows you to easily ‘compose’ multiple processing units (a.k.a. patches) together into composition while the view window displays what the composition will look like in real-time. I’m definitely going to spend some time to go through the examples (/Developer/Examples/Quartz Composer/Applications).

If you are free on a Tuesday night, be sure to stop by the Tysons Corner Panera&sll=38.878131,-77.278595&sspn=0.009689,0.0156&ie=UTF8&om=1&ll=38.931038,-77.230368&spn=0.043267,0.079479&z=14&iwloc=addr&source=embed) from 7pm-9pm. I’m going to make every effort to attend regularly.

What’s Your Segment?

Whether you realize it or not, there are several companies gathering a tremendous amount of data about you. One such company is Acxiom which maintains consumer information on nearly every household in the U.S. (and has branched out to other countries). Acxiom combines tax records and public census data with transactional data from its clients (the corporations you deal with on a daily basis). Using this data, Acxiom has created a product called PersonicX which is a finely-grained household level segmentation system based on consumer and demographic characteristics.

The idea of such a classification system both interests and appalls me quite a bit, so I’ve done some research into the 70 segments and 21 life-stage groups.

Even though this data is proprietary, I’ve found the following information through publicly available sources (ordered by cluster number):

  1. Summit Estates: The wealthiest of all the clusters. In every sense, these families are enjoying the good life—luxury travel, entertainment and consumption of every kind are within easy reach. Middle-aged (36-56). (Group 11B – Boomer Barons)
  2. Established Elite: Late middle-aged (46-65) married home owners with no children living with them. They are wealthy and live in suburban and urban locations. (Group 15M – Mature Wealth)
  3. Corporate Clout: This exceedingly well educated group defines themselves as workaholics. Savvy investors who like to read Forbes and travel magazines, they are also heavy users of air travel and corporate credit cards. (Group 15M – Mature Wealth)
  4. Skyboxes & Suburbans: One of the best-educated and wealthiest clusters. They shop at upscale stores, spend time feathering their nest and adhere to regular fitness programs. Middle-aged (36-55). (Group 11B – Boomer Barons)
  5. Sitting Pretty: Late middle-aged (46-65), married home owners with no children in the home. They are wealthy, living in the outer-suburbs and towns. (Group 19M – Golden Years)
  6. Shooting Stars: Still relatively young at a mean age of 36, and with top rankings for income, college education, home value and net worth, these consumers have the world by the tail. (Group 7X – Cash & Careers)
  7. Leveraged Lifestyles: Middle-aged (36-55), married home owners with school-aged kids in the household. Primarily upper-middle income, living in the outer-suburbs and towns. (Group 11B – Boomer Barons)
  8. Full Steaming: Late middle-aged (55-64), both single and married home owners with no kids in the household. Primarily upper-middle income, living in the outer-suburbs and towns. (Group 19M – Golden Years)
  9. Platinum Oldies: Seniors (aged 66+), both single and married, who own their own homes with no kids in the household. They are wealthy, living in the outer-suburbs and towns. (Group 19M – Golden Years)
  10. Hard Chargers: This affluent group of single men and women between the ages of 30 and 45 primarily own their own homes in the suburbs. (Group 7X – Cash & Careers)
  11. Kids & Clout: Middle-aged (36-45) married home owners with school-aged children. They are affluent, living in the outer suburbs and towns. (Group 8X – Jumbo Families)
  12. Tots & Toys: Work and family consume these middle-aged (30-45) couples. They’re putting their college degrees into action with lucrative careers, while saving for their children’s education. (Group 8X – Jumbo Families)
  13. Solid Single Parents: Middle-aged (36-54) single parents who own their own homes with school-aged children. They are affluent and live in urban and suburban areas. (Group 12B – Flush Families)
  14. Career Centered Singles: The affluent members of this segment are late middle-aged (46-65) and single. They own their own homes in urban and suburban areas and have no kids. (Group 16M – Aging Upscales)
  15. Country Ways: Late middle-aged (45-65), married home owners with no kids in the household. Primarily upper-middle income in a rural location. (Group 16M – Aging Upscales)
  16. Country Single: Middle-aged (36-55) single home owners with no kids. They are upper-middle income, living in rural areas. (Group 14B – Our Turn)
  17. Apple Pie Families: Late middle-aged (46-55) married home owners with school-aged kids. Members of this group are upper-middle income, living in urban and suburban areas. (Group 12B – Flush Families)
  18. Married Sophisticates: Recently married young couples who are well educated and enjoy health upper-middle range incomes. They are almost all homeowners in upscale suburban neighborhoods. (*[Group 2Y
  19. Country Comfort: Middle-aged (36-55), married home-owners who have school-aged children in the household (big families, family-oriented) make up this group. Primarily upper-middle income in a rural location. (Group 8X – Jumbo Families)
  20. Dynamic Duos: Made-up of upper-middle income married couples who own their own home but don’t have children, the members of this cluster are between 36 and 45. (Group 7X – Cash & Careers)
  21. Children First: At a mean age of 25, they are already raising an average of 1.2 kids. Despite a nearly even split between married and single households, 100% show the presence of children. They earn upper-middle incomes and live in suburban areas. (Group 2Y – Taking Hold)
  22. Fun & Games: The upper-middle income members of this cluster are between the ages of 46 and 55, married with no kids, and own their own homes. (Group 14B – Our Turn)
  23. Acred Couples: Late middle-aged (56-65) married home owners with no kids. These upper-middle income earners live in the outer suburbs and towns. (Group 16M – Aging Upscales)
  24. Career Building: This cluster is made up of young, childless singles. They are a mix of mobile renters and first-time homeowners, living in condos and single-family houses. (Group 2Y – Taking Hold)
  25. Clubs & Causes: Consisting of both married and single upper-middle income folks, the members of this cluster are aged 66 to 75 with no children who own their own homes. (Group 20S – Active Elders)
  26. Savvy Singles: Young middle-aged (30-45) single renters and home owners with no kids. Primarily upper-middle income, living in urban and suburban areas. (Group 7X – Cash & Careers)
  27. Soccer & SUVs: This cluster consists of upper-middle income married couples between the ages of 36 and 45 who own their own home and have school-age kids. (Group 8X – Jumbo Families)
  28. Suburban Seniors: These aged seniors (76+) own their own homes and have no kids. They are a mix of single and married upper-middle income earners living in urban and suburban areas. (Group 20S – Active Elders)
  29. City Mixers: Middle-aged (36-45) single renters and home owners with no kids. Primarily middle income, they live in downtown metro areas. (Group 9B – Boomer Singles)
  30. Spouses & Houses: This cluster is dominated by middle-income, childless couples in their mid-20s. They are mainly high school grads who own their homes and tend to live in smaller second cities around the country. (Group 2Y – Taking Hold)
  31. Mid Americana: Married suburbanites. They are middle of the road in terms of education and income, although long tenure and high equity versus home values result in high net worth. (Group 14B – Our Turn)
  32. Downtown Boomer Couples: Late middle-aged (46-65) married home owners with no kids. They are middle income and live in downtown metro areas. (Group 14B – Our Turn)
  33. Urban Tenants: Late middle-aged (46-55), single renters with no kids. Located in downtown metro areas, they are middle income. (Group 14B – Our Turn)
  34. Outward Bound: Middle-aged (30-45), married home owners with no kids. Primarily middle income, living in rural locations. (*Group 3X
    • Transition Blues*)
  35. Solo & Stable: Middle-aged (36-45) single home owners with no kids. Primarily middle income, living in urban and suburban areas. (Group 9B – Boomer Singles)
  36. Raisin’ Grandkids: Seniors (aged 66+), both single and married home owners with kids of mixed ages in the household. Primarily middle income, living in suburband and urban areas. (Group 20S – Active Elders)
  37. Cartoons & Carpools: Middle-aged (30-45), married home owners with school-aged kids. Primarily middle income, living in urban and suburban areas. (Group 5X – Gen X Parents)
  38. Blue Collar Bunch: Late middle-aged (46-65) married home owners with school-aged kids. Primarily lower-middle income, living in urban and suburban areas. (Group 13B – True Blues)
  39. Early Parents: Young (18-29) single and married parents who either rent or own their residence with kids (mixed ages) at home. Primarily lower income, living in urban and suburban areas. (Group 1Y – Beginnings)
  40. The Great Outdoors: Late middle-aged (45-65), married home owners with no children in the household. Primarily lower-middle income in a rural location. (Group 18M – Mature Rustics)
  41. Trucks & Trailers: Middle-aged (30-45), both single and married renters and home owners with no kids. Primarily lower-middle income, living in rural areas. (Group 3X – Transition Blues)
  42. First Mortgage: Young middle-aged (30-35), single home owners with no kids. Primarily lower-middle income, living in urban and suburban areas. (Group 4X – Gen X Singles)
  43. Work & Causes: These lower-middle income singles own their own homes. Living in urban and suburban areas, they are between 46 and 55 and have no children. (Group 17M – Modest Means)
  44. Community Singles: Late middle-aged (56-65) single home owners with no kids. With a lower-middle income, they live in urban and suburban areas. (Group 17M – Modest Means)
  45. First Digs: This group consists of young (24-29) singles with no kids who either rent or own. Primarily lower-middle income, living in urban and suburban areas. (Group 1Y – Beginnings)
  46. Home Cooking: Middle-aged (30-45) married home owners with no kids. Primarily lower-middle income, living in urban and suburban areas. (Group 3X – Transition Blues)
  47. Rural Parents: This group consists of middle-aged (36-55) single parents who own their own homes and have children of mixed ages. Primarily lower-middle income in a rural location. (Group 10B – Mixed Boomers)
  48. Farmland Families: Middle-aged (36-55), married home owners who have school-aged children in the household. Primarily lower-middle income in a rural location. (Group 13B – True Blues)
  49. Sedentarians: These married seniors (76+) own their own homes and have no children. Living in urban and suburban areas, this group consists of lower-middle income earners. (Group 21S – Leisure Buffs)
  50. The Greatest Generation: This group consists of married seniors (66+) who own their own homes where the kids have moved out. Primarily lower-middle income in a rural location. (Group 18M – Mature Rustics)
  51. Family Matters: These seniors (66-75) are married home owners with no kids at home. Living in the outer suburbs and towns, they are in the lower-middle income bracket. (Group 21S – Leisure Buffs)
  52. Still Landlorded: Middle-aged (36-45) singles renting their residence with no kids. Primarily lower-middle income, living in urban and suburban areas. (Group 4X – Gen X Singles)
  53. Metro Parents: Middle-aged (36-55) single parents who own their own homes and have school-aged children. Primarily lower-middle income, living in urban and suburban locations. (Group 10B – Mixed Boomers)
  54. Still Truckin’: This group consists of late middle-aged (46-65), single home owners with no kids in the household. Primarily lower-middle income in a rural location. (Group 18M – Mature Rustics)
  55. Humble Homes: These married, late middle-aged (46-65) home owners don’t have any kids. They live in the outer suburbs and towns and are lower income earners. (Group 17M – Modest Means)
  56. Modest Wages: Middle-aged (36-45) single home owners with no kids. Primarily low income, living in urban and suburban areas. (Group 9B – Boomer Singles)
  57. Collegiate Crowd: This group consists of young (18-23) singles with no kids who rent or own. Primarily lower income, living in urban and suburban areas. (Group 1Y – Beginnings)
  58. Young Workboots: Young (18-29) singles with no kids who rent or own. Primarily blue-collar workers (low income), living in rural areas. (Group 1Y – Beginnings)
  59. Low-Rent Digs: Young middle-aged (30-35) single renters with no kids. They are in the lowest income bracket, living in urban and suburban areas. (Group 4X – Gen X Singles)
  60. Rural Rovers: Middle-aged (36-45) single renters with no kids, they live in rural areas and are lower-middle income. (Group 10B – Mixed Boomers)
  61. Urban Scramble: Young (24-35) single renters with no kids. Primarily lower income, living in down town metro areas. (*Group 6X
    • Mixed Singles*)
  62. Kids & Rent: Middle-aged (30-45) single and married renters with kids (mixed-ages). Primarily lower income, living in the outer suburbs and towns. (Group 5X – Gen X Parents)
  63. Single City Struggles: These late middle-aged (46-65) single renters live in urban and suburban areas. They have no kids and are low income earners. (Group 17M – Modest Means)
  64. Rural Antiques: This group consists of single renters and owners aged 76 and older with no children in their household. Primarily lower income in a rural location. (Group 21S – Leisure Buffs)
  65. Thrifty Elders: These single seniors (66-75) own their own homes in the outer suburbs and towns. They are among the lowest income earners. (Group 21S – Leisure Buffs)
  66. Timeless Elders: In the lowest income bracket, these single aged seniors (76+) own their own homes in urban and suburban areas. (Group 21S – Leisure Buffs)
  67. Rolling Stones: This group consists of young (24-29) singles with no kids who rent or own. They are the in the lowest income bracket, living in urban and suburban areas. (Group 1Y – Beginnings)
  68. Penny Pinchers: These late middle-aged (46-65) single home owners are among the lowest income earners. They live in urban and suburban areas with no children. (Group 17M – Modest Means)
  69. Mortgage Woes: Middle-aged (30-45) single home owners with no kids. They are in the lowest income bracket, living in urban and suburban areas. (Group 6X – Mixed Singles)
  70. On the Edge: Middle-aged (36-45) single renters with no kids. They are in the lowest income bracket, living in urban and suburban areas. (Group 6X – Mixed Singles)

Some the cluster identifiers are funny while others are pretty disheartening—I’m sure the intention was to make them memorable and in that Acxiom has succeeded. You’ll notice that the majority of information available online focuses on the top thirty or so segments, as these are apparently the most lucrative (consisting of the upper and upper-middle classes).

It’s amazing how much data Acxiom collects and the detail of information they can extract from that data. For instance, for each segment they can give you an idea of what activities they enjoy, their opinions/beliefs, how they vote, where they shop, what they read, what they eat, what their finances look like, etc.

Acxiom provides a document on their website which shows how they can predict the migration of consumers from one segment to the next. They need to predict well as these segments are so specific that nearly one-third of Americans change their segment each year.

If you can help me find more freely-available, detailed information (like this) for any of these segments, please leave a comment below or email me.

References

Finally Awarded a Patent!

After almost seven years, I was finally awarded a patent: 7,170,862. It’s assigned to my previous employer, Cisco Systems, Inc. Until I started this process, I never imagined how long the USP&TO could take to finally assign a patent. Now I’m waiting to see if any of my other patents filed by Cisco will be awarded.

This actually happened back in January, but since I was never notified, I researched it myself after the recent news of Cisco opening up IOS to 3rd party developers.

Protecting Your Ruby Source Code for End User Applications

If you want to distribute your Ruby applications while still protecting your intellectual property you could use an obfuscation tools such as ZenObfuscate or try to write your own. But in this article, I’m going to show a different approach that’s been used by several different companies producing commercial products written in Ruby. The method is not specific to Ruby and should work for any interpreted language in which you need to distribute your source code with the application.

The secret is to encrypt your Ruby source, store it in a database and then modify the Ruby interpreter to look for your code in the database and decrypt it on the fly. I should note that there is no way to completely protect a product which is distributed to your customers—with enough diligence any security measures can be broken.

These instructions are for Unix operating systems (include MacOS X). Unfortunately (or fortunately, for me), I don’t own a Windows machine.

If you don’t already have Berkeley DB (BDB) installed on your system you can download it here. You will need to follow the instructions which come with BDB which explain how to build and install it. Originally, I was going to use OpenSSL to encrypt our Ruby source code before inserting it into the database and decrypt it after retrieving it from the database. Thankfully, the latest version of BDB includes AES support which allows you to maintain an encrypted database very easily.

You will also need to obtain a fresh copy of the Ruby source code so that we can build our own private version which knows how to pull classes out of our BDB instance. So let’s start by creating a directory for us to work in:

mkdir -p ~/RubyProject/deploy

You should unpack the Ruby source in the \~/RubyProject directory (this should create directory which looks like \~/RubyProject/ruby-1.8.6-p111). Go into that directory and run the configure script as follows:

./configure --prefix=~/RubyProject/deploy --with-static-linked-ext

This will enable us to build a stand alone Ruby interpreter which has everything it needs statically linked in.

To find out where we need to hook in our handler which loads missing classes from the database, you can grep through the source code looking for const_missing like this:

grep const_missing *.c

This turned up two files object.c and variable.c. Looking at the output, I could see that the const_missing function is actually defined in variable.c. Jumping in, I searched for the code executed when a constant cannot be found. This led me to the rb_const_get_0 function which walks up the class hierarchy looking for the specified constant. If it cannot be found, it returns the result of the const_missing function. So right before that final return, I added the following hook:

    /* At this point we haven't found the class so we must look
       inside of the BerkeleyDB for it, see dbloader.c */
    value = load_from_db(id);
    if (value != Qundef) return value;

Now I just had to implement the load_from_db function which should look inside of the encrypted BDB instance for the file. Looking at how the rb_const_get_0 function works, I knew my new method had to return a Ruby VALUE:

VALUE
load_from_db(id)
     ID id;
{
    DBT k;
    DBT d;

    memset(&k, 0, sizeof(DBT));
    memset(&d, 0, sizeof(DBT));

    verify_database_state();

    k.data = rb_id2name(id);
    k.size = strlen(k.data);

    d.flags = DB_DBT_MALLOC;

    if (bdb->get(bdb, NULL, &k, &d, 0) == 0) {
      rb_eval_string((const char *)d.data);
      free(d.data);
      return rb_eval_string(rb_id2name(id));
    }
    return Qundef;
}

You’ll note that if we find the class in the database, we first evaluate it using rb_eval_string. Since this returns nil, we need evaluate the class name so that we can pass back a Ruby VALUE. If the class doesn’t exist in the database, we return Qundef and const_missing gets called as usual.

The verify_database_state function ensures that the password for the BDB instance was passed and the database opened so that the get call can access it. It is implemented like so:

/* a simple way to obfuscate the password in memory */
#define A(c)             (c) - 0x1d
#define ENCRYPT_PWD(str) do { char * p = str; while (*p) *p++ -= 0x1d; } while (0)
#define DECRYPT_PWD(str) do { char * p = str; while (*p) *p++ += 0x1d; } while (0)

static void
verify_database_state()
{
  /* don't forget to NULL terminate this array! */
  static char info[] =
    { A('1'), A('2'), A('3'), A('4'), A('5'),
       A('6'), A('7'), A('8'), A('9'), A('0'),
       0 };

  const char * database_file = "data.db";

  if (bdb == NULL) {
    db_env_create(&bdbenv, 0);
    db_create(&bdb, bdbenv, 0);

    DECRYPT_PWD(info);
    bdbenv->set_encrypt(bdbenv, info, DB_ENCRYPT_AES);
    ENCRYPT_PWD(info);
    bdbenv->open(bdbenv, ".", DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0600);
    bdb->set_flags(bdb, DB_ENCRYPT);
    bdb->open(bdb, NULL, database_file, NULL, DB_BTREE, 0, 0644);
  }
}

You’ll notice that the password is hard coded into the binary along with the location of the BDB instance. If you are going use this code, you’ll want to change the default password (which needs to match the password you used when storing your Ruby source in the database) and perhaps the location of the database. To deter those looking to decrypt our Ruby source, I’ve written C macros which performs a transformation on the password. It is fairly simple to circumvent, so you should research other methods of securely storing passwords within applications.

Finally, you’ll need to remember to close the BDB instance before the Ruby interpreter exits. To do this we simply open up main.c and add the following line immediately following the call to ruby_run:

close_database();

The implementation of close_database is trivial, so you can just look in the patch I’ve provided for it.

The patch to the Ruby source code is included in this zipped attachment along with another utility I wrote to load all of your Ruby source into the encrypted BDB instance (along with its Makefile). Here is a transcript which shows how to use the tools and your new Ruby interpreter:

~RubyProject/tools> ./rb_store data.db *.rb
Enter database password:
Stored Example (131) bytes

~RubyProject/tools> ./rb_load data.db
Enter database password:
Hit CTRL+C to quit.
Enter a class name: Example
Found: 

class Example
  def initialize
    puts "Hello from the Example class"
  end

  def aMethod
    puts "Called aMethod"
  end
end

Enter a class name: Foo
No such class
Enter a class name: ^C

~RubyProject/ruby-1.8.6-p111> ./ruby -e "e = Example.new"
Hello from the Example class

~RubyProject/ruby-1.8.6-p111> ./ruby -e "f = Foo.new"
-e:1: uninitialized constant Foo (NameError)

This implementation doesn’t deal with multiple classes of the same name residing in different directories. Also, the way in which the rb_load tool determines the name of the class is rather naive and can screw it up. I’ve wanted to write this for over a year now after hearing Rich Kilmer talk about the way in which InfoEther distributes its Ruby applications. So this is more of a proof-of-concept for myself than anything else. Hopefully you learned from this tutorial and can put it to good use.