This gist referred to by the Devise wiki is no longer accurate as far as I can tell. So I wanted to share my approach for how to use devise in a situation where user documents are embedded in account documents, especially in the scenario where your account has a subdomain assigned to it.
Obviously, the first thing you need to do is make sure you always have access to the current account as keyed by the subdomain. This means a before filter on any controller that runs under the subdomain that loads in your account. The idea is that once you load the account, you never have to pull it from the database again:
class AccountSubdomainController < ApplicationController
before_filter :current_account
protected
def current_account
@account ||= params[:current_account] ||= get_account_by_subdomain
params[:user][:current_account] = @account if params[:user]
@account
end
def get_account_by_subdomain
Account.where(:subdomain => request.subdomain.downcase).first
end
end
If you're not using an account-specific subdomain, just modify this to pull out the account from the URL or something.
Read more...
One of the great things about embedded documents in MongoDB is that you can design your "schema" according to how you're going to use the data. Ordered lists of objects is a great use for embedded documents, as you can just shove objects in an array and read them out in order. This allows one to dispense with the unpleasantness of "acts_as_list"-style approaches where you have to juggle a "position" field and do an explicit sort.
But what if you want to reorder the embedded documents? Should be simple to sort an array. Our ODM - Mongoid in this case - would never represent the embedded collection as an array and not let us work with it as an array, right?
class Container
include Mongoid::Document
embeds_many :items
end
class Item
include Mongoid::Document
embedded_in :container
field :title, :as => String
end
c = Container.create
c.items.create :title => "first"
c.items.create :title => "second"
>> c.items
=> [#<Item _id: 4dd2d971322bcdab7c000003, title: "first", _id: BSON::ObjectId('4dd2d971322bcdab7c000003'), _type: nil>, #<Item _id: 4dd2d981322bcdab7c000004, title: "second", _id: BSON::ObjectId('4dd2d981322bcdab7c000004'), _type: nil>]
>> c.items.reverse!
=> [#<Item _id: 4dd2d981322bcdab7c000004, title: "second", _id: BSON::ObjectId('4dd2d981322bcdab7c000004'), _type: nil>, #<Item _id: 4dd2d971322bcdab7c000003, title: "first", _id: BSON::ObjectId('4dd2d971322bcdab7c000003'), _type: nil>]
>> c.save
=> true
>> c.reload.items
=> [#<Item _id: 4dd2d971322bcdab7c000003, title: "first", _id: BSON::ObjectId('4dd2d971322bcdab7c000003'), _type: nil>, #<Item _id: 4dd2d981322bcdab7c000004, title: "second", _id: BSON::ObjectId('4dd2d981322bcdab7c000004'), _type: nil>]
OK, so not that easy, but maybe this means we just need to set the new array explicitly.
Read more...
Treeoid
Tree hierarchies for your Mongoid::Document objects
I'm a bit late mentioning this, but I released another super-beta gem in the hopes it might help another poor soul: treeoid, the missing "acts_as_tree" library for mongoid. It couldn't be simpler, really: it gives you a "parent" accessor and a "children" collection. On top of that, it provides a scope allowing you to list a set of treeoid objects in hierarchical order, which is perfect for front end integration.
The tests are there but nominal; I'd love to see them fleshed out. I also had some ideas for making it cooler; for example, I keep an array of an object's descendants in the object, allowing me to hierarchically order objects. This opens up some novel means to simplify how I implement the parent and children accessor. Imagine this:
field :ancestry, type => Array # contains ids of all ancestors including self, already exists
# but instead of a parent_id accessor
def parent_id
ancestry.at(-2) # the parent can be fetched from the ancestry list
end
This also allows all descendants of a given object to be easily fetched - if the id shows up in the ancestry, return it! It's this kind of out-of-the-box thinking that has really endeared MongoDB to me. I hope you can benefit from this and help me improve it. Or help with greedy. I'd love to get said help at CVREG's upcoming Why Day hackfest.
Over the last week I've started a project with Rails 3 and I'm impressed. The increased configurability of the framework has not diminished its ease of use nor its core concepts in the slightest. You'll have to get used to a few new conventions, especially regarding routing, but there's lots of help out there.
Since this project is something I'm doing in my off time, I decided to experiment with MongoDB using the Mongoid framework. I had played with MongoMapper before, but always felt like I was using an ActiveRecord clone that didn't take advantage of the full capabilities of a document database and was forcing and ActiveRecord-style approach on me. With Mongoid you get has_many, has_one, and belongs_to relationships that map to MongoDB concepts like embedded documents. Mongoid is fully compatible with the ActiveModel interface for Rails3, and things like associations and nested attributes work out of the box.
I also had been hearing great things about CarrierWave from co-workers. It employs the concept of an "uploader" outside of the MVC ecosystem. The uploader handles resizing, storage, and all other details. In your model, you simply "mount" the uploader and you're golden. Of course, for this project the killer feature is the GridFS storage option, which is something I wanted to play with.
Read more...