I have a Rails 3 blog. I want every Post to have a "Like" button. Clicking on it will save this action to the database and store to cookies on the user who has just liked the Post (for disallowing same action again).
I wrote a simple action to do this:
def like
render :nothing => true
id = params[:post_id]
cookies.permanent[:like_history] ||= []
unless cookies.permanent[:like_history].include? id
cookies.permanent[:like_history] << id
#post = Post.find(id)
#post.update_column(:likes, #post.likes + 1)
end
end
But I'm getting NoMethodError (undefined method '[]' for nil:NilClass) when I try to log things. It points to this line: cookies.permanent[:like_history] ||= [] as if cookies.permanent isn't an array.
Am I doing something wrong with cookies here?
Turns out, the ||= operator counts as "reading" by rails standards, which actually makes sense. You can't "read" with cookies.permanent[:symbol], that's for writing, you read with cookies[:symbol]. So I modified that line to read:
cookies.permanent[:like_history] = "" unless defined? cookies[:like_history]
I think you have something stored in cookies.permanent[:like_history] which is not an Array. So make it nil or covert to array using to_a and try your code.
def like
render :nothing => true
cookies.permanent[:like_history] = nil #or cookies.permanent[:like_history] = cookies.permanent[:like_history].to_a
id = params[:post_id]
cookies.permanent[:like_history] ||= []
unless cookies.permanent[:like_history].include? id
cookies.permanent[:like_history] << id
#post = Post.find(id)
#post.update_column(:likes, #post.likes + 1)
end
end
Once it works remove that line you added.
Related
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.
I am trying to learn and write an update API and to start small I am passing a single params in the API and and try to get the response.
the controller :
module Api
module V1
class OrderApiController < ApiController
def order_update
response = Hash.new
result = Hash.new
#order = Order.find(params[:id])
if #order.update_attributes(order_params)
result['order_id'] = order.id
response['result'] = result
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
private
def order_params
params.require(:order).permit( :id)
end
end
end
end
the api route in routes.rb is:
match 'mobile/order_update' =>'order_api#order_update'
The url link what I give is
http://localhost:3000/api/v1/mobile/order_update?key=docket&id=1
However this throws the following error
ActionController::ParameterMissing at /api/v1/mobile/order_update
param is missing or the value is empty: order
I dont know what am I doing wrong. I am new to Rails as well as API generation. Please help
This is caused by the order_params method, in which you're requiring order(expecting order to be a nested hash), whereas, you're not nesting it.
An approach you could take is to visit:
http://localhost:3000/api/v1/mobile/order_update?key=docket&order[id]=1
Also, I see you're setting #order instance variable, but in your control block(if #order.update_attributes), you're using a local variable which would give you another error.
I'd recommend you go through the Hartl Tutorial as there are a lot of things you'd be able to learn from there
UPDATE
Based on the new error mentioned in the comment, I think you should rather be visiting:
http://localhost:3000/api/v1/mobile/order_update?order[key]=docket&id=1
This is assuming your orders table has a column key based on the params being set
Also, change your order_params to:
private
def order_params
params.require(:order).permit( :key) #since you cannot update a primary key, but I guess you want to update the key column
end
The solution I used is as follows
In my order_api_controller.rb , I have Changed
def order_update
response = Hash.new
result = Hash.new
#order = Order.find(params[:id])
if #order.update_attributes(order_params)
result['order_id'] = order.id
response['result'] = result
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
and edited it to this
def order_update
response = Hash.new
result = Hash.new
debugger
#order = Order.find(params[:order][:id]) # passed the order symbol into params
if #order.update_attributes(order_params)
result['order_id'] = #order.id # Modified local variable to instance variable as per oreoluwa's suggestion
response['result'] = result
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
And used the url as Follows
http://localhost:3000/api/v1/mobile/order_update?key=docket&order[id]=1
This seems to do the trick
I have a fairly straightforward if else statement in a controller as follows:
if citation_array.blank?
flash.now[:error] = "There was a problem saving the publications selected!"
#user = current_user
render 'pubmed_search'
else
citation_array.each do |user_publication|
begin
publication = Publication.new
render_publication(user_publication)
publication.citation = user_publication
publication.user_id = current_user.id
publication.title = #title
publication.authors = #authors
publication.journal = #journal
publication.year = #year
publication.volume = #volume
publication.pages = #pages
if publication.save
next
end
rescue
next
end
end
#user = current_user
redirect_to current_user
return false
end
It is served an array of id's in citation_array and if there are values present it loops throught them saving each publication found by the id's in the array. The render_publication method instantiates the instance variables so don't be concerned with that.
My issue is this. Very rarely an id is fake or wrong and so this block fails at that point. I want to simple move on to the next id in the array and forget about the failed id. I don't even need to save an exception. I'm new to Ruby (coming from a PHP background).
I want to check if this syntax is correct. I am having trouble checking it in the rails console.
Syntax errors are easier to spot if the code is indented correctly.
if citation_array.blank?
flash.now[:error] = "There was a problem saving the publications selected!"
#user = current_user
render 'pubmed_search'
else
citation_array.each do |user_publication|
begin
publication = Publication.new
render_publication(user_publication)
publication.citation = user_publication
publication.user_id = current_user.id
publication.title = #title
publication.authors = #authors
publication.journal = #journal
publication.year = #year
publication.volume = #volume
publication.pages = #pages
if publication.save
next
end
rescue
next
end
end
#user = current_user
redirect_to current_user
return false
end
The syntax seems correct. Though an easier way to find out would have been just to run the code.
Some things in the code are not necessary though. After cleaning up your code a bit, it would look something like this with the same functionality.
#user = current_user
if citation_array.blank?
flash.now[:error] = 'There was a problem saving the publications selected!'
render 'pubmed_search'
else
citation_array.each do |user_publication|
begin
render_publication(user_publication)
Publication.create!( # create! here so that if something does go wrong, then you're not just ignoring it, but you can log it in your rescue block.
citation: user_publication,
user_id: current_user.id,
title: #title,
authors: #authors,
journal: #journal,
year: #year,
volume: #volume,
pages: #pages
# This hash should be extracted to a method.
)
rescue
# Just doing nothing here is valid syntax, but you should at least log your error.
end
end
redirect_to current_user
false # This can most likely be omitted as well since not many places care about the return value of a controller action.
end
Syntax for begin-rescue,
begin
your code...
rescue => e
Rails.logger.debug 'Exception is #{e}'
end
I have a piece of code in Rails,
def create
#registration = Registration.new(registration_params)
if #registration.save
redirect_to #registration.paypal_url(registration_path(#registration))
else
render :new
end
end
I took it from tutorial. But I need just in this line:
#registration.paypal_url(registration_path(#registration))
Now, about my own controller, feed_controller, where
def create
#feed = Feed.new(check_params)
end
In the view erb file I put:
#feed.paypal_url(feed_path(#feed))
In my feed.rb (model):
def paypal_url(return_path)
values = {
business: "merchant#gotealeaf.com",
cmd: "_xclick",
upload: 1,
return: "#{Rails.application.secrets.app_host}#{return_path}",
invoice: id,
amount: course.price,
item_name: course.name,
item_number: course.id,
quantity: '1'
}
"#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query
end
Rake routes:
feed GET /:locale/feed(.:format) feed#index
feed#create POST /:locale/feed/create(.:format)
feed#new feed_new GET /:locale/feed/new(.:format)
feed#destroy feed_destroy GET /:locale/feed/destroy(.:format)
feed#edit feed_edit GET /:locale/feed/edit(.:format)
feed#update feed_update GET /:locale/feed/update(.:format)
But it prints the next error:
undefined method `paypal_url' for <#Feed::ActiveRecord_Relation:0x007fee24f5fc98>
How can I fix it? What is the problem?
UPDATE
def index
#current_user_is = current_user.email
session[:email] = #current_user_is
session[:id] = current_user.id
unless (current_user.member.present?)
#member = Member.new(:user_id => current_user.id)
#member.save()
redirect_to '/feed'
else
#new_feed = Feed.new
#feed = Feed.where(:member_id => current_user.member.id)
#category = Category.all
render 'home/uploads'
end
end
Simply use def self.paypal_url(return_path) instead of def paypal_url(return_path).
Explanation
You ran into your problem by defining a Class Method instead of an Instance Method, there's other posts discussing this.
The basic difference is, when defining:
def self.get_some_url
# code to return url of an instance
end
you can easily get the desired url of any objects, as in a view:
<% #feeds.each do |feed| %>
<%= feeds.get_some_url %>
<% end %>
Now calling Feed.get_some_url on the class would make no sense. Which url of the thousands would it call?
But there is a lot of use for class methods (where you define the method without self as you did)
def get_top_5
# code to return the top 5 most viewed feeds
end
Since this has nothing to do with a single instance, you define it for the entire Class. Leading to this call: Feed.get_top_5, which makes perfectly sense.
The second problem was not understanding the difference between where & find, this post will help you out with that.
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