Two different show views for restful resource in rails - ruby-on-rails

I have a job app with two kinds of (not-authenticated) users, referrers and candidates.
Currently I have 1 jobs controller and model.
Basically, I want to have two routes:
job/1 # jobs#show for referrers
j/1 # jobs#show for candidates
Both routes are public, there are no logged-in users.
While the model data (=content) is very similar for the two routes, the views are obviously different. I am struggling to create two different show views within the same controller.
I have looked at setting up a separate namespace (seems to be for the entire controller), specific get routes or setting up a separate controller, but really not sure what the best "Rails" way is.
Any suggestions would be really helpful, thanks in advance!

I see 2 options:
1) Create a separate action in your controller
def referrers
end
def cadidates
end
get '/j/:id' => 'jobs#cadidates'
get '/job/:id' => 'jobs#referrers'
This requires two new views with the actions. In your job views folder add two files: candidates.erb and referrers.erb. You can there adjust the view for each.
2) You can nest the resources
resources :job do
member do
get :referrers, :candidates
end
end
You would have to define a new model and create joint tables where both referrers and candidates are defined as users but part of different table referrers and candidates respectively.

In your JobsController create two different methods. Say
def referrers
end
def cadidates
end
Now in your routes.rb add newly created methods as like,
get '/j/:id', :to => 'jobs#cadidates'
get '/job/:id', :to => 'jobs#referrers'

Related

Managing Multiple Hierarchical Rails Models Interlinked to Multiple Levels

I am building a To Do application but with multiple levels of tasks and subtasks. I have been following Ruby on Rails Tutorial by Michael Hartl and almost everything I have learnt till now is from there.
My app has multiple hierarchy models. What I mean is that at the top is the User, a user can have multiple goals, each goal can have multiple tasks, and each task can have multiple subtasks.
Just to give you a more clear idea, I'll list down the hierarchy
User
Goals (multiple goals per user)
Tasks (multiple tasks per goal)
Subtasks (multiple subtasks per task)
Now, when I created the models, I included a dependency (has_many/belongs_to) between goals and users, but I also did the same for tasks and users and subtasks and users. The reason for this is that I wanted an 'id' column in all tables that corresponds to the user table so that I can list down all subtasks/tasks/goals of a user very easily.
Coming to the front end, I need forms that are capable of adding goals/tasks/subtasks.
The relation between users and goals is not problematic at all. The has_many/belongs_to relation allows me to user something like
def create
#goal = current_user.goals.build(path_params)
if #goal.save
redirect_to current_user
else
.
.
end
end
However, the problem arises when I have 3 levels of hierarchy. User, goals and tasks.
My question is that how exactly do I do this for the task controller?
I tried using something like
#task = current_user.goals.tasks.build(task_params)
but that has the obvious flaw apart from probably being syntactically incorrect : there is no way to detect what particular goal this task is being assigned to.
What I can think of is that the front end form must contain a field (perhaps hidden) that contains the goal ID to which the task is being assigned. But not entirely sure is this is correct as well or how if a front end gimmick like that can actually help in case there are even more levels of hierarchy.
Please tell me where I am going wrong / whats a better way to do this.
I am only a beginner right now so I am very confused on how multiple level model hierarchies are managed.
What I can think of is that the front end form must contain a field (perhaps hidden) that contains the goal ID to which the task is being assigned.
More or less. A better way is to have RESTful resources here. So you'll have this structure of nested resources:
# routes.rb
resources :users
resources :goals do
resources :tasks do
resources :subtasks
end
end
This means that to create a task, you have to send a POST request to
/goals/1/tasks
This way, in your TasksController, you'll have params[:goal_id] and now you can do something like this:
class TasksController
before_action :load_goal
def create
#task = #goal.tasks.build(task_params)
...
end
private
def load_goal
#goal ||= Goal.where(id: params[:goal_id]).first
end
end
Repeat for other levels of hierarchy.
As for the propagating user_id down the line, you can do something like this:
class Task
belongs_to :goal
before_save :copy_user_id_from_parent
def copy_user_id_from_parent
self.user_id = goal.user_id
end
end

Rails: Canned/RESTful way for accessing data returned by a method of a model

In my app I have a User model which defines a history method that returns a list of Activity objects, showing the last N actions the user has carried out. The UserController#history method wires this with a view.
The code looks as follows:
class UserController < ApplicationController
def history
user = User.find(params[:id])
#history = user.history(20)
end
end
class User < ActiveRecord::Base
has_many :activities
def history(limit)
...
end
end
Naturally, I also added this line to my routes.rb file:
match '/user/:id/:action', :controller => 'user'
so now when I go to localhost:3000/user/8/history I see the history of user 8. Everything works fine.
Being a Rails NOOB I was wondering whether there is some canned solution for this situation which can simplify the code. I mean, if /user/8 is the RESTful way for accessing the page of User 8, is it possible to tell Rails that /user/8/history should show the data returned by invoking history() on User 8?
First of all the convention to name controllers is in the plural form unless it is only for a single resource, for example a session.
About the routes I believe you used the resources "helper" in your routes, what you can do is specify that the resource routes to users also has a member action to get the history like this
resources :users do
member do
get :history
end
end
I think there is no cleaner way to do this
You can check it here http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
As far as the rails standards are concerned, it is the correct way to show the history in your case. In rails controllers are suppose to be middle-ware of views and model, so defining an action history seems good to me.
And you can specify the routes in better way as:
resources :user do
get 'history', :on => :member #it will generate users/:id/history as url.
end

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.

Rails routing/controllers - listing subsets of collections

I have a blog with posts in multiple categories. I'd like to give each category an individual landing page that lists all of the bog posts in that category.
What is the appropriate way to generate the routes and controller actions for each of these landing pages? Would it violate the spirit of REST to create multiple index-esque actions (one action per category) in my posts controller? If so, how else should I do it?
For example, my blog might have two categories, "Music" and "Movies".
GET /posts/ # would list all posts.
GET /music/ # would list all posts in the "Music" category.
GET /movies/ # would list all posts in the "Movies" category.
Apologies if this question has an obvious answer, or if I'm asking the wrong question entirely. I'm new to both Rails and REST and I'm trying to understand the best way to structure applications.
I'm not sure it's totally in REST spirit (i do not fully understand it yet), so i'll leave that part of the question to someone else. As the collection method exists to extend RESTful routes, i assume that it is permitted as long as you don't abuse of it.
I don't think, though, that having routes with no "/posts/" prefix is a good thing, because it would induce that the "/music/" path for instance relates to a completely different resource.
you can do something like this :
(in routes.rb)
resources :posts do
collection do
get 'music'
get 'movies'
end
end
... and then add index-like actions to your controller, e.g.:
def music
#posts = Post.where( category: 'music')
render :index
end
if you have a limited and constant set of categories, this can be DRYed up this way:
class Post < ActiveRecord::Base
CATEGORIES = [:music,:movies,:art,:jokes,:friends,:whatever].freeze
end
class PostsController < ApplicationController
Post::CATEGORIES.each do |category|
eval <<-INDEX_LIKE_ACTIONS
def #{category}
#posts = Post.where( category: '#{category}' )
render :index
end
INDEX_LIKE_ACTIONS
end
end
resources :posts do
collection do
Post::CATEGORIES.each {|category| get category.to_s}
end
end

Multiple controllers with a single model

I'm setting up a directory application for which I need to have two separate interfaces for the same Users table. Basically, administrators use the Users controller and views to list, edit, and add users, while non-admins need a separate interface which lists users in a completely different manner. To do this, would I be able to just set up another controller with different views but which accesses the Users model?
Sorry if this is a simple question, but I've had a hard time finding how to do this.
Why not put the admin part into a separate namespace - you would have Admin::UsersController with views in app/views/admin/users/. And your users would go to UsersController with its own views in app/views/users/.
The routing is defined like this:
map.namespace :admin do |admin|
admin.resources :users
end
map.resources :users
And can be got to via admin_users_path and users_path

Resources