Rails 3 Resource routing for join models - ruby-on-rails

I have a many-to-many relationship between users and teams (as a has_many :through), and I'm trying to setup a "team members" join model as a resource in the routes.
In my layouts I've setup a "team context form" that sets a session variable for the current_team, and I want the route for the team_members resources to be defined as /team_members/:user_id/show. Is there any way to do this with the resources :team_members in routes.rb?
I've tried using :path_names => {:action => "\some\url"}, however for actions that require an :id the router appends the route to be something like "\:id\some\url"

edit:
If you want to be able to edit the team membership, you could have
resources :users do
resources :team_members
end
and then, to edit the membership => /users/:user_id/team_members/:id/edit
And then you can do whatever you want in the team_members_controller.
Or as numbers1311407 said, just resources :team_members and you'll have all the rest routes to work with the team memberships.
Really don't want the standard /teams/:team_id/users/:id ?
If you really want /team_members/:user_id/show
You could just do
get "/team_members/:id/show" => "users#show"
But I dont think it's a good idea.

I wonder if what you're looking for is:
resource :team_members do
resources :users
end
The "resource" command creates a route where team takes no :id and would allow you to look up the team using your current_team session variable.
You'd get these path in your app:
/team_members # team_members#show
/team_members/users # users#index
/team_members/users/:id # users#show
In each case you're responsible for looking up current_team.

if the relationship is many-to-many then the route you're looking to write doesn't reference the team, unless this show page is intended to show all teams a user belongs to?
This would work out of the box if you assigned an ID to the join model and simply used its natural GET route, e.g. /team_memberships/:id
Edit: sorry I didn't read the 2nd paragraph carefully, if you are storing the team in the session you could (as suggested by someone else) set up team_members as a singleton resource and pull the team from the session when getting the member.
If it works in the app, though, considering team_membership as its own resource is probably more naturally RESTful.

Related

How to organize similar routes for multiple Rails users?

Let's say I have an Accountant < User model, and a Worker < User model. They both need to have pages like 'Settings', 'Dashboard', etc.
Right now the paths are assigned and explicitly defined in routes.rb:
resources :accountants
get '/accountant/dashboard' => 'accountant#dashboard'
get '/accountant/dashboard/:date' => 'accountant#dashboard'
get '/accountant/settings' => 'accountant#settings'
resources :workers
get '/worker/dashboard' => 'worker#dashboard'
get '/worker/dashboard/:date' => 'worker#dashboard'
get '/worker/settings' => 'worker#settings'
Saving the "home" dashboard path in a session / as application level helper methods which rely on current user class both don't seem very Ruby-esque. Is there an alternative to this in Rails 4?
The better way for this situation is NameSpace, Rails provide us something called namespace and you can use it in the routes for generate different routes for different views in your case maybe works something like that:
namespace :accountants do
get 'dashboard'
get 'dashboard/:date'
get 'settings'
end
namespace :workers do
get 'dashboard'
get 'dashboard/:date'
get 'settings'
end
and that will generate a routes like that:
localhost:3000/accountants/1/dashboard
localhost:3000/workers/1/settings
it's just and example you always can read the official documentation about it, but it's a good way for organize your different routes thinking in the scalability.
Another option is using roles to manage the different users you have because of you extend of user model is not scalable with the time and it's going to be a little confuse in the future read this code
Regards !

Rails 4: shallow resource not working with collection in RESTful routes

I have a nested resource as such:
resource :user, controller: :users do
# code ...
resources :profile, controller: :profiles, shallow: true do
# code ...
collection do
get :featured
end
end
end
The focus is the featured action of profiles.
The URL this generates is /user/profile/featured. I don't understand this because I specified shallow: true, yet it's still being nested under user/. I want the URL to be /profile/featured instead.
If I just do get :featured instead of putting it in a collection, I get /profile/:id/featured, which is also not what I want.
There are two types of routes within a given resource: collection routes (i.e. routes on the collection), and member routes (i.e. routes on individual records). When you specify that a nested route should be shallow, it only keeps routes nested that MUST be nested, and everything else is un-nested. Let's simplify your routes a smidge so we can talk through it better:
resources :users do
resources :profiles, shallow: true
end
If you take a look at the routes generated by that, you'll notice that the only profile-related routes nested under the user are:
GET /users/:user_id/profiles: Obtain a list of all profiles for this user
GET /users/:user_id/profiles/new: Render the page to create a new profile for this user
POST /users/:user_id/profiles: Create a new profile for this user
Everything else is no longer nested under users. Notice what all of these have in common: for this user. This comes back to what I said earlier: Rails only nests what it must. The user is an important part of the equation here, and there's no other way that it can be identified (without manual work on your part, anyway). However, once we have a record, we have a profile ID we can operate on. We no longer care about the user, and thus we don't need nested routes anymore.
In general, Rails expects that, since you've implied that profiles belong to users, it makes the most sense to only generate routes for operating on the collection within the scope of the user to which they belong. In other words, in most cases, you don't care about fetching the entire collection of profiles, you only care about the profiles for a particular user.
As a result, collection routes remain nested. On the other hand, for member routes we have a profile ID to work with, so they are not nested.
In your case, you're trying to do something a bit out of the above-described ordinary, so you'll need to create such routes yourself. Hopefully that explains the behavior you're seeing, though.
A final note, in case anyone notices: I used the plural resources here, where you actually used resource. It doesn't actually matter-- resource implies that there's a single user to operate upon when it sees a /user route, which is equivalent to the case with resources when it sees a /users/:user_id route. I used the plural form because I find it a little easier to understand.

Rails nested resources, how do I create two new resources when I need the other's id in the GET request?

Pretty much as the title suggest. I have two models, routes like this:
resources :users do
resources :books
end
- which gives me exactly the kind of urls I'm looking for. The problem is that I need to have a logic where a not logged in user can go in and click "create book", and in the new-page chose to click "new user" or "I have an account", which via javascript loads a form for a new user with all its params, or loads a form with only email and password. How do I create this route to work? With these routes it seems I get the path /users/:user_id/books/new, but then I need the user's ID, which I don't have.
Any Ideas?
By the way. User has_many :books, Book belongs_to :user.
Just add the route without the user id:
# with user_id
resources :users do
resources :books
end
# without user_id
resources :books
It looks dangerous btw to have a user id in the URL. That means that anyone can access the the books of any user, just by changing the URL. The ID of the current user should not appear in your routes at all, but should always use cookies or sessions.

Find path of a nested or non-nested resource

Working in Rails 3.2, I a polymorphic Subscription model whose subscribable_type may or may not be a nested resource. I'm trying to display the full URL link in an email view, but have no knowledge whether or not that resource is nested.
When I try url_for #model on a nested resource, it fails, expecting url_for [#parent, #model]. Unfortunately, I do not know how to discover the parent as defined in the Routes table.
Is there a way to identify the route path for a nested resource? If I could match the model to a route, I could fill in the necessary IDs.
As of right now, I've defined a method in my models called parent_resource :model that can be traversed, but I'm hoping there's a better way.
Within my routes.draw:
resources :projects do
resources :topics do
resources :comments
end
end
resources :subscriptions
(I realize I shouldn't be nesting so deeply)
Edit: Additional Information
My Subscription model is a resource I use to manage notifications. Subscribable types are provided a link that toggles the subscription for that user on that subscribable_type / subscribable_id on or off.
I then go through a Notifier < ActionMailer::Base which is provided the Subscription instance, and mail the user.
Through that setup, I'm trying to get the full url of subscription.subscribable which may be a Topic or a Project.
I realize that I could hammer out the conditions in this small case through a helper method, but I am curious to know how one would approach this if there were dozens of nested model pairs.
You mention subscription but your routes are completely different. I'm guessing the routes you gave were just an example then. I would start with trying to get rid of the custom parent_resource method you created. You can probably do the same thing simpler with adding a belongs_to through and maybe with conditions if you need too:
belongs_to :projects, :through => :topics, :conditions => ['whatever your conditions are']
I'd have one of those per parent type so I can do things like:
object.project.present?
And from there I could easily know if its nested or not and simplify things by letting rails do the parent traversal. That ought to simplify things enough to where you can at least figure out what type of subscription you have pretty easily. Next, I'd probably add some matched routes or try to cram an :as => 'somename' into my routes so I can call them directly after determining the nested part. One option would be something like this:
match "projects/subscription/:id" => "projects#subscription", :as => :project_subscription
match "other/subscription/:id" => "other#subscription", :as => :other_subscription
And so its pretty obvious to see how you can just specify which url you want now with something like:
if #object.project.present?
project_subscription_path(#object)
else
other_subscription_path(#object)
end
This may not be the best way to accomplish what I'm doing, but this works for me right now.
This builds a nested resource array off the shortest valid route helper and generates a URL:
(Tested in rails console)
resource = Comment.first
resource_name = resource.class.to_s.downcase
helper = Rails.application.routes.named_routes.helpers.grep(/.*#{resource_name}_path$/).first.to_s.split('_')
built = helper.slice!(-2,2) # Shortest possible valid helper, "comment_path"
while !(app.respond_to?(built.join("_").to_sym))
built.unshift helper.pop
end
built.pop # Get rid of "path"
resources = built.reverse.reduce([]) { |memo, name|
if name == resource_name
memo << resource
else
memo << memo.last.send(name.to_sym) # comment.topic, or topic.project (depends on belongs_to)
end
}
resources.reverse!
app.polymorphic_url(resources) # "http://www.example.com/projects/1/topics/1/comments/1"

Rails RESTful website vs user experience and less clicks

Situation: I have a team model, a user model and a teamate model for the users of a team.
Say I want to have a view that contains the information of a team team/show
and that I wish (to simplify the user's experience) to add a list of the users, an add user to team and the possibility to remove a user from that team.
To be perfectly restful, I would need a controller (let's call it Teamates), it would handle the users of a team.
I would have all the CRUD needed.
Is it clean to have the team/show view call the teamates controller for the following actions: adduser, removeuser, listusers.
What I am trying to achieve is less clicks for the users.
In other words, I would like the user to be able to manage the users of a team from the team view instead if requireing him to navigate even further.
I don't think you need a controller for teamates.
And you really should not have adduser/removeuser/etc actions in your team controller!
You could set up your routes like that:
resources :teams do
scope :module => "team_scope" do
resources :users
end
end
Then you would have a UsersController in app/controllers/team_scope/users_controller.rb
To create a new user for a team, you would post to: /team/1-team-a/users and it would hit the create action in the UsersController above.
When you use scope in your routes, it does not change the route helpers like with namespace. The new action would just be accessible via new_team_user_path(#team).
Hum... so yeah, in this case I would have a TeamatesController, and maybe set up my routes like that:
resources :teams do
resources :teamates, :only => [] do
collection do
get :edit
put :update
end
end
end
And then you could edit the associations between a team and its players...
Your form would post the users id to team_teamates_path(team)...
But I'm really not sure it's the best way, I'd have to think about it. This is not really restful as well.

Resources