Topic: Help needed designiong RESTful app with restful_authentication

In my app I have the following:
=======
MODELS:
Network
has_many :groups

Group
belongs_to :group

User
USER can be a member of several Networks AND Groups.
=======
I tried to make this using many-to-many Network-User/Group-User , :through => memberships
but I don't know how to display groups and networks user belongs to. How to implement this in RESTful way, plaease?

Re: Help needed designiong RESTful app with restful_authentication

There are three ways you can do this. One way is to create a has_and_belongs_to_many association between networks/users and groups/users. like this:

class Network < ActiveRecord::Base
  has_many :groups
  has_and_belongs_to_many :users
end

class Group < ActiveRecord::Base
  belongs_to :group
  has_and_belongs_to_many :users
end

class User < ActiveRecord::Base
  has_and_belongs_to_many :networks
  has_and_belongs_to_many :groups
end


With that schema you would create a networks_users table and a groups_users table holding two columns (foreign keys) linking the two models.


Another way is to create a Membership model that has a polymorphic association to group/network. For example:

class Network < ActiveRecord::Base
  has_many :groups
  has_many :memberships, :as => :group_item
end

class Group < ActiveRecord::Base
  belongs_to :group
  has_many :memberships, :as => :group_item
end

class Membership < ActiveRecord::Base
  belongs_to :group_item, :polymorphic => true
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :memberships
end


With this way you need to create a "memberships" table with "group_item_id" and "group_item_type" columns. Unfortunately, it is not possible to do a has_many :through association with this because the table that needs to be joined (groups/networks) is dynamically based on the group_item_type column. In other words, it is a limitation of SQL.


And finally, the third way to do this would be create two new models: GroupMembership and NetworkMembership and do two has_many :through relationships.

class Network < ActiveRecord::Base
  has_many :groups
  has_many :network_memberships
end

class Group < ActiveRecord::Base
  belongs_to :group
  has_many :group_memberships
end

class NetworkMembership < ActiveRecord::Base
  belongs_to :network
  belongs_to :user
end

class GroupMembership < ActiveRecord::Base
  belongs_to :group
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :network_memberships
  has_many :networks, :through => :network_memberships
  has_many :group_memberships
  has_many :group, :through => :group_memberships
end


Obviously this way you would create two tables: group_memberships and network_memberships which link the models.


It's hard to say which way is the right way to go. It depends on how you want to interact with the relationship.

Railscasts - Free Ruby on Rails Screencasts

Re: Help needed designiong RESTful app with restful_authentication

If he wants to develop a RESTful application, he should go with the last one. This way it's clear where to put methods like "add a user to a group".

red

Re: Help needed designiong RESTful app with restful_authentication

Good point. That one probably is the best way to go for a fully RESTful design.

However, I think it depends largely on how he wants the interface to behave. If for example you want checkboxes listing the networks/groups on the user edit form, you would want to go with the first approach (has_and_belongs_to_many).

Railscasts - Free Ruby on Rails Screencasts

Re: Help needed designiong RESTful app with restful_authentication

Yeap, I want to display groups and networks current_user in.
There is a list of groups and networks and user can check with checkbox which network/group he'd like to join.
But developing in RESTful way it's not a good to use habtm.
This was a nice try:

class Group < ActiveRecord::Base
  belongs_to :network
end

class Network < ActiveRecord::Base
  has_many :groups
end

class User < ActiveRecord::Base
  belongs_to :network, :through => :network_memberships
  belongs_to :group, :through => :group_memberships
end

class NetworkMembership < ActiveRecord::Base
  belongs_to :network
  belongs_to :user
end

class GroupMembership < ActiveRecord::Base
  belongs_to :group
  belongs_to :user
end


Is it ok ( is it restful enought? wink )?
In that case I have some problems with the following:
How to receive current_user.networks and current_user.groups from a controller?..

Re: Help needed designiong RESTful app with restful_authentication

If you want to go with NetworkMembership and GroupMembership models, use this schema.

class Network < ActiveRecord::Base
  has_many :groups
  has_many :network_memberships
end

class Group < ActiveRecord::Base
  belongs_to :network
  has_many :group_memberships
end

class NetworkMembership < ActiveRecord::Base
  belongs_to :network
  belongs_to :user
end

class GroupMembership < ActiveRecord::Base
  belongs_to :group
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :network_memberships
  has_many :networks, :through => :network_memberships
  has_many :group_memberships
  has_many :group, :through => :group_memberships
end


Then you should be able to call current_user.networks and current_user.groups.

Railscasts - Free Ruby on Rails Screencasts

Re: Help needed designiong RESTful app with restful_authentication

Thank you, everything works fine.
When the User wants to join some group how can I implement this? (Also, I have joined_on column in group_memberships and network_memberships) I think it ca\n be something like /groups/4;join ?..
Also, when I have

<%= f.select(:group, :network_id, Network.find(:all).collect {|n| [ n.name, n.id ] }, { :include_blank => true }) %>

to my new/edit group form, I'm getting
undefined method `merge' for []:Array

NoMethod error. Was it deprecated?

Last edited by Vlad (2007-01-25 04:29:47)

Re: Help needed designiong RESTful app with restful_authentication

If you want a user to join a group this is the same as creating a GroupMembership model. If you want to take the restful approach you would create a GroupMembershipsController with a "new" and "create" action just like other controllers. Pass the user_id and group_id you want to join to this method and you should be good to go. If you need some code I can probably whip it up.

As for the "select" method not working, here you are using form_for (I'm guessing) so you don't need to define the ":group" at the beginning for the select. This is offsetting the parameters.

<%= f.select(:network_id, Network.find(:all).collect {|n| [ n.name, n.id ] }, { :include_blank => true }) %>

collection_select is a better way to do this though:

<%= f.collection_select :network_id, Network.find(:all), :id, :name, :include_blank => true %>

Railscasts - Free Ruby on Rails Screencasts