Rails: undefined method `any?' for nil:NilClass - ruby-on-rails

I'm getting these params in the console:
category_id" # => ["", "14", "16", "17"]
When I check if the array is empty or not inside a model with this:
#category = category_id.any?
it returns the following error:
undefined method `any?' for nil:NilClass
I have the strong params like this:
:category_id => []
Any ideas on how to check if this array is empty or not?
def new
#account = Account.new
#categories.build
end
def create
#account = Account.new(account_params)
respond_to do |format|
if #account.save
format.html { redirect_to accounts_path, notice: 'Account was successfully added.' }
format.json { render :index, status: :created, location: #account }
else
format.html { render :new }
format.json { render json: #account.errors, status: :unprocessable_entity }
end
end
end
def account_params
params.require(:account).permit(:name, :street, :street_2, :city, :state, :postal_code, :country, category_id: []
end
console results:
"account"=>{"name"=>"", "street"=>"", "street_2"=>"", "city"=>"", "state"=>"", "postal_code"=>"", "country"=>"", "category_id"=>["", "14", "15"]

#variables are not available inside the model. Neither are the params.
It's good practice to not try to do so but here are some ways to do it:
If you want to pass a value that is a database field/column:
# In the controller
account = Account.new(:category => #category)
# In this example, :category is a database field/column of the
# Account model, not sure if that's accurate for you.
If not then you gotta use an attr_accessor like this:
# In the model
class Account < ActiveRecord::Base
attr_accessor :category
#In the controller (this is equivalent to the first example)
account = Account.new
account.category = #category
Using attr_accessor doesn't save anything into the database, but the controller doesn't know the difference (which is good). This allows you to move data around like you wanted.

Related

NoMethodError: undefined method `get' for RSpec

First of all, this is my first experience with ruby. At this moment, I'm creating tests for the a Controller called Exporter in my application. The method of the Controller I want to test is this:
def export_as_json(equipments)
equipments_json = []
equipments.each {|equipment|
equipment_json = {
:id => equipment.id,
:title => equipment.title,
:description => equipment.description,
:category => equipment.category_id
}
equipments_json << equipment_json
}
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
So, when I try to create a request for this method using this:
RSpec.describe ExporterController, type: :controller do
get '/equipments/all', headers: { 'CONTENT_TYPE' => 'application/json' }, format: :json
expect(response.response).to eq(200)
end
inside the exporter_controller_test.rb file I'm receiving this error:
NoMethodError: undefined method `get' for RSpec::ExampleGroups::ExporterController:Class
This is one of the problems pretty much every one runs into at least once ;)
Step 1: Read the error message very carefully
NoMethodError: undefined method 'get' for RSpec::ExampleGroups::ExporterController:Class
Step 2: Remember the wording NoMethodError: undefined method get for RSpec::ExampleGroups::XXX:Class
Step 3: Solve it by making it an actual example
RSpec.describe ExporterController, "#index", type: :controller do
it "should respond with status: 200" do
get '/equipments/all', headers: { 'CONTENT_TYPE' => 'application/json' }, format: :json
expect(response.response).to eq(200)
end
end
You were simply missing the it block.
I know this is not an answer to your question. But, since you mentioned that you're new to ruby, I thought I would point out that your code could be simplified and prettified a bit.
First, you don't need to do equipments_json = [] and then equipments.each. That's what map is for:
def export_as_json(equipments)
equipments_json = equipments.map{|equipment| {
:id => equipment.id,
:title => equipment.title,
:description => equipment.description,
:category => equipment.category_id
}
}
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
Now, that hash you're putting into equipments_json is just a subset of equipment's attributes. So, use slice there to get the attributes you want:
def export_as_json(equipments)
equipments_json = equipments.map{|equipment| equipment.attributes.slice('id','title','description','category_id')}
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
That map line is still a little long, so, maybe put it into a do block (like you had with each):
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.attributes.slice('id','title','description','category_id')
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
And personally, I like using symbols instead of strings as my keys, so use with_indifferent_access so that you can use symbols:
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.attributes.with_indifferent_access.slice(:id, :title, :description, :category_id)
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
That line got a little to long again, so I think I would go ahead and wrap it:
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.
attributes.
with_indifferent_access.
slice(:id, :title, :description, :category_id)
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
Now, there are some different ways to get those attributes you want (e.g., modifying to_json). But, this will get the job done.
Hope that helps and good luck!
PS: I just noticed in your original hash, you're doing:
:category => equipment.category_id
if that's not a typo and you really want category instead of category_id, then you could do something like:
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.
attributes.
with_indifferent_access.
slice(:id, :title, :description).
merge!(category: equipment.category_id)
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
Also, the convention for hashes is to do title: equipment.title. :title => equipment.title absolutely works, but is not the current convention. This is a style guide for ruby, in case it helps.

Record get saved in russian instead of english while using I18n with Rails

i am pretty new to ROR and i'm currently trying I18n. I have a dropdown of payment options along with other details where a user would order something. Also, i have a static array from which the user will be able to select, defined in model
PAY_MODE = [:cheque, :credit_card, :purchase_order]
My locale file is
order:
back_to_product: "назад"
sent_email: "Поздравления. Ваш заказ был успешным размещены ."
PAY_MODE:
cheque: "чек об оплате"
credit_card: "Кредитная карта"
purchase_order: "Заказ на покупку"
select_prompt: "Выберите режим оплаты"
Everything works perfectlty, however while saving the same into the database, i get the error
undefined method `translation missing: en.cheque' for #Order:0x007fc0e00fc898>
The controller looks like
def create
#order = Order.new(order_params)
#order.add_line_items_from_cart(#cart)
respond_to do |format|
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
OrderNotifier.received(#order).deliver
format.html { redirect_to store_index_path, notice: t('order.sent_email') }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
private
def order_params
params.require(:order).permit(:name, :address, :email, :pay_type)
end
and i'm displaying the options in view through
<%= f.select :pay_type, t(Order::PAY_MODE, scope: 'order.PAY_MODE'),prompt: t('order.PAY_MODE.select_prompt') %>
i know, i'll have to search its corresponding english translation but how do i do that. It searches it in the en.cheque instead of en.order.PAY_MODE
Any help is appreciated. Thanx in advance.
UPDATE:
So, i solved my initial problem.Apparently, in model, i did not write validation in proper format. It should have been
PAY_MODE = [:cheque, :credit_card, :purchase_order]
validates :pay_type, :inclusion => { :in => I18n.t(PAY_MODE, scope: 'order.PAY_MODE'), :message => "is not a valid" }
and now the record is getting saved into the DB. However, now the problem is that it gets saved in Russain language, rather that english. What do i do to save it to english?
I see you use t(Order::PAY_MODE, scope: 'order.PAY_MODE') is not good. Scope option in t method can't use with a array. Let see http://guides.rubyonrails.org/i18n.html (4.1 part).
You can write same that:
f.select :pay_type, t('order.PAY_MODE').to_h, prompt:....

Trouble with virtual attribute for payments

Im currently trying to convert my price_in_cents field to the virtual attribute of price_in_dollars. I did some research and basically implemented everything from the railscast virtual attributes video, here is the link below.
https://www.youtube.com/watch?v=Tr7tD2GPiXU
At first, the column in my db was 'amount' for the money field. So I ran a
$ rails g migration add_price_in_cents_to_payments price_in_cents:integer
then,
$ rake db:migrate
My application is just a basic CRUD scaffold for creating new payments.
I added the getter setter methods into the payment.rb file, like so.
class Payment < ActiveRecord::Base
attr_accessible :amount, :price_in_dollars, :from, :to
# The price_in_dollars attribute is taking the place of :amount
def price_in_dollars
price_in_cents.to_d/100 if price_in_cents
end
def price_in_dollars=(dollars)
self.price_in_cents = dollars.to_d*100 if dollars.present?
end
end
And changed the form field to represent the new price_in_dollars attribute.
<div class="field">
<%= f.label :price_in_dollars %><br />
<%= f.text_field :price_in_dollars %>
</div>
But now when I submit a new payment, it returns the NoMethodError for my "new" and "create" methods in the payments controller. In the video, Bates does not even touch his controller. Here is my controller.
def new
#payment = Payment.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #payment }
end
end
# GET /payments/1/edit
def edit
#payment = Payment.find(params[:id])
end
# POST /payments
# POST /payments.json
def create
#payment = Payment.new(params[:payment])
respond_to do |format|
if #payment.save
format.html { redirect_to #payment, notice: 'Payment was successfully created.' }
format.json { render json: #payment, status: :created, location: #payment }
else
format.html { render action: "new" }
format.json { render json: #payment.errors, status: :unprocessable_entity }
end
end
end
Do I need to specify to pass in the (params[:price_in_dollars]) in all my CRUD methods in the controller?
I'm a novice with only about 3 weeks of rails knowledge. Please help anyway possible.

How to get name of model linked from another model in Rails?

I have set up 2 models in Rails:
class Category < ActiveRecord::Base
attr_accessible :name
has_many :platforms
end
and
class Platform < ActiveRecord::Base
attr_accessible :name, :url, :country
validates :name, :presence => true, :length => { :minimum => 5 }
validates :url, :presence => true, :length => { :minimum => 5 }
belongs_to :categories
end
This is my platform controller :
class PlatformsController < ApplicationController
# GET /platforms
# GET /platforms.json
def index
#platforms = Platform.all
#categories = Category.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #platforms }
end
end
# GET /platforms/1
# GET /platforms/1.json
def show
#platform = Platform.find(params[:id])
#categories = Platform.categories
respond_to do |format|
format.html # show.html.erb
format.json { render json: #platform }
end
end
# GET /platforms/new
# GET /platforms/new.json
def new
#platform = Platform.new
#categories = Category.all
respond_to do |format|
format.html # new.html.erb
format.json { render json: #platform }
end
end
# GET /platforms/1/edit
def edit
#platform = Platform.find(params[:id])
#categories = Category.find(:all)
end
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
##categories = Category.new(params[:name])
#categories = #platform.categories.create(params[:categories])
respond_to do |format|
if #platform.save
format.html { redirect_to #platform, notice: 'Platform was successfully created.' }
format.json { render json: #platform, status: :created, location: #platform }
else
format.html { render action: "new" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
# PUT /platforms/1
# PUT /platforms/1.json
def update
#platform = Platform.find(params[:id])
#categories = Category.find(:all)
respond_to do |format|
if #platform.update_attributes(params[:platform])
format.html { redirect_to #platform, notice: 'Platform was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
# DELETE /platforms/1
# DELETE /platforms/1.json
def destroy
#platform = Platform.find(params[:id])
#platform.destroy
respond_to do |format|
format.html { redirect_to platforms_url }
format.json { head :no_content }
end
end
end
I do not understand what I do wrong, but it doesnt correctly assign categories to platforms, and also in the platforms index view, when I try to use :
<%= platform.categories %>
it gives me error cannot find Category with id= "and here the respective id"
I am really confused since I followed tutorial for this one.
I use Rails 3.2.8
Without your view, I can't say for sure what it is you're trying to do exactly. Most importantly, what is in your params[:categories] hash? Given the name, it sounds like you intended for it to be multiple categories. However, your code is written as if you intended it to be a single set of attributes which describe one Category.
Since I can't say for sure what you want to do, I'll answer your question by explaining what you are doing. Maybe that will help you figure out how to fix it.
Your create code currently looks like this:
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
##categories = Category.new(params[:name])
#categories = #platform.categories.create(params[:categories])
The first line creates the new Platform and is easy. Skipping over the comment to the third line. This is probably what's tripping you up.
You are selecting the associations for your newly created Platform and trying to create a new category with attributes as stored in the params[:categories] hash. I'm afraid this is not allowed. (I think it throws an ActiveRecord::RecordNotSaved exception, but I could be wrong.) You can not create on a #platform which hasn't been persisted yet. Instead, I think you want build.
Here is the relevant documentation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
The difference between create and build is that build just sets up the association without actually saving it to the database yet. create saves it immediately. The nice thing about build is that you don't actually have to save it yourself. It tags along for free when you call #platform.save or #platform.update_attributes. Also, save is automatically wrapped in a transaction, so it won't create the new Category if it fails to create the new Platform for whatever reason.
The next interesting thing is that you are assigning the result of your create to #categories. I don't think this is what you want either. You don't need to save the new Category because it tags along with your #platform. However, if the save of the platform fails, then you are going to re-render your new view with this value of #categories whereas in new you set #categories = Category.all. This could certainly cause some confusion on the new view after a failed create.
In summary, I think your create code should look something like the following.
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
#platform.categories.build(params[:categories])
respond_to do |format|
if #platform.save
format.html { redirect_to #platform, notice: 'Platform was successfully created.' }
format.json { render json: #platform, status: :created, location: #platform }
else
#categories = Category.all
format.html { render action: "new" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
If you're params[:categories] is not a hash of category attributes and is actually a comma delimited string of category names, then you would want to do something like the following instead of my second line above:
params[:categories].split(",").each do |category|
#project.categories.build(name: category)
end
You may also want to check out accepts_nested_attributes_for which can DRY out your controller even more.
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I hope that helps.

Rails: undefined method `total_price' for nil:NilClass

I'm building a concert ticket sales application with Rails 3.0.4, working primarily with the Agile Web Development tutorial (http://pragprog.com/titles/rails3/agile-web-development-with-rails) and trying to incorporate Ryan Bate's order purchase method (http://railscasts.com/episodes/146-paypal-express-checkout). Everything works with the following in orders_controller.rb:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
#order.ip_address = request.remote_ip
respond_to do |format|
if #order.save
Notifier.order_received(#order).deliver
format.html { redirect_to(calendar_url, :notice => 'Thank you for your order.') }
format.xml { render :xml => #order, :status => :created, :location => #order }
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
But when I add "&& #order.purchase" to the conditional clause, with the order.rb model as follows:
class Order < ActiveRecord::Base
#...
belongs_to :cart
#...
def price_in_cents
(cart.total_price*100).round
end
def purchase
response = GATEWAY.purchase(price_in_cents, credit_card, purchase_options)
cart.update_attribute(:purchased_at, Time.now) if response.success?
response.success?
end
#...
end
I receive an "undefined method `total_price' for nil:NilClass" error. I can get around this by adding
#order = current_cart.build_order(params[:order])
to the orders "create" method, but this messes up the "order_received" notification by somehow preventing the pertinent order information (in this case "#order.line_items") from rendering in the e-mail text.
The "cart" object is being set to nil somewhere along the way, but removing
Cart.destroy(session[:cart_id])
from the order "create" method does not fix the problem.
Anyone got a clue for this noob?
It doesn't look like the Cart object is ever actually specified in the belongs_to relation, you need to either do #order.cart = current_cart, or current_cart.order = Order.new, or something along those lines.

Resources