There is no one 'correct' way to do this. Both of your suggestions will work. But I'd agree with your instinct that they both feel a little bit hackish.
One solution would be to specify a different controller to handle users scoped inside groups:
resources :groups do
resources :users, :controller => 'group_users'
end
Then the concept of creating a user inside a group, and the concept of creating a user, can be handled in completely separate controllers.
The problem with this is that you'll be passing parameters like :user => {:name => "Bob Smith", :email => "bob@example.com", …} to your UsersController, while passing parameters like :user_id => 2 to your GroupUsersController. It feels like theres some inconsistency there.
So you could invent a new concept, called a 'membership':
resources :groups do
resources :memberships
end
When a user joins a group, you're not really creating a user -- you're creating a membership. When a user leaves a group, you're not really deleting a user -- you're deleting a membership.
Note that a resource doesn't have to match one-to-one with a database-backed model. So you can expose this concept of a membership at your controller level, and let the controllers deal with converting that to model talk:
POST '/groups/1/memberships', {:membership => {:user_id => 2}}
class MembershipsController < ActionController::Base
def create
@group = Group.find(params[:group_id])
@user = User.find(params[:membership][:user_id])
@group.users << @user
redirect_to wherever
end
end
Like I say, there isn't a 'correct' way of handling this kind of thing, but I personally like the idea of exposing the semantics of your application like this, and trying to think of things in terms of CRUD where possible.
Chris