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
Related
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)
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
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.
I've browse a lot of question over SO for this error and I didn't find the correct answer unfortunately.
I've created the class Avis (which, in French, takes an s no matter if it's singular or plural.) using rails generate scaffold Avis --force-plural.
As it's part of the Formation class, here's the route.rb file (part of):
resources :formations do
resources :avis
end
Here's the Avis controller:
class AvisController < ApplicationController
before_action :set_avi, only: [:show, :edit, :update, :destroy]
before_action :set_formation
#On indique que les tests sont fait sur l'autorisation des utilisateurs
load_and_authorize_resource :formation
# gestion du layout
layout :sections_layout
#layout = 'back'
respond_to :html
def sections_layout
#layout
end
def index
#avis = Avis.where(:formations_id => Formation.find_by(:id => formation_params))
respond_with(#avis)
end
def show
respond_with(#avi)
end
def new
#avi = Avis.new
respond_with(#formation, #avi)
end
def edit
end
def create
#avi = Avis.new(avis_params)
#avi.save
respond_with(#avi)
end
def update
#avi.update(avis_params)
respond_with(#avi)
end
def destroy
#avi.destroy
respond_with(#avi)
end
private
def set_avi
#avi = Avis.find(params[:id])
end
def avis_params
params[:avi]
end
def formation_params
params.require(:formation_id)
end
def set_formation
#formation = Formation.find_by(:id => params[:formation_id])
if #formation == nil
redirect_to forbidden_path :status => 403
end
end
end
When I try to create a new Avis I get this error:
ActionView::Template::Error (undefined method `avis_index_path' for #<#<Class:0x007ffa6052aa78>:0x007ffa60528b88>):
1: <% puts #avi%>
2:
3: <%= form_for(#avi) do |f| %>
4: <% if #avi.errors.any? %>
5: <div id="error_explanation">
6: <h2><%= pluralize(#avi.errors.count, "error") %> prohibited this avi from being saved:</h2>
app/views/avis/_form.html.erb:3:in `_app_views_avis__form_html_erb__481767065103572634_70356711120220'
app/views/avis/new.html.erb:3:in `_app_views_avis_new_html_erb___1382100742020377330_70356626529020'
app/controllers/avis_controller.rb:29:in `new'
How can I fix this?
Avi is a nested resource of Formations your form_for should look like this form_for(#formation, #avi), which will use the correct formation_avis_path.
For more information on nested resources check out Rails Routing from the Outside In
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