Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I have to model User and Board in rails using mongoid as ODM.

Each board is referenced to an user through a foreign key user_id and now I want to add the ability to share a board with other users.

Following CRUD I'd create a new Model called something like Share and it's releated Controller with the ability to create/edit/delete a Share but I have some doubts:

  1. First, where to save informations about Shares? I think I may create a field in the Board's collection called shared_with including an array of user ids. in a MySQL I'd created a new table with the ids of who share, the resource shared and the user the resources is shared with but I don't think that's necessary using MongoDB.
  2. Every user a Board is shared with should be able to edit the Board (but not to delete it) so the Board should have two relations one with the owner and another with the users the board is shared with, right?
  3. For permission (the owner should be able to delete a board but the users it is shared with shouldn't) what to use? I'm using Devise for authentication but I think something like CanCan would fit better. but how to implement it?

What do you think about this way? Do you find any problems or have better solutions?

share|improve this question
add comment

2 Answers

up vote 3 down vote accepted
+150

I generally agree with your solution and I would do the same, i.e. create a separate model called Share, even though it looks like a RDBMS-like solution.

But let's step back and review the available options.

First of all, consider the relations of your models. Draw them on a piece of paper and note the type of relations and their associations (belongs to, has one, has many, has many through or has_and_belongs_to_many). Perfect reference for the associations in rails is http://guides.rubyonrails.org/association_basics.html. Take into account that Mongoid also offers embedded_in/embeds_many type of relations.

In your case the relations would be: User (1..N) Boards for owning, User (N..N) Boards for sharing.

The hint to decide whether to use has_many, has_and_belongs_to_many or embeds_many is whether or not the subordinate model represents semantically embedded type of relation and is not connected with other models than its parent. It it is, use embeds_many, otherwise use has_many or has_and_belongs_to_many.

The hint to decide whether to use has_many :through or has_and_belongs_to_many is whether you are going to add more data to a linking model (Share in your case).

has_and_belongs_to_many in mongoid is doing without creation of a linking model, i.e. board_ids would be kept as an array in the User model and user_ids would be kept as an array in the Board model.

The reason to not use HABTM in your case is simple. You have two relations between your User and Board models (sharing and owning), you would have difficulty in figuring out what expression like user.boards means (shared boards or owned boards). I don't know of an easy way to separate these two kinds of relations without use of a third model (Share) as you proposed in comments (using :as, etc.)

You can also manually keep the ids of boards in your user model, like this:

class User
  include Mongoid::Document
  field :name
  key :shared_board_ids, Array, :index => true
  key :owned_board_ids, Array, :index => true

  def shared_boards
    Board.find shared_board_ids
  end
  # ...

end

However, difficulties in this case arise pretty fast. You have to manually destroy all board references from the User model when you destroy a board. If you keep a sharing_user_ids in your Board model, you have redundancy and another set of difficulties.

So, yes, I would recommend creating a separate Share model, as I show below.

class User
  include Mongoid::Document
  field :name
  has_many :shares
  has_many :boards
end

class Board
  include Mongoid::Document  
  field :title
  has_many :shares
  belongs_to :user
end

class Share
  include Mongoid::Document  
  belongs_to :user
  belongs_to :board
end

As of permissions, CanCan is simple, but it is doing just fine. Notice a difference between terms authentication (which checks the authenticity of a user, in other words checks that the user is really who he claims he is - normally using password) and authorization (which checks the rights of the user). Authentication is handled by gems like Authlogic, Warden (and Devise based on Warden), Sorcery. Authorization is handled by Declarative_authorization or CanCan.

share|improve this answer
    
I'm using mongodb here not an sql database:) –  Matteo Pagliazzi Apr 14 '12 at 16:14
    
oh and an user owns many boards not only one! –  Matteo Pagliazzi Apr 14 '12 at 16:44
1  
have a look at stackoverflow.com/questions/7000605/…. This explains a vital difference between embedded and associated relationships. –  Alexei Danchenkov Apr 14 '12 at 19:47
    
what's wrong with: -each board belongs_to an user -each user has_many boards and that works PERFECTLY. –  Matteo Pagliazzi Apr 15 '12 at 12:53
    
The problem is with the sharing parts, I think I can do so: –  Matteo Pagliazzi Apr 15 '12 at 12:53
show 5 more comments

I think you might find better answers on stackoverflow. I'm not an expert in RAILS or mongodb, but I'll try.

  1. Creating a field in the Board's collection would probably work OK.

  2. You might want to make a single relation collection, and have a type added to the relation. That way you could have multiple owners if you wanted, and you could add more types later, like a 'read only' or a blocked.

  3. Check the type and render views based on whether or not they are shared to and whether or not they are an owner. I don't think you need a permissions framework for this, but that doesn't mean it's a bad idea to put one in.

Hope that helps!

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.