Rails 5.2
In my inventories_controller.rb, I have the following:
before_action :fetch_product, only: [:show]
def show
........
end
def fetch_product
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
This works fine, when I do: http://0.0.0.0:3000/sku/12345678
I am trying to implement search functionality, so I modified nventories_controller.rb as follows:
def fetch_product
if params.has_key?(:search) && !params[:search].blank?
product = Product.find_by_sku(params[:search])
if !product
params = params.except[:search]
redirect_to product_show_path, alert: 'Product was not found'
end
params = params.merge!(:sku, product.sku)
end
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
When I do: http://0.0.0.0:3000/sku/12345678
I get an instant error message:
undefined method `has_key?' for nil:NilClass
Using my debugger, I find that on entry into the fetch_product method, params is nil
Any idea what's going on?
params = params.merge!(:sku, product.sku) modifies the hash in place and returns nil, don't do that assignment, just call params.merge! (if you still want to do the assignment, remove the "!").
Personally, I wouldn't modify the params hash unless it's really really needed, I would use another variable.
Related
Every time I submit the form I get this error: undefined method `each' for nil:NilClass. If everything is correct I can submit the form without any problems, but when there is one thing missing it gives me that error. The error sends me to this line:
views/users/new.html.haml
- #subscriptions.each do |fbs|
= fb.radio_button :subscription_id, fbs.id, class: 'radiobtn', required: true
controllers/users_controller.rb
def new
#user = Users::Business.new
#subscriptions = Businesses::Subscription.all
end
def create
#user = Users::Business.new(user_params)
if #user.save
sign_in(#user)
else
render :new
end
end
Assigns subscriptions in a create action too:
before_filter :set_subscriptions, only: %w(new create) #for edit and update if needed
private
def set_subscriptions
#subscriptions = Businesses::Subscription.all
end
Or add #subscriptions = Businesses::Subscription.all directly to create action after the saving is failed and you re-render new form.
It is happening because there is no value persisted with that model .
You can check the data into the rails console
like ,
$rails console
> Businesses::Subscription.count
And if you see that there is no data then you can write a if ... end block to check for null value and handle it.
I have a controller and need to pass objects which validation fail to another controller action to process them and show to user:
class PersonController
def save_persons
invalid_persons = ... #array of Person.new objects
flash[:invalid_persons] = invalid_persons
redirect_to action: :fix_errors
end
def fix_errors
invalid_persons = flash[:invalid_persons]
invalid_persons.each do |invalid_person|
puts invalid_person.errors #here i get exception!
end
end
end
When i try to puts invalid_person.errors i get error: undefined method errors for #Hash:0x007fd594e79098>. It seems that rails transform my objects array to some hash
Can you suggest me, what is the right way to pass some object through flash?
Edit: it turns out I made a very simple mistake and had a Template that was associated with a LocalTemplate id that no longer existed. If anyone has this problem and thinks that they somehow are unable to unable to associate the id of another model in their update action, make sure that you didn't accidentally delete the parent object causing that id to no longer exist!
The code below, while dramatically simplified did work for me.
I have a Template model in my rails app. It has a method "data" defined in it.
I am able to access this method in the create and show actions with #template.data, however when using the same #template.data in the update action of my controller I get a no method error because I am not showing the correct local template id to it. This line can be found in the model where it reads base_data = YAML.load(local_template.data)
I stored an id of the associated local_template when initially saving a new template, but how can I make sure I reference that id again in the update action so I do not get a no method error?
Here is a simplified version of the Template model and controller
Model:
class Template < ActiveRecord::Base
def data
base_data = YAML.load(local_template.data)
# couldn't pass the correct LocalTemplate here because
# the local_template_id I had in my Template model no
# longer existed. Changing the id to a LocalTemplate
# that did exist fixed the issue.
end
end
Controller:
class TemplatesController < ApplicationController
def index
#business = Business.find(params[:business_id])
#templates = #business.templates.all
end
def new
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build
end
def create
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build(template_params)
if #template.save
#template.data #works fine here
redirect_to business_url(#template.business_id)
else
render 'new'
end
end
def show
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
#template.data #works fine here too
end
def edit
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.find(params[:id])
end
def update
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
if #template.update_attributes!(pass_template_params)
Api.new.update_template(#template.data.to_json) #this is where I had a problem
redirect_to business_url(#template.business_id)
else
render 'edit'
end
end
end
You are mixing a lot. There is a lot to refactor in your controller...
First of all, your TemplatesController should be about the template resources, but your controller looks more like a BusinessesController. In general your update action for example should look more like:
def update
#template = Template.find params[:id]
#template.attributes = template_params # though this should raise a NoMethodError, because you dind't define it; I'd prefer params[:template] if possible
if #template.save
redirect_to business_url(#template.business_id)
else
#local_templates = LocalTemplate.all
render 'edit'
end
end
Instantiating #business and #local_templates makes non sense, because you don't use it at all. Speed up your responses if you can! :)
Fixed that, there is no need for the overhead of a nested resource in update (as you did).
If saving #template fails for validation reasons, you better should load the business object late by:
#template.business
in your /templates/edit.html.erb partial. Then you also do not need a nested route to your edit action... You see, it cleans up a lot.
As a general guideline you should create as less as possible controller instance variables.
If you cleaned up your controller and views, debugging your data issue will be easier.
I assume:
local_template
in your Template model to be an associated LocalTemplate model object. So it should no issue to call that anywhere if you ensured the referenced object exists:
class Template < ActiveRecord::Base
def data
return if local_template.nil?
YAML.load(local_template.data)
end
end
or validate the existence of the local_template object. or even b
You should confirm #template is not nil, if #template is nil, you can't use data method.
1.9.3-p547 :024 > nil.data
NoMethodError: undefined method `data' for nil:NilClass
from (irb):24
from /Users/tap4fun/.rvm/rubies/ruby-1.9.3-p547/bin/irb:12:in `<main>'
And you should use update_attributes!, it can raise an exception if record is invalid.
You can do like this.
if #template
#template.update_attributes!(template_params)
#template.data
end
i have my own RegistrationsController, which inherits from Devise. In update I want to send params to my User method like above:
def update
params = resource.delete_invoice(params) if resource.check_invoice(params[:user])
if params[:user][:name].blank?
flash[:alert] = t 'devise.registrations.wrong_user_name'
redirect_to action: 'edit' and return
end
super
end
Unfortunatelly i get error:
NoMethodError in Users::RegistrationsController#update, undefined method `[]' for nil:NilClass
It's strange because I can normally write puts params and it's not empty. How can I resolve this?
Try this:
unless params[:user].blank?
if params[:user][:name].blank?
#do your stuff
end
end
The error here is that it is trying to call [] on a nil object, as it is very unlikely that params or flash is nil, the only other place you call [] is on params[:user] which suggests that this is nil.
Couple of options, check if params[:user] is nil or use try:
if params[:user].nil? or params[:user][:name].blank?
flash[:alert] = t 'devise.registrations.wrong_user_name'
redirect_to action: 'edit' and return
end
Alternatively:
if params[:user].try(:fetch, :name).try(:blank?)
flash[:alert] = t 'devise.registrations.wrong_user_name'
redirect_to action: 'edit' and return
end
Which I think is neater. NB try is a rails method, not ruby.
Are you sure the resource is built when you run update? In Rails 4 the build_resource is being built inside that method devise/registrations_controller. Probably you want to use resource_class?
I have a route that matches /edit_account => accounts#edit since the account id isn't provided it's supposed to use the current user id and the account#edit method is shared with /accounts/[:id]/edit.
class AccountController < ApplicationController
...
def edit
# This doesn't work:
params = retrieve_id_if_missing(params)
# This works:
# aHash = params
# params = retrieve_id_if_missing(aHash)
end
def retrieve_id_if_missing(params)
# raise params.inpect => returns nil at this point
if params[:id].nil? and !logged_in?
redirect_to root_path
else params[:id].nil?
params[:id] = current_user.id
end
params
end
end
The problem I am having is that params, when passed to the class method, retrieve_id_if_missing, is becoming nil. However, if I assign params to another variable. e.g., aHash, before passing it to retrieve_id_if_missing it will contain the expected data, {"action" => "edit", "controller" => "account"}.
I've tried to search for a reason but have come up short, can someone explain to me why this is happening?
Have you tried
class AccountController < ApplicationController
...
def edit
retrieve_id_if_missing
end
def retrieve_id_if_missing()
if params[:id].nil? and !logged_in?
redirect_to root_path
else params[:id].nil?
params[:id] = current_user.id
end
params
end
end
I am fairly sure params will be in scope in the method.
Anyhow, check out gem devise for this. it should have everything you want and more
With devise you can just use
before_filer :authenticate_user!
At the top of your controller
https://github.com/plataformatec/devise
The Ruby interpreter is treating params as a local variable and initializing it with nil when it sees the assignment. This happens before it executes the retrieve_id_if_missing.
This is why explicitly assigning a value to the local variable before calling the method avoids the error, because the initialization to nil by Ruby doesn't happen.
The following examples demonstrate this:
Example #1
def foo(bar)
puts "foo bar: #{bar.class}"
end
bar = foo(bar) # => nil
puts "bar: #{bar.class}"
# Outputs:
# foo bar: NilClass
# bar: bar: NilClass
Example #2
a = a # => nil
puts "a: #{a.class}"
# Outputs:
# a: NilClass
Example #3
a = 123 if a # => nil
puts "a: #{a.class}"
# Outputs:
# a: NilClass
References:
Why is a = a nil in Ruby?
Ruby interpreter initializes a local variable with nil when it sees an
assignment to it. It initializes the local variable before it executes
the assignment expression or even when the assignment is not reachable
(as in the example below). This means your code initializes a with nil
and then the expression a = nil will evaluate to the right hand value.
a = 1 if false a.nil? # => true The first assignment expression is not
executed, but a is initialized with nil.
Ruby: method inexplicably overwritten and set to nil
Here's another example:
a = 123 if a # => nil a # => nil We shouldn't be able to say if a
because we never set a, but Ruby sees the a = 123 and initializes a,
then gets to if a at which point a is nil
I'd consider it a quirk of the interpreter, really. Gary Bernhardt
makes fun of it in wat (https://www.destroyallsoftware.com/talks/wat)
with a = a
While I cannot answer why your params object would be overridden with the code provided, here are some thoughts.
class AccountController < ApplicationController
before_filter :retrieve_id_if_missing, only: :edit
def edit
# You'll find params[:id] prepopulated if it comes here,
# else the request has been redirect
end
protected
# There should be no need to pass the params object around, it should be accessible everywhere
def retrieve_id_if_missing
if logged_in?
params[:id] ||= current_user.id # conditional assignment will only happen if params[:id] is nil
end
# Redirect to root if params[:id] is still blank,
# i.e. user is not logged in and :id was not provided through route
if params[:id].blank?
flash[:alert] = 'You need to be logged in to access this resource.'
return redirect_to root_url # early return!
end
end
end