Displaying item with a specific parameter in the index - ruby-on-rails

I am building a website for poetries.
There are two different type of poetries: famous or amateur.
I built the CRUD functions to display all the poetries (famous and amateur, without distinction) and this is working as intended (see the PoetrisController code below).
Now, I want to give the possibility to the user to choose if he wants to see only the amateur poetries or famous ones.
Basically the user clicks the link "Amateur" or "Famous" in the navbar and he is redirected to a new page listing only amateur or famous poetries.
My question is: should I create another Controller (for example PoetriesFamousController) and creating a index function inside it to display only the famous poetries or there is a way to use the already existing PoetriesController to show only the "famous poetries" if the user clicks the link in the navbar?
PoetriesController:
class PoetriesController < ApplicationController
skip_after_action :verify_authorized, only: [:home, :about, :newsletter, :disclaimer, :new, :create]
skip_before_action :authenticate_user!, only: [:home, :about, :newsletter, :disclaimer, :new, :create]
before_action :set_poetry, only: [:show, :edit, :update, :destroy,]
before_action :authenticate_user!, except: [:index, :amateur_poetries]
def index
if params[:search]
#poetries = policy_scope(Poetry).search(params[:search]).order("created_at DESC").limit(30)
else
#poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
end
end
def show
authorize #poetry
end
def new
#poetry = Poetry.new
end
def create
Poetry.create(poetry_params)
redirect_to poetries_path
end
def edit
authorize #poetry
end
def update
#poetry.save
redirect_to poetry_path(#poetry)
end
def destroy
#poetry.destroy
redirect_to poetries_path
end
private
def poetry_params
params.require(:poetry).permit(:title, :author, :body, :poster, :country)
end
def set_poetry
#poetry = Poetry.find(params[:id])
end
end
Poetries.rb
class Poetry < ApplicationRecord
def self.search(search)
where("lower(title) LIKE ? OR lower(author) LIKE ? OR lower(country) LIKE ? OR lower(born) LIKE ?", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%")
end
end
Routes.rb
get 'poetries', to: 'poetries#index', as: :poetries
get "poetries/new", to: "poetries#new"
post "poetries", to: "poetries#create"
get "poetries/:id/edit", to: "poetries#edit"
patch "poetries/:id", to: "poetries#update"
get 'poetries/:id', to: 'poetries#show', as: :poetry
delete "poetries/:id", to: "poetries#destroy"

Here is some idea for your problem
In your view (sample idea)
poetries type:
<%= select_tag :poetries_type, options_for_select(["Famous","Amateur"]), include_blank: true, :class => 'form-control' %>
in your controller:
def index
if params[:search]
if params[:poetries_type] == "Famous"
#poetries = Poetry.famous.search(params[:search]).order("created_at DESC").limit(30)
elsif params[:poetries_type] == "Amateur"
#poetries = Poetry.amateur.search(params[:search]).order("created_at DESC").limit(30)
else
#poetries = Poetry.search(params[:search]).order("created_at DESC").limit(30)
end
else
#poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
end
end
Poetries.rb, add two scope for famous an amateur
def self.amateur
where("poster != ?","Admin")
end
def self.famous
where("poster = ?","Admin")
end

The simplest thing would be to add two more actions to your controller.
def famous
#poetries = #get the famous ones
render :index
end
def amateur
#poetries = #get the amateur ones
render :index
end
Then update your routes
get 'poetries', to: 'poetries#index', as: :poetries
get 'poetries/famous', to: 'poetries#famous'
get 'poetries/amateur', to: 'poetries#amateur
# rest of the routes

Related

Why am I getting a recordnotfound error when trying to access an instance in rails?

I have a user profile controller called "userinfo" and it's corresponding view. The userinfo index is the root path. In the homepage(which is the userinfo index), I have a link that takes you to the user profile page. It is giving me this error when I go to the home page:
My routes are:
My userinfos_controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.find(params[:id])
end
def show
#myvideo = Video.last
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
and my view is:
<%= link_to 'profile', userinfors_path(#userinfors) %>
My routes.rb file:
Rails.application.routes.draw do
devise_for :users
resources :userinfos do
resources :videos
end
resources :pages
get '/application/decide' => 'application#decide'
root 'userinfos#index'
get '/userinfos/:id', to: 'userinfos#show', as: 'userinfors'
end
Thanks for any help!
ok, there are multiple errors and you are not following conventions of rails, index is not for what you have used.
Index is used to list all the users and show for a particular one with id passed in params.
Your index path is, as you can see, /userinfos which is correct and it doesn't have any id with it but you are trying to find user with params[:id] which is nil and hence the error.
Lets try out this:
def index
#userinfors = Userinfo.all #pagination is recommended
end
In your index view,
<% #userinfors.each do |userinfor| %>
<%= link_to "#{userinfor.name}'s profile", userinfo_path(userinfor) %>
<% end %>
It should work now.
Please read routing and action controller to get the idea and understand the magic behind rails routing and mvc architecture..

Editing a Wizard multistep form after save - Wicked Gem / Rails

I've been going round in circles all day with this. I have a large multi-step form using the Wicked gem and Ruby on Rails. It works perfectly but I can't figure out how to to get back into the form to edit individual entries.
Iim trying to make the ability to go into the client show page, click an individual client and then from there go back into the quote to edit and update it. As the Wicked gem only seems to work with show and update actions, if I try to build a standard edit action Wicked expects to be on a step therefore doesn't work.
I read the I would have to factor the edit action into my show/update actions but I'm having difficulties. Any help would be great thanks!
Clients Controller:
class ClientsController < ApplicationController
before_action :authenticate_user!, only: [:index, :show, :edit]
before_action :set_client, only: [:edit, :show, :update]
def index
#clients = Client.order('created_at DESC').paginate(page: params[:page], per_page: 10)
end
def show; end
def new
#client = Client.new
end
def edit; end
def update
if #client.update_attributes(client_params)
redirect_to client_quotes_path
flash[:success] = 'Client successfully updated'
else
render 'edit'
end
render_wizard #client
end
# After client is completed:
def create
#client = Client.new(client_params)
if #client.valid?
#client.save
session[:current_user_id] = #client.id
ClientMailer.new_client(#client).deliver
redirect_to quotes_path
else
flash[:alert] = 'Sorry, there was a problem with your message. Please contact us directly at ...'
render :new
end
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(:first_name, :last_name, :title, :email, :email_confirmation,
:phone, :time, :reminder, :ref_number, :day, :note, :logs_reminder)
end
end
Quotes Controller:
class QuotesController < ApplicationController
include Wicked::Wizard
before_action :set_client, only: [:show, :update, :quote_success]
steps :profile, :employment, :general_questions, :indemnity_details, :declarations
def show
#client.build_doctor unless #client.doctor.present?
#client.build_dentist unless #client.dentist.present?
#client.old_insurers.build
#client.practice_addresses.build
render_wizard
end
def update
#client.update(client_params)
render_wizard #client
end
def quote_success; end
private
def set_client
current_user = Client.find_by_id(session[:current_user_id])
#client = current_user
end
# After full quote form is completed:
def finish_wizard_path
if #client.valid?
ClientMailer.new_quote(#client).deliver
ClientMailer.new_quote_user_message(#client).deliver
end
quote_success_path
end
end
def client_params
params.require(:client).permit(:name, :email, :email_confirmation, :phone, :date_required,
:title, :first_name, :last_name, :date_of_birth, :nationality, :reg_body, :reg_date, :reg_type, :reg_number,
:qual_place, :qual_year, :post_grad, :membership ...
Routes:
Rails.application.routes.draw do
devise_for :users
root 'clients#new'
get 'client', to: 'clients#new', as: 'client'
post 'client', to: 'clients#create'
get '/client_quotes', to: 'clients#index', as: 'client_quotes'
get '/client_quotes/:id', to: 'clients#show', as: 'client_quote'
get '/client_quotes/:id/edit', to: 'clients#edit', as: 'edit_client_quote'
patch '/client_quotes/:id', to: 'clients#update'
put '/client_quotes/:id', to: 'clients#update'
resources :quotes, only: [:index, :show, :update, :quote_success]
get 'quote-success' => 'quotes#quote_success'
devise_scope :user do
get '/login' => 'devise/sessions#new'
end
end
My solution to this in the end was rather than have the edit form as a multi step wizard, I've joined the form data together in a separate view page and got a traditional route to it as you mention. Not perfect but does the job!
When you are updating it is like you are "editing" the element, so you need to redirect to the wizard when you want to edit and when you call the update method you really would be editing that entry. So call the wicked path.

ActiveRecord::RecordNotFound (Couldn't find Story with 'id'=):

I have been looking at other people's posts about this question. Unfortunately, still a while later I am stuck on it. I understand that a couple of the controller methods cannot find a story with the relevant 'ID' and render that to the views, hence my error.
However, I don't understand how I can edit my controller methods/routing so it can actually find the id of '1,2,3,4 etc'. I believe it is trying to look for a different from of ID. The 'create' and 'show' methods are creating the same error.
Error on screen:
ActiveRecord::RecordNotFound in StoriesController#create
Couldn't find Story with 'id'=
def find_story
#story = Story.find(params[:id])
end
Here, I have put in ID as the params for the story find method but it isn't finding it. Why?
class StoriesController < ApplicationController
before_action :find_story, only: [:destroy, :create, :show, :edit, :update]
def index
#stories = Story.order('created_at DESC')
end
def new
#story = Story.new
end
def create
#story = Story.new(story_params)
if #story.save
flash[:success] = "Your beautiful story has been added!"
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
if #story.update.attributes(story_params)
flash[:success] = "More knowledge, more wisdom"
redirect_to root_path
else
render 'edit'
end
end
def destroy
if #story.destroy
flash[:success] = "I think you should have more confidence in your storytelling"
else
flash[:error] = "Can't delete this story, sorry"
end
end
def show
#stories = Story.all
end
private
def story_params
params.require(:story).permit(:name, :description)
end
def find_story
#story = Story.find(params[:id])
end
end
My routes.rb:
Rails.application.routes.draw do
get 'stories/new/:id' => 'posts#show'
resources :stories
devise_for :users
root to: 'stories#index'
end
You want to change the ":find_story" to not include the create as that is what is telling it to look for an id, but there is no id when on the create page, you are creating a new one, not finding one that exists
so change the before_action to this
before_action :find_story, only: [:destroy, :show, :edit, :update]
Your issue with stories is the route you are trying to use. Show looks for an id, for the same reason I mentioned above, so the route need to be something like
stories/show/1
where "1" is the id of the story you want.
ActiveRecord::RecordNotFound in StoriesController#create
you have before_action :find_story with create method which is trying to find Story but there is no :id in params
So you need to remove :create action from the list of before_action and change it to
before_action :find_story, only: [:destroy, :show, :edit, :update]
I have got the same problem.
#table = Table.find(params[:id])
ActiveRecord::RecordNotFound - Couldn't find Table with 'id'=12222:
app/controllers/tables_controller.rb:27:in `edit'
Sol: Try this query
#table = Table.find_by_id(params[:id])

Rails4: Link to A Controller

I have a User's Show Page where
1.) Routes
get 'users/:id' => 'user#show', as: :user
2.) user_controller.rb
class UserController < ApplicationController
before_filter :authenticate_user!, only: :show
def show
#user = User.find_by_name(params[:id]) # for name instead of id
#listings = #user.listings
end
end
and i can link to it via "current_user".
I wanted to create a Shop Controller, so i followed the same steps. I Generated a Shops Controller and modified the routes and controller as follow:
1.) Routes
get 'users/:id' => 'user#show', as: :user
get 'shop/:id' => 'shop#show', as: :shop
2.) shop_controller.rb
class ShopController < ApplicationController
before_filter :authenticate_user!, only: :show
def show
#user = User.find_by_name(params[:id]) # for name instead of id
#listings = #user.listings
end
end
This Works only if im at the User's Page (localhost:3000/users/test) and then click the link to the controller. then it switches to (localhost:3000/shop/test).
If i try to click the link anywhere else im getting
The link is ->
<li><%= link_to "My Shop", :controller => "shop", :action => "show" %></li>
I'm fairly new to Rails, if someone could enlighten me it would be very nice :)
First start with correcting names of your controllers as per rails conventions. Names should be as follows.
controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!, only: :show
def show
#user = User.find(params[:id]) # Because Id can't be same for two users but name can be.
#listings = #user.listings
end
end
And in case of shop_controller it is fine beacuse shop is not a model.
controllers/shop_controller.rb
class ShopController < ApplicationController
before_filter :authenticate_user!, only: :show
def show
#user = User.find(params[:id]) # Id can't be same for two users but name can be.
#listings = #user.listings
end
end
And give link like this.
<%= link_to "My Wonderful Shop", {:controller => "shop", :action => "show", :id => #user.id} %>
In your routes file
get 'shop/:id' => 'shop#show'

Checking an attribute is true before executing a CRUD action

Before any of my article controller crud actions can run (excluding index), i want to make sure that the article's active field is true.
I thought about doing this in a before_filter, but at that point #article has not been set, any ideas please?
Thanks
You could set the article in a helper method and remove some code duplication while you're at it.
class .. < ApplicationController
helper_method :current_article
def index
# your code etc..
end
private
def current_article
#article ||= Article.find(params[:id], :conditions => { :active => true }) ||
raise(ActiveRecord::RecordNotFound)
end
end
Basically you can now call current_article in your show, edit (etc) actions and views instead of #article.
You just need to do 2 before_filter.
1 with load the article and the second one to check if field exist
before_filter :load_article, :only => [:show, :edit, :update]
before_filter :has_field, :only => [:show, :edit, :update]
...
private
def load_article
#article = Article.find(params[:id])
end
def has_field
unless #article.active
redirect_to root_url
end
end

Resources