Skip to content
Mar 15 / John Wyles

Sessions in MongoDB using MongoMapper and Devise

I found that setting up MongoDB to be a session store using MongoMapper was, at first glance, non-obvious: that is, until I discovered devise. At first, I tried (and failed) to setup sessions in authlogic and then moved to the other end of the spectrum and attempted to role my own authentication and session store using this gist as a starting point. While it worked it felt wrong and I foresaw rewriting a ton of features that came for free with most authentication plugins. So, after an even more exhaustive search, I found devise; what a wonderful find! Nothing in devise said it wouldn’t work and I indeed found that to be the case; it was really agnostic to the session store and extremely simple to setup. Since devise is built atop warden.

So lets get started, first off you need to get the required gems and a skeleton app:

sudo gem install ruby-debug mongo mongo_ext mongo_mapper warden devise
rails myapp
cd myapp
rm public/index.html

Setup the environment.rb with some changes we will require:

# config/environment.rb

config.gem 'mongo_mapper'
config.gem 'devise'

Update the route for the home controller:

# config/routes.rb

# devise user definition
map.devise_for :users
map.resource :user, :o nly => [:new, :create, :edit, :update]

# root
map.root :controller => :user, :action => :sign_in

Generate the scaffold for the session and user model:

script/generate devise_install
script/generate devise User

Modify the generated user model:

# app/models/user.rb

class User
  include MongoMapper::Document
  devise :registerable, :authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
end

Sterilize the logs for sensitive information:

# app/controllers/application_controller.rb

filter_parameter_logging :password, :password_confirmation

Remove ActiveRecord from the environment:

# config/envrionment.rb

config.frameworks -= [ :active_record ]

Replace database.yml with the following:

# config/database.yml

development:
  adapter: mongodb
  database: myapp-development
  host: localhost

test:
  adapter: mongodb
  database: myapp-test
  host: localhost

production:
  adapter: mongodb
  database: myapp-production
  host: localhost

Add mongo_mapper YAML initializer:

# config/initializers/mongo_mapper.rb

# load YAML and connect
database_yaml = YAML::load(File.read(RAILS_ROOT + '/config/database.yml'))
if database_yaml[Rails.env] && database_yaml[Rails.env]['adapter'] == 'mongodb'
  mongo_database = database_yaml[Rails.env]
  MongoMapper.connection = Mongo::Connection.new( mongo_database['host'])
  MongoMapper.database =  mongo_database['database']
end

Enable mongo_mapper support for devise:

# config/initializers/devise.rb

Devise.setup do |config|
  config.orm = :mongo_mapper
end

You can now startup the server:

script/server -u

Now point your browser to http://127.0.0.1:3000/users/sign_up and sign up for an account.

Use the mongo client to get out the confirmation code:

mongo
> use myapp-development
> db.users.find();
{ "_id" : ObjectId("4c9e9118af5ee501860230004"), "encrypted_password" : "18cf5678ba4ef9d412c3b7bdea48794b7c69ec3a", "created_at" : "Mon Mar 15 2010 18:44:56 GMT-0700 (PDT)", "updated_at" : "Mon Mar 15 2010 19:18:41 GMT-0700 (PDT)", "confirmation_sent_at" : "Mon Mar 15 2010 18:44:56 GMT-0700 (PDT)", "last_sign_in_ip" : null, "current_password" : "abc123", "last_sign_in_at" : null, "sign_in_count" : 0, "password_salt" : "3xyzQfMwJeyAZa9tXYZ2", "remember_token" : "ArStvJ7ABzEkzE_feixT", "reset_password_token" : null, "current_sign_in_ip" : null, "remember_created_at" : null", "confirmation_token" :  "C0fuZMksMC7rhOgCxQef", "current_sign_in_at" : null, "email" : "user@example.com", "confirmed_at" : null }

You can now navigate to http://127.0.0.1:3000/users/confirmation?confirmation_token=C0fuZMksMC7rhOgCxQef and confirm the account. Once this is complete you can then goto http://127.0.0.1:3000/users/sign_in to verify that sign is working. Obviously this merely gets you a very bare bones rails app that talks to MongoDB and where I am going to leave it to you. Next steps you will probably want to take will be generating a root controller (e.g. home) and placing links to the sign_in and sign_up pages.

8 Comments

leave a comment
  1. Sedward / Mar 17 2010

    For your devise.rb initializer, shouldn’t that be Devise.setup do | config| …end?

    • John Wyles / Mar 25 2010

      Good eye! Fixed.

  2. Vasko / Apr 14 2010

    mongo_database['hostname'] should be mongo_database['host'], or you would need to change the database.yml to “hostname” instead of “host”.

    • John Wyles / Apr 14 2010

      Fixed! Thanks!

  3. Tyler / May 13 2010

    Hey great post, but I can’t seem to get it working on my own machine. I’ve followed the steps over and over and I always run into a problem when running the devise_install script (I get some sort of error with activesupport that says ‘uninitialized constant User (NameError)’. I’ve gone through almost every post explaining how to set up devise but I always get this some error and I feel like its something simple that I am missing. Would it possible to get a copy of the project you created in this post so I could see if it runs on my computer? Any help would be greatly appreciated! Thanks

    • John Wyles / May 16 2010

      You might checkout this guys example; I haven’t verified it works but you may find it has what you need:

      http://github.com/theshortcut/devise_mongomapper_example

    • Shannon W. / May 24 2010

      I had the exact same problem. On my machine, script/generate devise does not appear to like the ‘map.devise_for :users’ line or the ‘map.resource :user…’ line, as it appears to think the User model should already have been created. What worked for me was to do:

      % rails myapp
      % cd myapp
      % script/generate devise_install
      -> add the two config.gem lines to config/environment.rb
      -> add the root route to config/routes.rb
      % script/generate devise User

      You should see the following output:
      exists app/models/
      create app/models/user.rb
      create db/migrate
      create db/migrate/20100524223740_devise_create_users.rb
      route map.devise_for :users

      Unfortunately this way it’s still using ActiveRecord, even in the migrate script, so it looks like you’ll still need to do a bit of hand editing of routes.rb, the migrate script, and user.rb. Also the version of devise I am using already incudes a commented-out config.orm line for mongo_mapper.

      I’m on OS X 10.5.8 and the versions I’m using are:
      devise (1.0.5)
      mongo_mapper (0.7.6)
      mongomapper_ext (0.3.0)
      warden (0.10.5)

      • akhamutov / Jun 9 2010

        You should add lines to routes,rb after generate.
        And before generating you should add config.frameworks -= [:active_record] to environment.rb. In this case you can run
        script/generate devise User
        script/generate devise_install

        and it made no active record migrations.

Leave a Comment