Rails simple_form_for No route matches, missing [:id] - ruby-on-rails

Straight to the point, a bit confusing here
this is my code
routes.rb
Rails.application.routes.draw do
namespace :main, path: ':master_url' do
root 'sites#index'
namespace :dashboard do
root 'dashboards#index'
resources :masters
end
get "/:action" => 'sites#:action'
end
root 'main/sites#index'
end
rake routes
Prefix Verb URI Pattern Controller#Action
main_root GET /:master_url(.:format) main/sites#index
main_dashboard_root GET /:master_url/dashboard(.:format) main/dashboard/dashboards#index
main_dashboard_masters GET /:master_url/dashboard/masters(.:format) main/dashboard/masters#index
POST /:master_url/dashboard/masters(.:format) main/dashboard/masters#create
new_main_dashboard_master GET /:master_url/dashboard/masters/new(.:format) main/dashboard/masters#new
edit_main_dashboard_master GET /:master_url/dashboard/masters/:id/edit(.:format) main/dashboard/masters#edit
main_dashboard_master GET /:master_url/dashboard/masters/:id(.:format) main/dashboard/masters#show
PATCH /:master_url/dashboard/masters/:id(.:format) main/dashboard/masters#update
PUT /:master_url/dashboard/masters/:id(.:format) main/dashboard/masters#update
DELETE /:master_url/dashboard/masters/:id(.:format) main/dashboard/masters#destroy
main GET /:master_url/:action(.:format) main/sites#:action
root GET / main/sites#index
main/dashboard/masters_controller.rb
class Main::Dashboard::MastersController < ApplicationController
before_action :all_masters, only: [:index, :create, :update, :destroy]
before_action :set_master, only: [:edit, :update, :destroy, :show]
before_action :init_master
layout 'main'
respond_to :html, :js
def new
#master = Master.new
end
def create
#master = Master.create(conf_params)
end
def edit
end
def update
#master.update_attributes(conf_params)
end
def destroy
#master.destroy
end
private
def all_masters
#masters = Master.all
end
def set_master
#master = Master.find(params[:id])
end
def conf_params
params.require(:master).permit(:title,:url)
end
def init_master
#master_url = Master.find_by_url(params[:master_url])
end
end
this is what i did when partially render _form_master.html.erb , which i called on new.js.erb and edit.js.erb
$('#form-master').html("<%= j (render 'form_master', :master => #master) %>");
_form_master.html.erb contents
<%= simple_form_for [:main, :dashboard, #master], remote: true do |f| %>
<%= f.input :url, label: 'URL' %>
<%= f.input :title, label: 'Title' %>
<%= f.button :submit %>
<% end %>
PROBLEMS:
When i called new master, with this
<%= link_to new_main_dashboard_master_path, remote: true do %>
the page rendered flawlessly, and create master doing good
but when i called edit , with this line
<%= link_to :controller=>"masters",:action =>"edit",:id => master do %>
which was my problem earlier , rails returning an error with messages:
ActionView::Template::Error (No route matches {:action=>"show", :controller=>"main/dashboard/masters", :format=>nil, :id=>nil, :master_url=>#<Master id: 4, url: "test", title: "Test Aja", created_at: "2015-02-02 08:55:07", updated_at: "2015-02-02 08:55:07">} missing required keys: [:id]):
i am explicitly render values of master.id and master.title to the page, which is returning correct values.
Q: How do i fix this ? i was trying a couple of times, from changing the locals pass on js.erb, change the #model to just model, still not work
Any help are appreciated, thanks ! :)

Here we have post = Post.find(params[:post_id])
from http://blog.8thcolor.com/en/2011/08/nested-resources-with-independent-views-in-ruby-on-rails/
Don't you have anything like that for your model?
You need an :id of some kind don't you?
This may be out of context but I'm not going to try to reuse your code.
And it may be too simple to try something like
render plain: params[:someparam].inspect
but it helps me.
http://codefol.io/posts/Deep-Rails-Understanding-HashWithIndifferentAccess-Understanding-the-Params-Hash

Related

Ruby on Rails - ActionController::UrlGenerationError

I am developing a project in Ruby on rails 5.2, and in this route it tells me that I have an error and that the specified route is not found. but when checking, the route is there or at least I think so.
Here's my routes.rb:
resources :checkin do
post :get_barcode, on: :collection
end
checkin_controller.rb:
class CheckinController < ApplicationController
def index
#checkin = CheckIn.all
end
def show
end
def new
#checkin = CheckIn.new
#checkin.upc = params[:upc]
end
def edit
end
def update
end
def destroy
end
def get_barcode
#checkin = Merchant.find_or_initialize_by(upc: params[:upc])
unless #checkin.new_record?
redirect_to #checkin
else
redirect_to new_product_path(upc: params[:upc])
end
end
end
And my link in my view:
<%= link_to "Check-In", checkin_path, :class => "nav-link" %>
here's a image of my error page:
If you run rake routes in your console, you'll see that your routes are:
get_barcode_checkin_index POST /checkin/get_barcode(.:format) checkin#get_barcode
checkin_index GET /checkin(.:format) checkin#index
POST /checkin(.:format) checkin#create
new_checkin GET /checkin/new(.:format) checkin#new
edit_checkin GET /checkin/:id/edit(.:format) checkin#edit
checkin GET /checkin/:id(.:format) checkin#show
PATCH /checkin/:id(.:format) checkin#update
PUT /checkin/:id(.:format) checkin#update
DELETE /checkin/:id(.:format) checkin#destroy
As you can see, the checkin_path expects an id, which you are not providing here:
<%= link_to "Check-In", checkin_path, :class => "nav-link" %>
Your error probably says something about missing id, but you don't provide the error in your question, so we can't see exactly what it says.
BTW, by convention, CheckinController should probably be CheckinsController. And your routes should probably be:
resources :checkins do
post :get_barcode, on: :collection
end
As you said in one of your comments, you're expecting the URL for the index page?
Then instead of
<%= link_to "Check-In", checkin_path, :class => "nav-link" %>`
you need to use
<%= link_to "Check-In", checkin_index_path, :class => "nav-link" %>

Undefined Method 'underscore'

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

Custom url helper method in presenter causing test error

I'm writing tests for a presenter class and am getting the following error when running them, coming from a custom url helper method.
Error:
PersonPresenterTest#test_0011_should get email icon:
ArgumentError: arguments passed to url_for can't be handled. Please require routes or provide your own implementation
app/helpers/people_helper.rb:4:in `link_fa_to'
app/presenters/base_presenter.rb:14:in `method_missing'
app/presenters/person_presenter.rb:54:in `email_icon'
test/presenters/person_presenter_test.rb:59:in `block in <class:PersonPresenterTest>'
person_presenter_test.rb
class PersonPresenterTest < ActionView::TestCase
add_template_helper(PeopleHelper)
def setup
#person = FactoryGirl.create(:person)
#presenter = PersonPresenter.new(#person, view)
end
test 'should get email icon' do
expected = '<a class="icon-link" href="/people/1"><i class="fa fa-envelope fa-1x"></i></a>'
actual = #presenter.email_icon
assert_equal(expected, actual)
end
end
person_presenter.rb
class PersonPresenter < BasePresenter
presents :person
def email_icon
link_fa_to('envelope fa-1x', person) if person.email.present?
end
end
base_presenter.rb
class BasePresenter
def initialize(object, template)
#object = object
#template = template
end
def self.presents(name)
define_method(name) do
#object
end
end
def method_missing(*args, &block)
#template.send(*args, &block)
end
end
people_helper.rb
module PeopleHelper
def link_fa_to(icon_name, link)
link_to content_tag(:i, '', class: "fa fa-#{icon_name}"), link, class: "icon-link"
end
end
routes.rb
Rails.application.routes.draw do
resources :organisations
resources :people
resources :reset_password, only: [:edit, :update, :new, :index, :create]
resources :dashboards, only: [:index]
resources :users
get 'profile' => 'users#show', as: :profile
controller :sessions do
get 'login' => :new
post 'login' => :create
end
get '/auth/:provider/callback', to: 'sessions#create'
get "sessions/create"
get 'sessions' => "sessions#destroy", as: :logout
root 'sessions#new'
end
I can test presenter methods which use link_to successfully, and would expect methods calling link_fa_to to 'fall' through to the template in the same way. This is only affecting tests, the method works perfectly when running the application.
Any help much appreciated.
edit:
If I remove add_template_helper(PeopleHelper) from person_presenter_test.rb, the error is:
Error:
PersonPresenterTest#test_0011_should get email icon:
NoMethodError: undefined method `link_fa_to' for #<#<Class:0x007fcdb6bb19e0>:0x007fcdb6bb15a8>
Did you mean? link_to
app/presenters/base_presenter.rb:14:in `method_missing'
app/presenters/person_presenter.rb:54:in `email_icon'
test/presenters/person_presenter_test.rb:58:in `block in <class:PersonPresenterTest>'
edit 2:
After updating people_helper.rb to the following in response to Shishir's answer, the error message remains the same:
module PeopleHelper
def link_fa_to(icon_name, link)
link_to link, class: "icon-link" do
content_tag(:i, '', class: "fa fa-#{icon_name}")
end
end
end
The method content_tag returns a string (actually it's an ActiveSupport::SafeBuffer, but you can handle it like a string), so that's not your problem.
It seems that you are calling your helper method with an instance of ActiveRecord::Base instead of an url:
link_fa_to('envelope fa-1x', person) if person.email.present?
vs:
def link_fa_to(icon_name, link)
link_to content_tag(:i, '', class: "fa fa-#{icon_name}"), link, class: "icon-link"
end
You could try to update your code to something like this:
link_fa_to('envelope fa-1x', person_path(person)) if person.email.present?
The syntax of link_to you are using doesn't accept content_tag as parameter. It only accepts string.
link_to content_tag(:i, '', class: "fa fa-#{icon_name}"), link, class: "icon-link"
Try
link_to link do
content_tag(:i, '', class: "fa fa-#{icon_name}")
end

Route to the wrong URL

I'm getting
ActionView::Template::Error (No route matches {:action=>"to_approve", :controller=>"microposts", :id=>nil} missing required keys: [:id]):
No route matches {:action=>"to_approve", :controller=>"microposts",
:id=>nil} missing required keys: [:id]
But it make no sense because I'm routing to different route
route.rb
match '/microposts/:id/approve', to: 'microposts#to_approve' , via: [:get, :post], as: 'approve_micropost'
match '/microposts/to_approve', to: 'microposts#approve' , via: :get
controller.rb
def show
#tag = Tag.find(params[:id])
#microposts = #tag.microposts
end
show.html.rb
<%= render #microposts %>
_micropost.html.rb - Here is the line it shows the error on
<% if is_an_admin? %>
<%= link_to "Approve", approve_micropost_path(micropost.id) %>
<% end %>
micropost_controller.rb
def approve
#microposts = Micropost.unapproved
end
def to_approve
micropost = Micropost.unapproved_by_id(params[:id])
if micropost.update_attributes(approved: true)
flash[:success] = "Approved!"
else
flash[:error] = "Not approved!"
end
redirect_back_or microposts_to_approve_path
end
micropost.rb
default_scope { where(approved: true).order('microposts.created_at DESC')}
def self.unapproved
self.unscoped.all.where(approved: false).order('microposts.created_at DESC')
end
def self.unapproved_by_id(id = nil)
self.unscoped.all.where(id: id)
end
You can see it tries to create microposts_to_approve_path with :id which obviously not exists, but I wrote approve_micropost_path.
What am I missing?
Plus, in route for microposts_to_approve_path I permitted [:get, :post] although I only want to allow access to to_approve method through on_click events (post?) and there is no view for it.. How should I rewrite this?
rake routes:
microposts POST /microposts(.:format) microposts#create
micropost DELETE /microposts/:id(.:format) microposts#destroy
approve_micropost GET|POST /microposts/:id/approve(.:format) microposts#to_approve
microposts_to_approve GET /microposts/to_approve(.:format) microposts#approve
On the error page, the parameters:
Request
Parameters:
{"id"=>"4",
"name"=>"tag name"}
Solution
The problem was because I use default_scope and than the object the I was working with wasn't OK.
Before fix
#microposts = #tag.microposts ##microposts is CollectionProxy
After
#microposts = #tag.microposts.all ##microposts is AssociationRelation
Once I've change to .all the problem was solved.
BTW, is it a bug? In my prespective default_scope shouldn't change the default behavior..
If you want it to only respond to a post, then try changing your link to use the method 'post'.
<% if is_an_admin? %>
<%= link_to "Approve", approve_micropost_path(micropost.id), method: :post %>
<% end %>
Conversely, if you want it to route to microposts#to_approve on a get, then make your link explicitly call get.
<% if is_an_admin? %>
<%= link_to "Approve", approve_micropost_path(micropost.id), method: :get %>
<% end %>
Then you should be routed to microposts#to_approve using POST. However, since you re allowing either action to take place, make sure in your to_approve action, you'll have to check for request type. Such as:
#microposts controller
def to_approve
if request.post?
# Do post related things
else
# Do get related things
end
end
Sidenote *** using the to_approve action on the approve url, and using the approve action on the to_approve url is confusing, things you might forget why-you-did-what-you-did when you look at the code 6 months from now.
Edit
An alternative might be to break apart the routes so you aren't calling an if statement in your controller.
route.rb
post '/microposts/:id/approve', to: 'microposts#approve', as: 'approve_micropost'
get '/microposts/:id/to_approval', to: 'microposts#to_approve', as: 'micropost_approvals'
resources :microposts

Weird Rails 4 RESTful URL after form error

I have a RESTful controller inside of a namespace called dashboard, so my URL looks like this:
/dashboard/member
/dashboard/member/edit
Something weird is happening when I submit the member form with a validation error... it shows the error like it's suppose to, but when it goes to the PATCH url "/dashboard/member" it comes with an ".2" in the end:
/dashboard/member.2
the "2" is the ID of the record.
The funny thing is that I did everything correctly and it works great, this ".2" is the only thing that is bothering my head.
My Controller
class Dashboard::MembersController < ApplicationController
load_and_authorize_resource :class => Member
before_filter :authenticate_member!
def show
end
def edit
#member ||= current_member
end
def update
#member ||= current_member
if #member.update_attributes(member_params)
flash[:success] = "Profile updated"
redirect_to dashboard_member_path
else
render "edit"
end
end
private
def member_params
params.require(:member).permit(:first_name, :last_name, :address, :city, :state, :country, :zipcode, :home_phone, :cell_phone)
end
end
My Route
namespace :dashboard do
resource :member, only: [:show, :edit, :update]
end
If you meant to use resource :member (instead of resources :member) then you should know that it always looks up without referencing an ID.
Here is how the routes would be created without id's:
edit_dashboard_member GET /dashboard/member/edit(.:format) dashboard/members#edit
dashboard_member GET /dashboard/member(.:format) dashboard/members#show
PATCH /dashboard/member(.:format) dashboard/members#update
PUT /dashboard/member(.:format) dashboard/members#update
When you are sending a PATCH request make sure that you don't pass an argument with it. If you pass an argument then it would be interpreted as format(like .html, .js etc). In your case you passed an argument as 2 or a member with an id 2
For eg:
PATCH request to dashboard_member_path(2)
The route was matched against PATCH /dashboard/member(.:format) dashboard/members#update
2 is interpreted as (.:format) because there is no :id part.
I had to remove the #member from my form, so... I had this:
<%= form_for(#member, url: dashboard_member_path(#member), html: {method: "patch", class: "form-horizontal"}) do |f| %>
And it became this:
<%= form_for(#member, url: dashboard_member_path, html: {method: "patch", class: "form-horizontal"}) do |f| %>
Now, there's IDs being passed to the URL.
Thanks guys!

Resources