I have a controller:
class Admin::MassScanListsController < ApplicationController
def index
#mass_scan_lists = MassScanList.all
end
def show
end
end
an appropriate simple model MassScanList and a view, where I want to list all my MassScanLists with links to show their content:
- if #mass_scan_lists.present?
- #mass_scan_lists.each do |list|
tr
td= list.id
td= link_to list.name, list
td= list.enabled
I get an error undefined method 'mass_scan_list_path' which is I suppose I can understand, it must be admin_mass_scan_list_path, because my controller is Admin::MassScanListsController and not MassScanListsController. Bot how can I generate a show path in my case?
P.S.
In my routes.rb I have:
constraints admins_constraint do
namespace 'admin' do
resources :mass_scan_lists, only: [:index, :show]
end
end
try this:
link_to list.name, admin_mass_scan_list_path(list)
Related
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.
I have a page I want to display that is not index, show, edit, etc. The page I want is adoption.html.erb, but for some reason it's grabbing the show page id. In my views/shared/_header.html.erb file, I have <li><%= link_to "More About Dog Adoption", adoption_info_path, class: "dog-adopt-info" %></li>. Which is supposed to go to here: adoption.html.erb, but is instead erring out with: Couldn't find Dog with 'id'=adoption. I'm really stuck and not sure why it's grabbing the id from show. Thoughts? P.S. Let me know if you need more information.
class DogsController < ApplicationController
def index
#dogs = Dog.all
end
def show
#dog = Dog.find(params[:id])
end
def adoption
end
end
routes.rb
resources :dogs, only: [:index, :show]
get "dogs/adoption", as: "adoption_info"
app/views/shared/_header.html.erb
<a class="page-scroll" href="#about">About Dog Adoption</a>
<ul class="about-dog-adoption">
<li><%= link_to "More About Dog Adoption", adoption_info_path, class: "dog-adopt-info" %></li>
</ul>
When I click on the above link it errors with:
My rspec test passes which seems like a false positive:
context "#dogs_adoption" do
it "renders more info about dog adoptions" do
get :adoption
expect(response).to have_http_status(200)
end
end
try below code:
get "dogs/adoption", as: "adoption_info"
resources :dogs, only: [:index, :show]
Problem: your routes "/dogs/adoption" goes to dogs controller with show as action with id: "adoption".
I'm trying my hand at STI using Thibault's tutorial: https://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-3
It's been working fine up till the dynamic paths part where I get 'undefined method `underscore' for nil:NilClass' for this snippet
def format_sti(action, type, post)
action || post ? "#{format_action(action)}#{type.underscore}" : "#{type.underscore.pluralize}"
end
Routes:
resources :blogs, controller: 'posts', type: 'Blog' do
resources :comments, except: [:index, :show]
end
resources :videos, controller: 'posts', type: 'Video'
resources :posts
Post controller:
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :set_type
def index
#posts = type_class.all
end
...
private
def set_type
#type = type
end
def type
Post.types.include?(params[:type]) ? params[:type] : "Post"
end
def type_class
type.constantize
end
def set_post
#post = type_class.find(params[:id])
end
Posts Helper:
def sti_post_path(type = "post", post = nil, action = nil)
send "#{format_sti(action, type, post)}_path", post
end
def format_sti(action, type, post)
action || post ? "#{format_action(action)}#{type.underscore}" : "#{type.underscore.pluralize}"
end
def format_action(action)
action ? "#{action}_" : ""
end
Post index.html
<% #posts.each do |p| %>
<h2><%= p.title %></h2>
Created at: <%= p.created_at %><BR>
Created by: <%= p.user.name %><P>
<%= link_to 'Details', sti_post_path(p.type, p) %><P>
<% end %>
The error appears when I try to access index.html, and I haven't tried other links yet. I've tried removing 'underscore', then '_path' becomes an undefined method. I've also tried other suggestions such as 'gsub', but it also shows it as an undefined method, which leads me to think that it's a syntax error...
UPDATE:
I had attr_accessor :type and that made 'type' nil. So I removed that and now it is working
In your PostsHelper.rb
def format_sti(action, type, post)
action || post ? "#{format_action(action)}#{type.underscore}" : "#{type.underscore.pluralize}"
end
This method's else portion has type.underscore. type may be nil here. To verify it, try this:
def format_sti(action, type, post)
action || post ? "#{format_action(action)}#{type.underscore}" : "#{"post".underscore.pluralize}"
end
Try the try command, which will not return the NoMethodError exception and will return nil instead,
def format_sti(action, type, post)
action || post ? "#{format_action(action)}#{type.try(:underscore)}" : "#{type.try(:underscore).pluralize}"
end
Definition:
Using try, a NoMethodError exception will not be raised and nil will be returned instead, if the receiving object is a nil object or NilClass.
Here is the reference
I am getting an undefined method stripe_managed_accounts_path when trying to create a new resource via typical rails forms. Below is my code, I am dumbfounded, cannot figure it out.
Controller
class StripeManagedAccountsController < ApplicationController
before_action :authenticate_printer!
def new
#stripe_managed_account = StripeManagedAccount.new(printer_id: current_printer.id)
end
end
model
class StripeManagedAccount < ActiveRecord::Base
belongs_to :printer
end
views/new
<h1>Create New Stripe Managed Account</h1>
<%= render 'form' %>
view/form
<h5>inside the form</h5>
<%= form_for #stripe_managed_account do |f| %>
<% end %>
routes
resources :printers, only: [:show, :edit, :update] do
resources :stripe_managed_accounts
end
error
`undefined method 'stripe_managed_accounts_path' for #<#<Class:0x007fc627d342b8>:0x007fc62b36e108>`
routes
printer_stripe_managed_accounts GET /printers/:printer_id/stripe_managed_accounts(.:format) stripe_managed_accounts#index
POST /printers/:printer_id/stripe_managed_accounts(.:format) stripe_managed_accounts#create
new_printer_stripe_managed_account GET /printers/:printer_id/stripe_managed_accounts/new(.:format) stripe_managed_accounts#new
edit_printer_stripe_managed_account GET /printers/:printer_id/stripe_managed_accounts/:id/edit(.:format) stripe_managed_accounts#edit
printer_stripe_managed_account GET /printers/:printer_id/stripe_managed_accounts/:id(.:format) stripe_managed_accounts#show
PATCH /printers/:printer_id/stripe_managed_accounts/:id(.:format) stripe_managed_accounts#update
PUT /printers/:printer_id/stripe_managed_accounts/:id(.:format) stripe_managed_accounts#update
DELETE /printers/:printer_id/stripe_managed_accounts/:id(.:format) stripe_managed_accounts#destroy
and it is highliting this line <%= form_for #stripe_managed_account do |f| %>
I have grepped the entire code base for stripe_managed_accounts_path and it is no where. I am at odds end...
UPDATE:::
If I add that route it disappears??? Why is it looking for that route. Is it becasue of how I named my fodlers, etc??
You're nesting stripe_managed_accounts inside printers on your routes file. If you take a look at the output of rake routes, you can see that there isn't a path for stripe_managed_accounts_path.
You can either use the shallow option on the stripe_managed_accounts resource or adjust your form to include the printer which the managed account will belong to.
#controller
class StripeManagedAccountsController < ApplicationController
before_action :authenticate_printer!
def new
#stripe_managed_account = current_printer.build_stripe_managed_account
end
def create
current_printer.stripe_managed_accounts.create stripe_managed_account_params
# handle response
end
def stripe_managed_account_params
params.require(:stripe_managed_account).permit([list of attributes])
end
end
#form
<h5>inside the form</h5>
<%= form_for [current_printer, #stripe_managed_account] do |f| %>
<% end %>
That will generate the proper url, nesting the stripe_managed_account inside the current printer.
For has_one association reference http://guides.rubyonrails.org/association_basics.html#has-one-association-reference
For your context: This is my first attempt to create a app. I have just started coding:-).
I am trying to get a simple CRUD setup to work.
Now i'm having two problems i can't get my head around:
My entries don't show up on my index page. it gives te following error: 'undefined method `title' for nil:NilClass'. The model contains the following columns:
string :title,text :forecast, date :review_date
If i go to decisions/edit it gives me the following error: 'Couldn't find Decision with 'id'=edit'
This is my code:
Controller:
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update]
def index
# gets all rows from decision table and puts it in #decision variable
#decisions = Decision.all
end
def show
# find only the decision entry that has the id defined in params[:id]
#decision = Decision.find(params["id"])
end
# shows the form for creating a entry
def new
#decision = Decision.new
end
# creates the entry
def create
#decision = Decision.new(decision_params)
if #decision.save
redirect_to #decision
else
render 'new'
end
end
# shows the form for editing a entry
def edit
#decision = Decision.find(params["id"])
end
# updates the entry
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find(params["id"])
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
index view
<h1>Hello World ^^</h1>
<% #decisions.each do |descision| %>
<p><%= #decision.title %></p>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :decisions
root 'decisions#index'
end
I have been working on these two all morning but i can't figure it out. I would be a great help if you guys can take a look for me.
I have just started coding
Welcome!!
My entries don't show up on my index page.
I'm sure you mean decisions, right?
If so, you have to remember that if you're calling a loop in Ruby, you'll need some conditional logic to determine if it's actually populated with any data before trying to invoke it:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |decision| %>
<%= content_tag :p, decision.title %>
<% end %>
<% end %>
This will have to be matched by the appropriate controller code:
#app/controllers/decisions_controller.rb
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update, :destroy]
def index
#decisions = Decision.all
end
def show
end
def new
#decision = Decision.new
end
def create
#decision = Decision.new decision_params
#decision.save ? redirect_to(#decision) : render('new')
end
def edit
end
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find params["id"]
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
This will give you the ability to call #decisions and #decision in your views depending on which route you're accessing.
An important point is that when you say...
decisions/edit it gives me the following error: Couldn't find Decision with 'id'=edit'
... the issue is caused by the way in which Rails routing is handled:
Because Ruby/Rails is object orientated, each set of routes corresponds to either a collection of objects, or a member object. This is why routes such as edit require an "id" to be passed - they're designed to work on member objects.
As such, when you access any "member" route (decisions/:id, decisions/:id/edit), you'll have to provide an id so that Rails can pull the appropriate record from the db:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |descision| %>
<%= link_to "Edit #{decision.title}", decision_edit_path(decision) %>
<% end %>
<% end %>
I can explain a lot more - the above should work for you for now.