0

I'm using Rails 5.0.0.1 and a custom authentication/authorization system (similar to Devise/CanCan).

I'm working on a Rails app and I'm looking for some advice on how to show/hide data based on whether or not a user is part of a "group" or "project".

The app: basically, users can "highlight" lines of a text article. They are first presented with a view of the article’s contents (i.e., an index of its lines). They can select a line to highlight. When a line has been highlighted, any user can click on it to bring up a comments section for that line.

Article has_many Lines
Line has_many Highlights
Highlight has_many Comments
User has_many Highlights
User has_many Comments

Highlight belongs_to User
Comments belongs_to Highlight
Comments belongs_to User

Now I would like to add "Group Project" functionality. A user selects an article to use in a Group Project. He invites other users to this project. Invited users can highlight and comment, but all the highlights and comments are only visible to users of that project.

My idea was to create ‘scope’. By default, for all articles, highlights, and comments, the scope is ‘public’. Any user can log in and view those articles, highlights, comments. But a user can also access one of his Group Projects and see (possibly the same) articles with ‘privately’ scoped highlights, comments, etc. This brings up the problem that a single Line can have multiple highlights and comments with different scopes. An article's line's highlights/comments have a single public version and multiple private versions.

My question is how can I efficiently deal with these multiple instances? Should I create new instances of the Lines for each project? This seems like unnecessary duplication in the database. Should I put some conditional in the controller or model to scope based on project_id or something similar?

I know there are multiple gems for dealing with "user groups", but they are typically used for authorization. I would like for a user to be able to create any number of projects, which each have their own invitees and highlights and comments.

RSH
  • 39
  • 1
  • 5
  • Devise isn't an authorisation framework - it's an authentication framework - don't confuse the two. It sounds to me like you do want authorisation of resources based upon the current user - you should check out CanCanCan which does provide this, it would work in conjunction with your authentication system. – David Dec 11 '16 at 13:11
  • Good point. I have an authorization system as well. What I want is authorization based not on current user alone but on the projects that the user belongs to. I.e., if user is looking at a project, display all the highlights and comments for that article associated with that project. – RSH Dec 11 '16 at 14:28

1 Answers1

0

You may add field project_id to highlights and comments tables and setup associations:

class User < ActiveRecord::Base
  has_and_belongs_to_many :projects
end

class Project < ActiveRecord::Base
  has_and_belongs_to_many :users
  has_many :highlights
  has_many :comments
end

class Line < ActiveRecord::Base
  has_many :comments
  has_many :highlights
end

class Highlight < ActiveRecord::Base
  belongs_to :project
  belongs_to :line
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :project
  belongs_to :highlight
  belongs_to :user
  belongs_to :line
end

Then you will be able to access all (say) highlights by

Highlight.all

all public highlights by

Highlight.where(project: nil)

all highlights of specific @line and @project by

@line.highlights.where(project: @project)

Also, in order to save db space and provide some flexibility, you may add belongs_to_id and belongs_to_type fields into comments table and update Comment model to something like

class Comment < ActiveRecord::Base
  def self.belongs_to(types)
    types.each do |type|
      super type, foreign_key: "belongs_to_id"
    end
    validates_inclusion_of :belongs_to_type, in: types.map { |type| "#{type.capitalize}" }
  end

  belongs_to [:project, :highlight, :user, :line]
end

# didn't test
kengho
  • 231
  • 1
  • 6
  • Thanks, this looks straightforward. I do also need to create a ProjectUser join table in order to use the associations, e.g., `User.first.projects`. – RSH Dec 12 '16 at 14:47
  • You're welcome. Correct, you may look for related migrations [here](https://stackoverflow.com/questions/6561330/rails-3-has-and-belongs-to-many-migration) – kengho Dec 12 '16 at 16:57