Ruby on Rails: Finding Records using passed parameters - ruby-on-rails

I'm making an online magazine style website and am having difficulties getting the syntax right for my final part of the project. The relationships are working as they should I am just having trouble calling the intended records.
Each post belongs to a category with category_id being the foreign key. When a user clicks this link, <%= link_to 'News', categories_path(:category_id => 1) %>, I'd like for them to be brought to an index page showing only posts with a category_id matching the parameter in the URL.
I've been messing around in the categories_controller.rb for almost two hours now with no luck. Anyone be so kind as to throw this noob a bone?

There are a few components of what you're trying to do. We'll start with the routing side, and make our way to the controller.
First, you need to make the proper routes. Since the post belongs to a category, you will need to have the category id in order to handle performing any sort of operations on the posts. So we'd need a route like /category/:category_id/posts/:id. Luckily, Rails has something to handle this. If you nest a resources within a resources, it'll generate these routes. So, we end up with this:
resources :categories do
resources :posts
end
And that will get you what you want in terms of routes. But now we have to actually implement it. So, we're going to need to take a look at the controllers. If you notice, all of those routes have a :category_id - so looking up the category shouldn't be too difficult:
class PostsController < ApplicationController
before_action :load_category
private
def load_category
#category = Category.find(params[:category_id])
end
end
Now, you have the category loaded, and it shouldn't be too difficult to implement the other methods from there:
class PostsController < ApplicationController
before_action :load_category
def index
#posts = #category.posts
end
def show
#post = #category.posts.find(id: params[:id])
end
# ...
end
In order to reference the Post index path, you'll have to use category_posts_path helper.

Your problem is that you're trying to use an existing route to handle some new functionality (for which it was incidentally not designed). That categories_path route is meant to take you to your category index.
You need to create a method in your controller to perform the functionality you want to see.
class PostsController < ApplicationController
...
def posts_by_category
#posts_by_category = Post.where("category_id = ?", params[:category_id])
end
...
end
Then you're going to need a view to display your #posts_by_category array (I'll leave this exercise to you).
And now for the key to your problem: you need a route pointing to the posts_by_category method.
get 'posts/posts_by_category' => 'posts#posts_by_category'
Now you should be able to create your link with the correct route:
<%= link_to 'News', posts_by_category_path(:category_id => 1) %>

Related

Static pages to ruby on rails [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm new to Ruby on rails, and in a month i will start a course on Ruby-on-Rails, but i would like to get some code going before i start the course since i want to learn as much as i can.
I made a project with:
rails new portfolio
Then i did:
rails generate controller portfolio index
To get the front page going.
Rails.application.routes.draw do
get 'portfolio/index'
resources :company
root 'portfolio#index'
end
then:
rails generate controller company
controller:
class CompanyController < ApplicationController
def new
end
end
Then i made a file under views/portfolio/ called index.html.erb where my front page will be.
Under views/company/ i will have a file called company.
When i now go to the url: localhost:3000/company/company i get the error:
The action 'show' could not be found for CompanyController
My CompanyController is this:
class CompanyController < ApplicationController
def new
end
end
Anyone that knows alot about ruby that can just give me a little pointer in the right direction?
Thanks.
Start with a single model and controller for companies. Create a index method inside the app/controllers/companies_controller. Then create the content inside file app/views/companies/index.html.erb to check that everything works, for example:
<h1> Hi! This is root page and index method in CompaniesController! </h1>
In config/routes.rb, you must specify a plural name for companies if you plan to create and process more than one, and leave it as it is, if the entity is the only one company for this project. Set plural name for this resource for this moment to create standard routes for CRUD:
resources :companies
root to: "companies#index"
More about routes you can find in rails guide.
You can try using the built-in scaffold generator in order to quickly generate the application skeleton:
rails generate scaffold companies
The command above will generate controller, model, views and routes with CRUD methods in controller and views for the controller methods. Each view in app/views/"resource_name_plural" adjusted with method in controller in config/routes.rb file. This is how the MVC pattern works.
If you want to create static pages, maybe you should look at the high_voltage gem.
In Rails you need to pay careful attention to pluralization. When declaring routes for a resource it should always be the plural form unless its the rare case where the resource really is singular (there can be only one).
Rails.application.routes.draw do
resources :companies
end
This will route to all companies at /companies and a single company at /companies/:id. If you thus try to get /companies/company it will be routed to the #show action since /company will be interpreted as the id.
Controllers should also be named in plural:
# app/controllers/companies_controller.rb
class CompaniesController < ApplicationController
before_action :set_company, only: [:show, :edit, :update, :destroy]
# GET /companies
def index
#companies = Company.all
end
# GET /companies/:id
def show
end
# ...
private
def set_company
#company = Company.find(params[:id])
end
end
You can use the scaffold command to get a full example of a standard rails CRUD controller:
rails g scaffold companies
If you have the create method, or new, you have to include the show method and index method.
in your routes
resources :companies
resources :portfolios
try in your controller:
class CompanyController < ApplicationController
def new
end
def new
end
def index
#companies = Company.all
end
def show
end
end
and if you have the controller you can create the views and point without problem
localhost:3000/company/company
This, I think it should be something more like this.
localhost:3000/companies/
always plural.
Your controller will look for the view defined in the method. look how it is by default, that of a project of mine. in your place of groups will be companies.
look at the name of the views, are the same names of the methods of your controller. right?

Using Self joins in Rails views

I have a Users table which also has a manager's id to implement a self-join. when I login as a a manager and click on "My subordinates", I should see my subordinates. The subordinates are also from the User table.
So my question is
What should I say here <%= link_to "My Subordinates", ????_path %>(I mean like user_path.).
How should the model and controller logic be?
I would do something like #ryanfelton said, but instead of overwriting the index method, i would create a new one specifically for the subordinates.
class Manager::UsersController < ApplicationController
before_action :ensure_manager! #this one check the manager_id or any other condition to be manager
def sobordinates
#subordinates = #user.subordinates
end
end
#routes.rb
namespace :manager do
resources :users do
collection do
get :subordinates
end
end
end
This way you can maintain the index of users and you have a method only for the subordinates.
Be aware that you need to create a subordinates.html.erb inside the users folder >
app/views/manager/users/subordinates.html.erb
EDIT:
You where asking for the model and the link also so, here it goes:
The link: after editing the routes.rb, go to the console and use rake routes
and search for the subordinates link. Add the _path or _url depending on the use you are whiling for that path.
The model, I strongly recommend you to read the official documentation about relations: http://guides.rubyonrails.org/association_basics.html. That would help you more than having the answer for copying and pasting :)
I would recommend namspacing a users_controller.rb.
So it would be in the folder app/controllers/manager/users_controller.rb
class UsersController < ApplicationController
before_action :ensure_manager!
def index
#manager.users
end
end
In the routes.rb you would have this route:
namespace :manager do
resources :users
end
So ultimately your path would be manager_users_path

Route and controller design for vote/like resource

I have a like model, recording which user liked which record. I used polymorphic association so a user can like many models.
Currently I use nested-resources to handle likes.
POST /items/:item_id/likes
DELETE /items/:item_id/likes/:id
Now for some reasons I want to get rid of the use of like_id by designing a better route. This is because it will be easier to cache a fragment view.
Note that item model is only one of a few models which are likable, and I want to avoid code duplication if possible.
What's a good way to design routes and controllers that will not use like_id but also allows better code reuse in controller?
Possible implementation
I was thinking of routes like this:
POST /items/:item_id/like
DELETE /items/:item_id/like
I won't use nested like resource. Instead I place a like action in items controller. It will determine if the request is a POST or a DELETE and act accordingly. This however doesn't feel DRY.
I don't know about Rails necessarily, but in Zend Framework I would create a front controller plugin to route all requests with methods 'LIKE' and 'UNLIKE' to a particular controller which then deduces which route was requested, and subsequently which resource was requested, and then performs the necessary actions to 'like' or 'unlike' that resource in the name of the requesting user.
Why? Because the user is 'like'-ing or 'unlike'-ing the resource in question, not 'creating a like' or 'deleting a like'. Sure, in the backend, the 'like' is a record in a cache or database that gets created or deleted -- but the semantics of a resource are not necessarily equivalent that of whichever method is used to persist that resource.
What you need is Singular Resources.
routes.rb
resources :items do
resource :like, only: [:create, :destroy]
end
likes_controller.rb
class LikesController < ApplicationController
before_action :load_likeable
def create
#like = Like.where(likeable: #likeable, user: current_user).first_or_create
redirect_back(fallback_location: #likeable)
end
def destroy
#like = Like.find_by(likeable: #likeable, user: current_user).destroy
redirect_back(fallback_location: #likeable)
end
private
def load_likeable
klass = [Recording].detect { |c| params["#{c.name.underscore}_id"] }
#likeable = klass.find(params["#{klass.name.underscore}_id"])
end
end
likes_helper.rb
module LikesHelper
def like_button_for(item)
if item.liked
form_tag recording_like_path(item), method: :delete do
button_tag "UnLike"
end
else
form_tag recording_like_path(item), method: :post do
button_tag "Like"
end
end
end
end
item.liked is method from Item model

How do I create a resource that is the sub-set of an existing resource

In my "routes.rb" file I have the following line:
resource :users
which gives me a bunch of named routes for accessing my User model in a RESTful manner.
Now, I've made some additions to the User model including creating a special class of user. These are still stored in the User model but there is a "special" flag in the database that identifies them as special.
So, is it possible to create special_users resource? For example, I'd like to have a "special_users_path" as a named route to "/special_users" which will return an index of only the special users when you perform a GET on the URL.
Is there a way to do this?
In Rails routing, a 'resource' refers to the standard 7 routes that are created for RESTful resources: index, show, new, create, edit, update and destroy. Normally that is enough, but sometimes you might want to create another action.
In the model, you want to create a scope that only returns special users:
class User < ActiveRecord::Base
scope :special, where(:special => true)
end
On the controller side, there are two ways to go about this. What you are suggesting is the creation of an additional action:
match "/users/special" => "users#special"
resource :users
In the controller, your special action would return the scope you just created:
class UsersController < ApplicationController
def special
#users = User.special
end
end
That will do what you ask, but I would suggest NOT doing it this way. What if you add other flags later that you want to search by? What if you want to search by multiple flags? This solution isn't flexible enough for that. Instead, keep the routes the way they are:
resource :users
and just add an additional line to your controller:
class UsersController < ApplicationController
def index
#users = User.all
#users = #users.special if params[:special]
end
end
and now, when you want to display special users, simply direct the user to /users?special=true
This approach is much more future-proof, IMO.
(This answer is assuming Rails-3. If you're still using 2.3 let me know)
You could set the special_users as a resource:
resource :special_users
If you need to point it to a special controller, you could specify it with:
resource :special_users, :controller => :users
But I would really suggest you to not creating another controller for retrieving a kind of user, but using a param to get them:
class UsersController < ApplicationController
def index
users = case params[:type].to_s
when "special"
User.special_users # Using named scopes
else
User.all
end
end
end
When you use the users_path to call the special users:
users_path(:type => :special)

Rails: how can I nest only associated resources?

To set up nested resources in Rails, I have seen example routes given like this:
map.resources :players
map.resources :teams, :has_many => :players
By doing this, you can visit teams/1/players and see a list. But it lists all players, not just those that belong to team 1.
How can I list only the resources that are associated with the parent resource?
You need to load the team first. A common practice is to do this in a before filter.
class PlayersController < ActionController::Base
before_filter :get_team
def get_team
#team = Team.find(params[:team_id])
end
def index
#players = #team.players # add pagination, etc., if necessary
end
def show
#player = #team.players.find(params[:id])
end
end
Note that the code above insists that you specify a team. If you want the same controller to work for both, you need to change it slightly (i.e. check for params[:team_id]).
You can use the excellent inherited_resources gem to DRY this up if you controller logic is straightforward.
The problem has little to do with map.resources and routing in general.
Note, players are not fetched magically by the framework: there's some action in some controller processing teams/1/players request and your code there fetches list of players to show. Examining that action (or posting here) should help.

Resources