Cannot convert data from cookies - ruby-on-rails

I want to make a method that will count the number of user actions on the site.
I have the code:
def actions_counter
if cookies[:"actions"] != nil
cookies[:"actions"].to_i += 1
else
cookies[:"actions"] = 0
end
end
But for some reason this does not work.
NoMethodError in PostsController#show
undefined method `to_i=' for "1":String Did you mean? to_i to_r to_f to_s to_d to_c

Just change your method to
def actions_counter
if cookies[:actions]
cookies[:actions] = cookies[:actions].to_i + 1
else
cookies[:actions] = 0
end
end
The issue is that Ruby understands cookies[:"actions"].to_i += 1 as
cookies[:"actions"].to_i = cookies[:"actions"].to_i + 1
# and this ^^^^^^^^ doesn't make sense

Related

ruby scripts - (NameError) undefined local variable or method `null' for main:Object

My code
require "json"
require "erb"
flowvar = $workflowvar
path = 'src/main/resources/'+$workflowvar+'.drl'
rule = ""
File.open(path,"w") do |f|
f.puts "package com.drools.demo\;"+"\n"+"import org.mule.MessageExchangePattern\;"+"\n"+"import com.drools.demo.cashliquidassets\;"+"\n"+"global org.mule.module.bpm.MessageService mule\;"+"\n"+
"dialect \"mvel\""+"\n"+"dialect \"java\""+"\n"+"declare cashliquidassets"+"\n"+"#role\(\'event\'\)"+"\n"+"end"+"\n"
f.close
end
def concateRule(attribute,val)
if(val==null || val=="")
return "";
end
if(attribute != null)
if (attribute == "taxonomy_code" || attribute == "parent_taxonomy_code" || attribute == "report_name")
return "";
end
end
if val.start_with('<>')
return attribute+" != "+val[3,val.length].strip
elsif val.start_with('>')
return attribute+" > "+val
elsif val.start_with('<')
return attribute+" < "+val
elsif val.include? ","
return attribute+".contains("+val+"\)"
else
return attribute+" == "+ val
end
end
json = JSON.parse($payload)
json.each do |hash1|
hash1.keys.each do |key|
hash1[key].each do |inner_hash,value|
#inner_hash = inner_hash
#values = value
str = concateRule #inner_hash,$values
end
end
end
Compile is working fine, but in runtime, I am getting this following error. Any suggestions
Root Exception stack trace:
org.jruby.exceptions.RaiseException: (NameError) undefined local
variable or method `null' for main:Object
at RUBY.concateRule(<script>:15)
at RUBY.block in (root)(<script>:43)
at org.jruby.RubyHash.each(org/jruby/RubyHash.java:1350)
at RUBY.block in (root)(<script>:40)
at org.jruby.RubyArray.each(org/jruby/RubyArray.java:1735)
at RUBY.block in (root)(<script>:39)
at org.jruby.RubyArray.each(org/jruby/RubyArray.java:1735)
at RUBY.<main>(<script>:38)
You need to use nil instead of null.
So, just replace it.
Following the conversation in the comments above, here is how I would write the method:
def concat_rule(attribute, val)
val = val.to_s
if val == '' || ['taxonomy_code', 'parent_taxonomy_code', 'report_name'].include?(attribute)
return ''
end
if val.start_with?('<>')
"#{attribute} != #{val[3..-1].strip}"
elsif val.start_with?('>')
"#{attribute} > #{val}"
elsif val.start_with?('<')
"#{attribute} < #{val}"
elsif val.include?(',')
"#{attribute}.contains(#{val})"
else
"#{attribute} == #{val}"
end
end
A few notes:
Using snake_case method names and 2 space tabs, is a very strongly adhered to style guide in the ruby community.
Similarly, you can make use of ruby's implicit return, to shorten the code: The final value at the end of a method is returned automatically.
Adding val = val.to_s to the top of this method simplifies the rest of the code; eliminating the need to repeatedly convert to a string or perform nil checks.
You can use ruby's string interpolation ("#{code-to-evaluate}") syntax as a more elegant way to define strings than repeated use of + for concatenation.

Caching, when nil or false are acceptable results [duplicate]

This question already has answers here:
How can I memoize a method that may return true, false, or nil in Ruby?
(2 answers)
Closed 8 years ago.
In some ruby classes, it is useful to cache the results of an expensive operation using the ||= operator, as in the following snippet:
class CacheableCalculations
def foobar
#foobar ||= lambda { some_expensive_calculation }.call
end
end
The issue arrises when the returned value is either nil or false, as this test shows:
class Example
attr_accessor :counter
def initialize(value)
#counter = 0
#value = value
end
def fancy_calculation
#foo ||= lambda { #counter += 1; #value }.call
end
end
first = Example.new(true)
5.times { first.fancy_calculation }
puts first.counter # <== 1, expected
second = Example.new(false)
5.times { second.fancy_calculation }
puts second.counter # <== 5, not caching
third = Example.new(nil)
5.times { third.fancy_calculation }
puts third.counter # <== 5, not caching
Is there any pros or cons with using the defined? operator instead, as in the following block of code?
class Example
attr_accessor :counter
def initialize(value)
#counter = 0
#value = value
end
def fancy_calculation
(defined? #foo) ? #foo : (#foo = lambda { #counter += 1; #value }.call)
end
end
This is still one 1 line, but is quite repetitive.
Is there a better way of easily returning cached results, regardless of what the value is?
The problem with the way it is written is that the ternary operator ?: has higher precedence than assignment = so it is parsed as
def fancy_calculation
((defined? #foo) ? #foo : #foo) = lambda { #counter += 1; #value }.call # NO
end
which you don't want, because #foo is always assigned to.
Instead, do this
def fancy_calculation
defined?(#foo) ? #foo : (#foo = lambda { #counter += 1; #value }.call)
end
This is probably about as succinct as can be without using a separate package/function specifically for memoization.
What you are trying to achieve is called memoization. There used to be a method doing what you need in Rails but at some point they extracted to a separate memoist gem. Check it out: https://github.com/matthewrudy/memoist
There is an alternative one as well: https://github.com/dkubb/memoizable

Show only a few characters from a string

I have this simple helper (in Rails app):
def shortener(text, text_length = nil)
if text_length.nil?
text_size = 60
else
text_size = text_length
end
#text_size = 60 if text_length.nil? => return the same error as above
if text.length.to_i > text_size.to_i # HERE IS THE ISSUE
return "#{text[0..(text_size-5)]}..."
else
return text
end
end
However, I am getting this error:
undefined method `length' for nil:NilClass
Why am I getting this error? Both parameters exist and are integers.
Because you are using Rails, I recommend you to use Rails built-in helper truncate
truncate("And they found that many people were sleeping better.", length: 25, omission: '... (continued)')
For more information, refer http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-truncate
If for some reason you want to roll your own method instead of using the built-in truncate:
def shortener(text = "", text_length = 60)
"#{text[0...text_length]}..."
end
It means text is nil. Use caller to find out why.
You are getting that error because text is passed as nil.
In order for it to behave like a normal text renderer in rails, you might want to do this:
def shortener(text, text_length = 60)
text ||= ''
if text.length.to_i > text_length.to_i
return "#{text[0..(max_text_length-5)]}..."
else
return text
end
end

Rails Fixnum Error

I have a simple query that Rails seems to be interpreting as a fixnum, but I'm not sure why. My code looks like this:
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
#user_with_points.each_with_index do |user_with_point, index|
When I add puts #user_with_points, it shows:
#<Point:0x6360138>
#<Point:0x6322f38>
However, I'm receiving this error this error:
NoMethodError: undefined method 'each' for 75:Fixnum
adding Entire Code
def self.update_overall_rank_and_points
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
rank = 0
points = 0
#user_with_points.each_with_index do |user_with_point, index|
#user = User.find(user_with_point.user_id)
if user_with_point.points != points
points = user_with_point.points
rank += 1
end
#user.rank = rank
#user.points = user_with_point.points
#user.save
end
end
Your query is returning a scalar value which the sum of points as an integer. The total of your query happens to be 75, hence the error. Therefore you can't do an each against it since it's not an enumeration.
Try:
#user_with_points = Point.sum(:points, :group => :user_id, :order => 'sum(points)')
#user_with_points.each do |user_id, points|
#...
user = User.find(user_id)
if user.points != points
puts "not equal!"
end
end

Rails: nil error where I am sure the object is not nil

This method increments the ActiveRecord attr_accessible attribute current_step:
def next_step
logger.debug "Now at step: " + current_step.inspect
if (current_step == nil)
current_step = 0
end
current_step = current_step + 1
end
At execution of the method, the log shows Now at step: 0 but the +1 line fails:
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.+):
app/models/assessment.rb:16:in `next_step'
Is it a miracle? Is current_step nil or not?
EDIT: Thanks fl00r and whitequark for the great answers! Here is what the code was meant to be:
def next_step
current_step ||= 0
self.current_step += 1
save
end
Conditionals and loops do not have their own scope in Ruby; and here you have a variable and a method of self which possess the same name. In the condition of if, the current_step method is used, but in its body a local variable is defined, and all future references to the current_step will refer to the local variable. The pitfall you've encountered is, even if the if body is not executed, the local variable is still defined, and default value of nil is assigned to it.
I'll make it more clear by adding _M to the identifier when the method is accessed, and _L for the local variable.
def next_step
logger.debug "Now at step: " + current_step_M.inspect
if (current_step_M == nil)
current_step_L = 0
### this part is implicit:
# else
# current_step_L = nil
end
current_step_L = current_step_L + 1
end
I'm guessing you was actually trying to do self.current_step = 0, which would invoke the setter.
def next_step
current_step ||= 0
logger.debug "Now at step: " + current_step.inspect
current_step += 1
end

Resources