I have some questions about Has Many Through and how to manage a
couple of things that keep cropping up. I’m reasonably new to rails,
having come from a C# background and I have to admit I’m having some
trouble getting my head around models. Anyway this is my second
project in rails and it’s a little more complex than the first. I’m
trying to do things the ‘rails way’ but I’m running into a few
problems.
So, let me start off with some background. We have a few tables that
enjoy various has-many (and has-many-thorugh) relationships.
To start off with we have an ‘owners’ table (these are essentially
users but we call them owners for various reasons)
Owners have the usual stuff, username, password, first_name,
last_name, etc.
We also have ‘notebooks’. These notebooks have little information,
just a name and are used to hold notes (duh!), but more on that later.
Owners have many notebooks through a table called
‘notebookshares’ (man I hate that name). Notebookshares carry
information about the owners relationship to the notebook,
so on top of notebook_id and owner_id the notebookshares table
contains a field called ‘access’. This field can be one of three
values; ‘readonly’, ‘full’, ‘owner’. If it’s ‘owner’ then that user
created the notebook and owns it and everything in it. They can then
share notebooks they own with others, which is where ‘readonly’ and
‘full’ come in. If I have ‘readonly’ access to a notebook then I’d
somewhat restricted on what I can do, if I have ‘full’ access then I
can do everything to the notebook but share it with others.
Finally notebooks have many notes, but notes only belong to one
notebook. Notes simply contain some text, the date/time they were
created and last updated.
Essentially it all looks like this:
class Owner < ActiveRecord::Base
has_many :notebookshares, :dependent => true
has_many :notebooks, :through => :notebookshares do
def find_editable()
find(:all, :conditions => “notebookshares.access <>
‘readonly’”)
end
def by_share_type(share_type)
find(:all, :conditions => ['notebookshares.access = ?',
share_type])
end
end
etc…
end
class Notebookshare < ActiveRecord::Base
belongs_to :owner
belongs_to :notebook
end
class Notebook < ActiveRecord::Base
has_many :notes, :order => ‘position’, :dependent => :destroy
has_many :notebookshares, :dependent => true
has_many :owners, :through => :notebookshares do
def find_editable()
find(:all, :conditions => “notebookshares.access <>
‘readonly’”)
end
def by_share_type(share_type)
find(:all, :conditions => ['notebookshares.access = ?',
share_type])
end
end
etc…
end
class Note < ActiveRecord::Base
belongs_to :notebook
acts_as_list :scope => :notebook
etc…
end
So, if I have either ‘owner’ or ‘full’ access to a notebook (as
defined by the relationship between my owner_id and the specific
notebook_id) then I can edit any given note in that notebook. If I
only have ‘readonly’ I can view the notes but can’t really manipulate
them in any way.
One of the things that keeps coming up is the ‘access’ level of a
particular note or notebook. For example, if I’m trying to delete a
note I need to know if I have the right to do so.
For example something like this would be great (which would be found
in the notes controller):
def destroy
@owner = Owner.find(session[:owner_id])
@note = @owner.notes.find(params[:id])
@note.destroy unless @note.access == 'readonly'
end
Similarly this information would be fantastic for notebooks, so
something like
#where @owner is an Owner object that was retrieved previously
if @owner.notebooks.find(params[:id]).access == ‘readonly’
blah...blah...blah
end
Now, I think it would have to be owner driven since the access an
owner has on a notebook is what defines what they can do to the
notebook and subsequently to the notes in that notebook.
I suspect that we could also have something like
Notebook.find(params[:id]).access(@owner) since we would then know the
owner.id and the notebook.id but I kind of like the first way better.
So, I have a few questions:
-
I know it’s possible to do something like
@owner.notebook.notes.find(params[:id]) but is it possible to do
something like @owner.notes.find(params[:id]) ?
If it is I certainly haven’t been able to work out how - perhaps
I’m missing something. -
If it is possible to do 1 (or even if it isn’t and you have to do
the longer and less elegant @owner.notebook.notes.find(params[:id]))
is it possible to add a function to the note model that would return
the access for that given note, or even add an accessor that get
populated after the note is retrieved.
I suspect that the difficulty would be in determining if the owner was
present, because it would also be possible to do something like
Notes.find(params[:id]) which means that we wouldn’t know who was
looking for the note and therefore what kind of access level they had.
Perhaps in this case the access function would return nil.
- Similarly for 2 above how would you go about writing a function for
Notebook that returned the access level for the supplied owner using
something like
Notebook.find(params[:id]).access(@owner)
and similarly could something like
@owner.notebooks.find(:all)
prepopulate an access accessor for the notebook such that we could do
something like
foreach @owner.notebook.find(:all) do |notebook|
puts notebook.name unless notebook.access == 'readonly'
end
Thanks for any help on this subject - even a link to something similar
would be appreciated.