I have such problem: I don't need to nest all resources, because I need only two action.
First - show all reports related with 1 website.
Second - show all reports.
When I rewrite code in routes.rb like this:
resources :websites do
member do
match 'performance_reports' => 'performance_reports#index'
match 'performance_reports/show' => 'performance_reports#show'
end
end
//rake routes
performance_reports_website /websites/:id/performance_reports(.:format)
performance_reports#index
performance_reports_show_website /websites/:id/performance_reports/show(.:format)
performance_reports#show
Here is my action:
before_filter :load_website
private
def load_website
#website = Website.find(params[:website_id])
end
def index
#performance_reports = #website.performance_reports
end
Code from view:
<%= link_to "Link", performance_reports_website_path(website)) %>
but when I'm calling it I get error:
Couldn't find Website without an ID
rb:7:in `load_website'
What I'm doing wrong ?
In controller change the code to
#website = Website.find(params[:id])
Related
I'm new to rails, and I've already learnt how to do CRUD using scaffold and using resource, I wanna know how to Do CRUD without using resource, However what I want to do is create custom methods for CRUD in the controller that will be like the traditional CRUD model. Please help me with this.
Actually, for the action index / new / show / create / edit / update / destroy, this is a convention in Ruby On Rails
If I'm right and if you're trying to change their name using resources in routes.rb (Changing by exemple the new action into def my_restaurant), Rails will render
Unknown action The action 'new' could not be found for
RestaurantsController
Netherless, you can create some methods to perform some particular action into the index, and add them in the "private section" you want to hide them to the public. There is no routes attach to this method.
class RestaurantsController < ApplicationController
def index
#restautants = Restaurant.all
#restaurants.sort_by_name
end
private
def sort_by_name
#some action here
end
end
If you want to create your own name method, you can personnalise it but you can't use resources "shortcut" in the routes.rb
by exemple :
#reviews_controller.rb
class ReviewsController < AplicationController
def index
#reviews = Reviews.all
end
def update
#review = Review.find(review_params[:id])
end
def history
#some action
end
private
def review_params
params.require(:review).permit(:liked, :comment, :id)
end
end
Then add a view
#app/views/reviews/history.html.erb
Don't forget the routes :
Rails.application.routes.draw do
resources :reviews, only: [:index, :update] do
collection do
get :history
end
end
end
I hope this will help you and complete the previous answer.
as for your second question :
I still do have one doubt tho..... Will <%= form_for #post do |f| %>
this form be enough for new_post and edit_post, will it automatically
identify them? If that's not enough can you please tell me the what
comes at new_post.html.erb and edit_post.html.erb....Thanks again for
the help.
If the form is the same for your new_post and edit_post, i may suggest you to put into a partial file. (For this example I used simple_form gem)
#app/views/posts/_form.html.erb
<%= simple_form_for(#post) do |f| %>
<%= f.input :name, label: "post name" %>
<%= f.input :photo, as: :file %>
<%= f.submit "Save", class:"btn btn-small btn-success" %>
<% end %>
and then render the partial in your views new file.
#app/views/posts/new.html.erb
<div>
<h1>New Post</h1>
</div>
<%= render "form" %>
Well I hope I could help you with this answer.
Do not hesitate too to read ruby documention. You may find more information that you're looking for too.
My answer may be redundant but it's the better way for me to clearly explain it...
In oder to use your own custom methods you need to create them in your controller, setup the route and if needed create an view.
# PostsController
def create_post
# Do your create stuff here
end
def read_post
# Do your read stuff here
end
def update_post
# Do your update stuff here
end
def delete_post
# Do your delete stuff here
end
# routes
post '/create_post', to: 'posts#create_post'
get '/read_post/:id', to: 'posts#read_post'
put '/update_post/:id', to: 'posts#update_post'
delete 'delete_post/:id', to: 'posts#delete_post'
With the controller and routes setup you will only need a view for the read_post method. The best way to do that is create the file: views/posts/read_post.html.erb
There is 7 CRUD routes to Create, Read, Update and Delete.
Rails.application.routes.draw do
get "restaurants", to: "restaurants#index"
get "restaurants/new", to: "restaurants#new", as: :new_restaurant
post "restaurants", to: "restaurants#create"
# NB: The `show` route needs to be *after* `new` route.
get "restaurants/:id", to: "restaurants#show", as: :restaurant
get "restaurants/:id/edit", to: "restaurants#edit", as: :edit_restaurant
patch "restaurants/:id", to: "restaurants#update"
delete "restaurants/:id", to: "restaurants#destroy"
end
So once the route create, you can create in the controller, the action that you need
class RestaurantsController < ApplicationController
def index
#restaurants = Restaurant.all
end
end
and the view
app/views/restaurants/index.html.erb.
The best practice is to create only the routes that you will need and to work in silos.
1 route, 1 controller action, 1 view.
Before getting into details I have read through these posts to try to find the solution without success : one, two, three
That being said: I am [new and] building an ecomm site for selling secondhand clothing, shoes and decor items.
My structure has only one Product model and associated controller and table. Each 'product' has one of three different main categories, which is what I am using to differentiate and create 3 different URLs.
My routes look like this:
Rails.application.routes.draw do
root to: 'pages#home'
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show'
get 'shoes', to: 'products#shoes'
get 'shoes/:id', to: 'products#show'
get 'home', to: 'products#home'
get 'home/:id', to: 'products#show'
get 'products/new', to: 'products#new'
post 'products', to: 'products#create'
end
My products_controller looks like this:
class ProductsController < ApplicationController
before_action :set_all_products
before_action :set_one_product, only: [:show]
def shoes
#all_shoe_products = #all_products.where(main_category_id: MainCategory.find_by_name("shoes").id)
end
def clothing
#all_clothing_products = #all_products.where(main_category: MainCategory.find_by_name("clothes").id)
end
def home
#all_home_products = #all_products.where(main_category: MainCategory.find_by_name("housewares").id)
end
def show
end
def new
#new_product = Product.new
end
private
def set_one_product
#product = Product.find(params[:id])
end
def set_all_products
#all_products = Product.all
end
end
And when writing <%= link_to clothing_path(product) %> ('product' being the placeholder in an .each loop), I get a path: root/clothing.[:id] and not root/clothing/[:id]
I know I am making a convention error, and trying to have 3 different URLs within the same controller may be where I am gong wrong.
Note: manually entering root/clothing/[:id] in the address bar does return a product correctly.
When you do this:
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show'
in your routes.rb, it creates these routes (which you can see by doing rake routes in your console):
clothing GET /clothing(.:format) products#clothing
GET /clothing/:id(.:format) products#show
As you can see, clothing_path routes to /clothing, not /clothing/:id. So, when you do:
<%= link_to clothing_path(product) %>
rails appends the id as .id (which is what you're experiencing).
#jvillian explains the cause of the issue well here, though I'd like to propose a slight refactor as a solution.
This might be a little more work, though you'd likely be better off with seperate controllers for shoes, clothing and home, and following a RESTful design. That would allow you to use resources in your routes file.
For example, your shoes_controller.rb would be like the following:
class ShoesController < ApplicationController
before_action :set_all_products
before_action :set_one_product, only: [:show]
def index
#all_shoe_products = #all_products.where(main_category_id: MainCategory.find_by_name("shoes").id)
end
def show
end
private
def set_one_product
#product = Product.find(params[:id])
end
def set_all_products
#all_products = Product.all
end
end
And then the routes to define them would be:
resources :shoes, only: [:index, :show]
You follow this pattern for the other resources and you'll have nicely segregated code be following good Rails conventions.
This will generate the routes as you're after:
shoes GET /shoes(.:format) shoes#index
shoe GET /shoe/:id(.:format) shoes#show
That will resolve your issue and give you a nicely designed app - there's also opportunity to extrapolate some of the code shared between the new controllers, though that sounds like a follow up task :)
Hope this helps - let me know if you've any questions or feedback.
I found a solution, though seems a bit of a logic mystery to me why it's working.
In routes.....
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show', as: 'clothing/item'
In the index page....
<%= link_to clothing_item_path(product) do %>
This yields the right URL structure: root/clothing/[:id]
While testing this I was expecting: root/clothing/item/[:id]
...though I prefer the result over my expectation
I think what you want is parameterized routes, like this:
get ':product_line', to: 'products#index'
get ':product_line/:id', to: 'products#show'
This would allow you to create any number of custom product lines without ever having to define new methods in your controller. Assuming there is a product_line attribute on your Product model, the controller would look like this:
class ProductsController < ApplicationController
def index
#product_line = params[:product_line]
#products = Product.where(product_line: #product_line)
end
def show
#product_line = params[:product_line]
#product = Product.find(params[:id])
end
end
And your views/products/index.html.erb would look like this:
<p id="notice"><%= notice %></p>
<h1><%= #product_line %></h1>
<table>
<thead>
<tr>
<th>Description</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<% #products.each do |product| %>
<tr>
<td><%= product.description %></td>
<td><%= product.price %></td>
<td><%= link_to 'Show', "#{#product_line}/#{product.id}" %></td>
</tr>
<% end %>
</tbody>
</table>
Note that the link_to can no longer use a Rails helper method to generate the url. You'd have to do that yourself.
The beauty of this approach is that users could type in ANY product line in the URL. If you had that product line (like say 'sporting_goods'), go ahead and display it. If not, render a page thanking them for their interest and log the fact that someone requested that product line so you can guage interest as you expand your offerings.
Plus, it's RESTful! Yay!
The Rails way of solving this is by creating a nested resource:
resources :categories do
resources :products, shallow: true
end
This nests the collection routes so that you get GET /categories/:category_id/products.
While this might not be as short as your vanity routes it is much more versatile as it will let you show the products for any potential category without bloating your codebase.
You would setup the controller as so:
class ProductsController < ApplicationController
before_action :set_category, only: [:new, :index, :create]
# GET /categories/:category_id/products
def index
#products = #category.products
end
# GET /categories/:category_id/products/new
def new
#product = #category.products.new
end
# POST /categories/:category_id/products
def new
#product = #category.products.new(product_params)
# ...
end
# ...
private
def set_category
#category = MainCategory.includes(:products)
.find_by!('id = :x OR name = :x', x: params[:id])
end
end
You can link to products of any category by using the category_products_path named path helper:
link_to "#{#category.name} products", category_products_path(category: #category)
You can also use the polymorphic path helpers:
link_to "#{#category.name} products", [#category, :products]
form_for [#category, #product]
redirect_to [#category, :products]
If you want to route the unnested GET /products and nested GET /categories/:category_id/products to different controllers a neat trick is to use the module option:
resources :products
resources :categories do
resources :products, only: [:new, :index, :create], module: :categories
end
This will route the nested routes to Categories::ProductsController.
Hi everybody I'm from the old school using rails 2.
Actually I'm using rails 4 and I'm trying to find a way to create methods on the controller without writting
On RAILS 2 used: (only needed to write the name on the controller)
#controller
def report_a
end
def report_b
end
def report_c
end
...and whatever def
#ROUTES
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
On RAILS 4
#controller
def report_a
end
def report_b
end
def report_c
end
#ROUTES
match ':controller(/:action(/:id(.:format)))', :via => [:get, :post]
The problem is when I create a view report like this: (views/reports/report_a.html.erb)
<%= form_tag :action=>"report_a" do %>
<% end %>
I get this message:
No route matches [GET] "/reports/report_a"
To resolve this issue and doing Rails instruccions works like this:
#controller
def report_a
#users= User.search(params[:name])
end
def result_report_a
#users= User.search(params[:name])
end
#view/reports/report_a.html.erb
<%= form_tag :action=>"result_report_a" do %>
<% end %>
#routes.rb
get "reports#report_a"
post "reports#result_report_a"
get "reports#report_b"
post "reports#result_report_b"
get "reports#report_c"
post "reports#result_report_c"
Also I found this better way:
#controller reports.rb
def search_report_a
report_a
render :report_a
end
def report_a
#users = User.where(:name=>params[:name])
end
def search_report_b
report_b
render :report_b
end
def report_b
#users = User.where(:address=>params[:address])
end
...
#Routes.rb
resources :users do
match 'search_report_a', :via => [:post,:get], :on => :collection
match 'search_report_b', :via => [:post,:get], :on => :collection
...
end
Is there any other way to create methods without adding all inside ROUTES.RB ?
Any suggestions or the only way is adding get and post?
Imagine a case where you have several methods.
Best approach in Rails is to use REST architecture. Your controller should be able to view, create, update and destroy some resource (of course all actions are not mandatory).
For example:
def ReportsController
def index
# Actions to show links to all possible reports
end
def show
# Show report based on params
end
end
Your #show method may show any of report (report_a, report_b, etc) just by checking param from GET request.
And you don't need to make all logics inside #show method. It would be better to place report-related logic in, maybe, some service objects.
When I go to the characters controller, show action, all the normal params[:id] is as how it should be according to REST.
In the show view, I render a partial. In that partial, I have a link that goes to the vote_socionics action. This action is defined under a socionics_votes module, which gets included by the characters controller. (I have it set up this way because I have other controllers that also include this module).
My problem is that when I click on this link, and it goes to the set_votable private method within the socionics_votes_module.rb file, the params[:id] is no longer present. Using pry, I found that it actually turns into params[:character_id]
Questions:
1) Why does this happen (is it because it goes to a "different" controller, even if it's a module?)
2) How do I work around this? I would think that it would be more elegant to have it be params[:id], instead of having to do an if-else to account for both keys.
characters_controller.rb
class CharactersController < ApplicationController
include SocionicsVotesModule
def show
#character = Character.find(params[:id])
end
characters/show.html.haml
= render partial: 'votes/vote_socionics',
locals: { votable: #votable, votable_name: #votable_name, socionics: #socionics }
_vote_socionics.html.haml
= link_to content_tag(:div,"a"), send("#{votable_name}_vote_socionics_path", votable, vote_type: "#{s.type_two_im_raw}"),
id: "vote-#{s.type_two_im_raw}",
class: "#{current_user.voted_on?(votable) ? 'voted' : 'not-voted'}",
method: :post,
data: { id: "#{s.type_two_im_raw}" }
socionics_votes_module.rb
module SocionicsVotesController
extend ActiveSupport::Concern
included do
before_action :set_votable
end
private
def set_votable
votable_constant = controller_name.singularize.camelize.constantize
#votable = votable_constant.find(params[:id]) # This is where it fails, since there is no params[:id], and rather, params[:character_id]
end
def set_votable_name
#votable_name = controller_name.singularize.downcase
end
routes.rb
concern :socionics_votes do
post 'vote_socionics'
end
resources :characters, concerns: :socionics_votes
resources :celebrities, concerns: :socionics_votes
resources :users, concerns: :socionics_votes
The URL of the link in the partial when hovered over.
localhost..../characters/4-cc/vote_socionics?vote_type=neti
Something like .find(params[:id] || params[:"#{#votable_name}_id"]) didn't work, and seems silly.
You need to add the vote_socionics route as a member of the resource:
concern :socionics_votes do
member do
post 'vote_socionics'
end
end
This way the id parameter gets set correctly
this is my form
<%= form_for([#game,#message_template]) do |f| %>
and in my controller I do this:
def edit
#message_template = MessageTemplate.find(params[:id])
#game = Game.where(:slug => params[:game_id])
end
My route is like this /games/:game_id/message_templates/:id
But I am getting this
undefined method `model_name' for #
Try setting that arel search to return the first object (for #game).
def edit
#message_template = MessageTemplate.find(params[:id])
#game = Game.where(:slug => params[:game_id]).first
end
routes.rb:
#based on your comment, this will give you all (and likely more) of the paths you will need.
resources :games do
resources :message_templates
end