My first real project using MongoDB (Rails + MongoMapper)



Written by Anthony Heukmes on Tue Jan 05 16:01:23 UTC 2010

15 comments



This article is not an introduction to MongoDB or MongoMapper.
It's a summary of a few things I learned while I was working on my first real project based on MongoDB.
You will find here some tips that will (I hope) help you during your MongoMapper learning phase.



I've been working recently on a project for a client who wanted to sell second-hand vehicles on his website.
It's a classic story : visitors can register and post announces describing the vehicle they'd like to sell.
Various vehicles types are available : cars, bikes, trucks and more.



Each announce is characterized by various attributes depending on the selected vehicle type.
You can find general information (brand, mileage, ...), information on the equipment (GPS? Radio?), the contact person, some pictures... and more.
Of course, the equipment of a truck and a bike will be very different!



I've been thinking for a while about the best way to structure my SQL schema but I was never satisfied with the results.
It was either too complicated to implement or to maintain and I also had the impression that the solution was not pretty enough.



Being addicted to new technologies, I directly took this opportunity to jump into the NoSQL world and start learning MongoDB, a documents oriented database more and more popular in the Rails community.



When I think about it, it's so much more beautiful to represent announces as documents, containing various subdocuments (general info, equipment, pictures...) instead of a couple of rows in SQL tables.



Notice that I'm using ActiveRecord (AR) an MongoMapper (MM) in the same project (because I installed my CMS engine) and it's working perfectly!



First steps



MongoMapper is very easy to learn because it mainly respects the ActiveRecord API. You will not be lost in a unknown world because you will be able to manipulate your documents as you are currently doing with your AR records. You can persist documents using well-known methods like save, create and update_attributes. You can also select the data you need using finders and conditions. Associations, callbacks and validation are available as well. In fact, the views and the controllers generated by a scaffold can directly be used as they are with MongoMapper.



That being said, don't think that learning MM will be as easy as pie. This is because your brain is currently SQL formated!
During the development of your project you will ask yourself a few questions and you'll have to search to find the answer.
For example, how can you execute a search on text fields? With SQL, you know that you have to use LIKE. But what is the equivalent with MongoDB?



MongoMapper is a young project and it is still under heavy development. There is currently no documentation, no books and only a few people are blogging about it.
The best place to get help is the Google group.



Once familiarized with the beast, you'll probably not want to come back to ActiveRecord.
After two full months of Mongo I had to get back to ActiveRecord and this is probably only at that time that I really realized the awesomeness of MongoMapper.



It was hard to remember migrations syntax and rake tasks to execute them... With MongoMapper, these painful migrations are gone!



The biggest disadvantage of the MongoMapper-ActiveRecord switch is that you loose most of your plugins. I'm for example a fan of Authlogic and I had to drop it. This is probably a matter of time because I know that his author is working on a MM compatible version. Some plugins will still work, like the excellent will_paginate for example.



Also, some very used features of ActiveRecord like named scopes aren't available yet.



Embedded Documents



MongoDB allows you to create embedded documents. In my personal case, an Announce can contain multiple pictures :





At the beginning it maybe difficult to determine whether or not you should use classic or embedded documents.
To help you in your choice, I will tell you when not to use embedded documents.



It's important to know that an EmbeddedDocument has a very limited set of methods.
For example, you cannot call the find method on a such object.



Following instructions are not valid :





So if you want to be able to manipulate a document independently of his parent, you should not use an embedded document.
These documents are mostly manipulated using classic Ruby code.



To create a new embedded document :





To select a document :





a.pictures returns a simple Ruby Array, the select method is a classical Ruby method, it has nothing to do with MongoMapper.



If you want to edit a document :





Note that you have to call the save method on the main document (the announce in this case).



Finally, here is the code to delete an embedded document :






Associations



You will find many examples on the Internet using the String type to declare "foregin keys".
But this is deprecated and should not be used anymore with new versions of MM.



Use ObjectID instead :





At your first tries you will also probably try to find an equivalent of the ActiveRecord has_one association.
We don't need that with MongoMapper because keys are not limited to basic types.



If you want to specify that a user has one profile, you just have to define your key like this :





The Profile class can for example be an EmbeddedDocument.




Finders



Let's come back to one of my first questions : how can you execute a search on text fields (equivalent of LIKE in SQL)?
By using regular expressions!





All users containing "thony" in their email address will be returned.
The /i allows us to specify that we want to ignore the case.



As you understood, you'll be able to perform very precise research if you refresh your skills in Javascript regular expressions.



What if I want to find all users created today? You can use $gt (greater than) and $lt (lower than) :





And finally, how do you find articles matching the searched keyword in their title OR description?
We'd like to generate a OR instead of a AND :






Testing



I use Machinist a lot to generate fake data in my specs.
Happily, an adapter exists for MongoMapper and you can find it on GitHub.



Just install the gem (gem install machinist_mongomapper) then edit your blueprints.rb file to load the new adapter (require 'machinist/mongomapper').



As you probably noticed, Mongo IDs are not simple numbers.
If you have to generate fake IDs in your tests, the following method is for you : Mongo::ObjectID.new




My plugins



I'll finish this article by promoting two plugins I recently developed :



- mongo_admin : generate an admin interface for your MongoMapper models. Demo available here.

- mongo_translation : allows you to internationalize (I18n) your MongoMapper models very easily.

Reverse scaffold an existing table



Written by Anthony Heukmes on Tue Aug 04 15:21:55 UTC 2009

8 comments



If like me you are used to work with existing database schemas, the reverse_scaffold script should be interesting for you.


Just download it from GitHub and copy the file into the "script" folder of your Ruby on Rails application.

You can then execute it from your terminal :

$ ruby script/reverse_scaffold users user


For this example I presume you have an existing "users" table in your database described by the following columns :


  • id

  • pseudo

  • email

  • password




In this case, the script will execute the command :

ruby script/generate scaffold user --skip-migration id:integer pseudo:string email:string password:string


As you understood, the first argument represents the name of the table and the second one is the name of the model to generate.


You can use the -V option if you don't want to execute the scaffold but just display the generated command.

If you are an RSpec adept, you can specify the -r option and rspec_scaffold will then be used.



For more information :

$ ruby script/reverse_scaffold -h



A successful story of Ruby on Rails in the enterprise



Written by Anthony Heukmes on Fri May 29 11:24:37 UTC 2009

3 comments



Robert Martin gave a excellent keynote at RailsConf 09 "What killed Smalltalk could kill Ruby too" where he enforced that Rails must open his doors to the external world's needs.
There are a lot of good practices and conventions in the Ruby and Rails community and I really think it's the best framework for web development.
But our practices are not respected everywhere. Other people have different needs/visions and Rails should be a good solution for them too.

A simple example is related to primary keys. By default, database tables of a Rails application contain an auto-incremented ID acting like a primary key.
In his base version, Rails doesn't support composite primary keys. A plugin exists to add this functionality but it's not a core feature of ActiveRecord.
If you ask Rails fanatics for the reason of a such choice, they will tell you that composite primary keys is not a good practice and you should avoid them!
Unfortunately, those primary keys are everywhere in the professional world where a lot of big Oracle schemas don't respect our conventions. And they will of course not change to adapt to a new framework.

Rails have to adapt to that kind of needs if we want it to be a competitive choice for the enterprise.
And happily, Rails is more and more enterprise ready version after version.

I recently managed to integrate Rails in a big company which was only using Java. The goal of this article is to share the difficulties I met during the process. I hope it will maybe help you to accomplish the same "exploit" and make your dream comes true : using Rails on a daily basis!

Rails development on a legacy Oracle schema was my main fear. Until that day, my schema was always evolving with my Rails application and so, it was following the ActiveRecord conventions.
However, I rapidly discovered that it was easy as pie to configure an AR model to override these conventions. You can read my blog post on this subject. Executing stored procedures and other kind of boring stuff is also a piece of cake.

The scripting language term is scary for most people, mainly because of the slowliness idea associated to it.
Do not hesitate to reassure your customers by showing them many high traffic websites using Rails and enforcing that they don't encounter performance issues. Here is a list that will help you in this task.
Another argument to bring to light is JRuby on Rails. I think it's a great solution to integrate Rails more easily in the enterprise. Customers are very happy to learn that Rails can run on the JVM and that they will not totally leave the J2EE world.

Ruby and Rails skills is another major problem. If you suggest a new Java framework, chances are big that it will be accepted very easily. All developers in the company already know Java! But almost nobody knows Ruby and that means they will loose time because of the learning curve! And don't forget that many developers are not passionate by their job, they don't want to learn new technologies! Add to this the fact that it's already difficult to hire a good Java developer... how hard will it be to find Rubyists?

I personaly got rid of these fears by demonstrating how Ruby and Rails are easy to learn. Take the necessary time to prepare an attractive training during which you will share your excitement and your passion for this technology. I gave trainings to my colleagues and they quickly discovered Rails' benefits and how easy it is to learn. Most of my co-workers were then blowing the merits of Rails and I wasn't the only one to fight anymore.
"Awesome" was on every mouthes when my colleagues started their first test applications using our wonderful framework. What a pleasure to see their smiling faces!
I also compared pieces of Java and Ruby code if front of a customer so he can realize how Ruby syntax is clear and easy. And so, how Rails applications are easier to maintain over time.

Another issue is Rails development on Windows. I'm personally working on a Mac but I'm the only one.
We tried Aptana and Netbeans but it was clearly not a good solution. Happily, RubyMine released an excellent IDE. You have to try it!
A big problem is that Windows is even used for deployment. And we all know that deploying a Rails application on Windows is a pain in the ass. Plus, tools to monitor and deploy Rails applications are considered as toys compared to the powerful Java tools. One more time, I think that JRuby can be the perfect solution. The warbler utility makes it possible to create a war file from a Rails application. Once you have that, you can easily deploy your app on any J2EE application server. I'll talk about that in a next article.

The enterprise word is very often associated with portal. So a natural question is : "Is it possible to integrate a Rails application in a portlet?". I never personally tried but I read that it's possible to achieve.

The Java framework (Oracle ADF) that had been used since now by our customer was based on JSF so users were used to rich components like datagrids with sorting, pagination, etc. The kind of stuff we don't have out of the box with Rails. That why I created a Rails plugin based on jQuery to create full featured datagrids with just a few lines of code.

Finally, I will finish this article by giving a few arguments that convinced my customers.

RSpec was very appreciated! I simulated a failing test and the autotest-growl combo made a good impression.
Specs generation at the doc format was also an interesting point.
I was not using Cucumber at that time but I'm sure that it will impress too!

I aslo created a template for new projects creation. It contains the company layout (css, images, ...) and other shared functionalities. The possibility to generate a new application with just one command line was very appreciated.

Do not hesitate to share your experiences... I'll be happy to read them!

EdgeRails : rack router, protect_from_forgery & modules dependencies



Written by Anthony Heukmes on Fri Apr 17 16:16:01 UTC 2009

5 comments



The way towards Rails 3 is going on and you can find a lot of activity on GitHub these days.

First good news, Carl Lerche & Josh Peek are working on a new router (a Rack middleware, so it will not be only useful for Rails). According to Carl, performances are 2x faster than Merb router (and so, 4 times faster than Rails).

More information available on Rack GG group.

Another good news, we will not have to add authenticity tokens to our hand-written Ajax requests anymore!
You know, when you create a Javascript function that sends a POST request to your server and you get a nice "Invalid Authenticity Token" error message in return.
The solution is to add the token to your parameters or to disable CSRF attacks check in your controller using protect_from_forgery :except => :my_action.
This will not be required anymore with Rails 3!

The touch method has also been added to ActiveRecord models.
When called, it will save the record with the updated_at attribute set to the current time.

It can also be used as an option with the belongs_to association. So when the current instance is saved/destroyed, the associated object will be touched too.

belongs_to :company, :touch => true


If you follow the evolution of the Rails codebase you've probably been surprised by setup, depends_on and use keywords. You can see an example in this commit.

The source code for can be found in ActiveSupport (activesupport/ lib/active_support/core_ext/module/setup.rb) :


class Module
attr_accessor :_setup_block
attr_accessor :_dependencies

def setup(&blk)
@_setup_block = blk
end

def use(mod)
return if self < mod

(mod._dependencies || []).each do |dep|
use dep
end
# raise "Circular dependencies" if self < mod
include mod
extend mod.const_get("ClassMethods") if mod.const_defined?("ClassMethods")
class_eval(&mod._setup_block) if mod._setup_block
end

def depends_on(mod)
return if self < mod
@_dependencies ||= []
@_dependencies << mod
end
end


A cleaner way to organize modules inclusion and dependencies. Yehuda Katz wrote an article about that.

Add jQuery datagrids to your Ruby on Rails applications



Written by Anthony Heukmes on Mon Apr 13 14:53:33 UTC 2009

154 comments



It's now possible to add jQuery datagrids to your Ruby on Rails applications with just a few lines of code.

Have a look at the demo application of my new plugin : 2dcJqgrid.

Datagrids are based on a jQuery plugin and support the following features :

- Ajax-enabled
- Pagination
- Sorting
- Filters
- Multiple selections
- Master-Details
- Data manipulation (create, edit, delete)
- And more ...