How to pass Arguments and use those in (resque-status) Resque::JobWithStatus? - ruby-on-rails

my resque worker class is:
require 'resque'
require 'resque/job_with_status'
class PatstatResqueWorker < Resque::JobWithStatus
#queue = :my_worker_q
def self.perform(query, label)
puts "query:"
puts options['query']
puts "label:"
puts options['label']
end
end
and my controller part, where I call this resque is...
class MyController < ApplicationController
def resque
job_id = PatstatResqueWorker.create(:query => #query, :label => "yes")
status = Resque::Plugins::Status::Hash.get(job_id)
end
end
and its not working :(
if i remove the parameter from resque function it says Wrong number of arguments (2 for 0) and if i add the parameter section back it says options not defined :(
Could you help?

The reason you're getting the "options not defined" error is that you haven't defined options in the method that uses it. Your self.perform method expects to receive two distinct arguments, query and label, but the code inside the method expects to have an options hash. You've got to choose one or the other.
Either do this:
def self.perform(query, label)
# use the parameters we've already defined
puts "query:"
puts query
puts "label:"
puts label
end
# call it like this
PatstatResqueWorker.create(#query, "yes")
Or else do this:
# change the method signature to match what you're doing
def self.perform(options)
puts "query:"
puts options['query']
puts "label:"
puts options['label']
end
# call it like this, with string keys
PatstatResqueWorker.create('query' => #query, 'label' => "yes")
Notice that with the hash version, I changed the call to use strings for the hash keys instead of symbols. You can use symbols if you want, but you'd have to change it in the body of the method as well (i.e. options[:query] instead of options['query']). You've just got to be consistent.

Related

Rails/Ruby incorrectly showing variable not defined

In debugging console, while app running (using binding.pry to interrupt it), I can see that my variable Rails.configuration.hardcoded_current_user_key is set:
pry(#<TasksController>)> Rails.configuration.hardcoded_current_user_key
=> "dev"
But it doesn't appear to be defined:
pry(#<TasksController>)> defined?(Rails.configuration.hardcoded_current_user_key)
=> nil
Yet it works fine to store and test its value:
pry(#<TasksController>)> tempVar = Rails.configuration.hardcoded_current_user_key
=> "dev"
pry(#<TasksController>)> defined?(tempVar)
=> "local-variable"
What is going on?
This is because Rails config implements respond_to? but not respond_to_missing?, and defined? only recognizes respond_to_missing?:
class X
def respond_to?(name, include_all = false)
name == :another_secret || super
end
private
def method_missing(name, *args, &block)
case name
when :super_secret
'Bingo!'
when :another_secret
'Nope.'
else
super
end
end
def respond_to_missing?(name, include_all = false)
name == :super_secret || super
end
end
x = X.new
puts x.super_secret # => Bingo!
p defined?(x.super_secret) # => "method"
puts x.another_secret # => Nope.
p defined?(x.another_secret) # => nil
It's recommended to implement respond_to_missing? along with method_missing, I too wonder why Rails did it that way.
You shouldn't be using defined? on anything but the "stub" of that, or in other words, merely this:
defined?(Rails)
Anything beyond that is highly unusual to see, and I'm not even sure it's valid.
defined? is not a method, but a construct that tests if the following thing is defined as a variable, constant or method, among other things. It won't evaluate your code, it will just test it as-is. This means method calls don't happen, and as such, can't be chained.
If you want to test that something is assigned, then you should use this:
Rails.configuration.hardcoded_current_user_key.nil?

Argument Error : Wrong number of arguments

I am writing the following in rails console.
> class Hello
> def method
> d = Jobs.find_by_sql("select id, count(*) as TOTAL from table group by id having count>100")
> d.each do |m|
> puts m.account_id
> end
> end
> end
=> :method
> Hello.method
ArgumentError: wrong number of arguments (0 for 1)
I can't figure out what's wrong in this code. How can I solve this error.
your method name "method" is an existing method of the Object class, which is ultimately inherited by all classes in ruby.
You define an instance method with this name, which would be fine and would override the inherited instance method if it existed already. However, when you come to call it, you're calling it as a class method (because you're calling it on Hello, which is the class), so you're calling the existing "method" method, which is complaining about not getting any parameters.
Change your method to be called "foo" and then try to do Hello.foo. You'll get an "undefined method or variable" error because there's no foo class method.
Then do
hello = Hello.new
hello.foo
and it will work.
EDIT:
If you want it to actually be a class method, then you can do it via either of these ways:
class Hello
def self.method
d = Jobs.find_by_sql("select id, count(*) as TOTAL from table group by id having count>100")
d.each do |m|
puts m.account_id
end
end
end
end
or
class Hello
#class methods go in here
class << self
def method
d = Jobs.find_by_sql("select id, count(*) as TOTAL from table group by id having count>100")
d.each do |m|
puts m.account_id
end
end
end
end
end
As an aside, it's a convention, and generally a good idea, to use meaningful variable names. For example, if you have a variable which is a collection of Job objects, call it "jobs", not "d". Then anyone reading your code can easily remember what is held in that variable.
Using this principle, i would rewrite your code thus:
def output_job_account_ids
jobs = Jobs.find_by_sql("select id, count(*) as TOTAL from table group by id having count>100")
jobs.each do |job|
puts job.account_id
end
end
end
See how it's immediately much more obvious what is happening? I renamed the method name too: it's generally a good idea to have a method name describe what the method does.

solr, sunspot, bad request, illegal character

I am introducing sunspot search into my project. I got a POC by just searching by the name field. When I introduced the description field and reindexed sold I get the following error.
** Invoke sunspot:reindex (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute sunspot:reindex
Skipping progress bar: for progress reporting, add gem 'progress_bar' to your Gemfile
rake aborted!
RSolr::Error::Http: RSolr::Error::Http - 400 Bad Request
Error: {'responseHeader'=>{'status'=>400,'QTime'=>18},'error'=>{'msg'=>'Illegal character ((CTRL-CHAR, code 11))
at [row,col {unknown-source}]: [42,1]','code'=>400}}
Request Data: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><add><doc><field name=\"id\">ItemsDesign 1322</field><field name=\"type\">ItemsDesign</field><field name=\"type\">ActiveRecord::Base</field><field name=\"class_name\">ItemsDesign</field><field name=\"name_text\">River City Clocks Musical Multi-Colored Quartz Cuckoo Clock</field><field name=\"description_text\">This colorful chalet style German quartz cuckoo clock accurately keeps time and plays 12 different melodies. Many colorful flowers are painted on the clock case and figures of a Saint Bernard and Alpine horn player are on each side of the clock dial. Two decorative pine cone weights are suspended beneath the clock case by two chains. The heart shaped pendulum continously swings back and forth.
On every
I assuming that the bad char is 
 that you can see at the bottom. that 
 is littered in a lot of the descriptions. I'm not even sure what char that is.
What can I do to get solr to ignore it or clean the data so that sold can handle it.
Thanks
Put the following in an initializer to automatically clean sunspot calls of any UTF8 control characters:
# config/initializers/sunspot.rb
module Sunspot
#
# DataExtractors present an internal API for the indexer to use to extract
# field values from models for indexing. They must implement the #value_for
# method, which takes an object and returns the value extracted from it.
#
module DataExtractor #:nodoc: all
#
# AttributeExtractors extract data by simply calling a method on the block.
#
class AttributeExtractor
def initialize(attribute_name)
#attribute_name = attribute_name
end
def value_for(object)
Filter.new( object.send(#attribute_name) ).value
end
end
#
# BlockExtractors extract data by evaluating a block in the context of the
# object instance, or if the block takes an argument, by passing the object
# as the argument to the block. Either way, the return value of the block is
# the value returned by the extractor.
#
class BlockExtractor
def initialize(&block)
#block = block
end
def value_for(object)
Filter.new( Util.instance_eval_or_call(object, &#block) ).value
end
end
#
# Constant data extractors simply return the same value for every object.
#
class Constant
def initialize(value)
#value = value
end
def value_for(object)
Filter.new(#value).value
end
end
#
# A Filter to allow easy value cleaning
#
class Filter
def initialize(value)
#value = value
end
def value
strip_control_characters #value
end
def strip_control_characters(value)
return value unless value.is_a? String
value.chars.inject("") do |str, char|
unless char.ascii_only? and (char.ord < 32 or char.ord == 127)
str << char
end
str
end
end
end
end
end
Source (Sunspot Github Issues): Sunspot Solr Reindexing failing due to illegal characters
I tried the solution #thekingoftruth proposed, however it did not solve the problem. Found an alternative version of the Filter class in the same github thread that he links to and that solved my problem.
The main difference was the i use nested models through HABTM relationships.
This is my search block in the model:
searchable do
text :name, :description, :excerpt
text :venue_name do
venue.name if venue.present?
end
text :artist_name do
artists.map { |a| a.name if a.present? } if artists.present?
end
end
Here is the initializer that worked for me:
(in: config/initializers/sunspot.rb)
module Sunspot
#
# DataExtractors present an internal API for the indexer to use to extract
# field values from models for indexing. They must implement the #value_for
# method, which takes an object and returns the value extracted from it.
#
module DataExtractor #:nodoc: all
#
# AttributeExtractors extract data by simply calling a method on the block.
#
class AttributeExtractor
def initialize(attribute_name)
#attribute_name = attribute_name
end
def value_for(object)
Filter.new( object.send(#attribute_name) ).value
end
end
#
# BlockExtractors extract data by evaluating a block in the context of the
# object instance, or if the block takes an argument, by passing the object
# as the argument to the block. Either way, the return value of the block is
# the value returned by the extractor.
#
class BlockExtractor
def initialize(&block)
#block = block
end
def value_for(object)
Filter.new( Util.instance_eval_or_call(object, &#block) ).value
end
end
#
# Constant data extractors simply return the same value for every object.
#
class Constant
def initialize(value)
#value = value
end
def value_for(object)
Filter.new(#value).value
end
end
#
# A Filter to allow easy value cleaning
#
class Filter
def initialize(value)
#value = value
end
def value
if #value.is_a? String
strip_control_characters_from_string #value
elsif #value.is_a? Array
#value.map { |v| strip_control_characters_from_string v }
elsif #value.is_a? Hash
#value.inject({}) do |hash, (k, v)|
hash.merge( strip_control_characters_from_string(k) => strip_control_characters_from_string(v) )
end
else
#value
end
end
def strip_control_characters_from_string(value)
return value unless value.is_a? String
value.chars.inject("") do |str, char|
unless char.ascii_only? && (char.ord < 32 || char.ord == 127)
str << char
end
str
end
end
end
end
end
You need to get rid of control characters from UTF8 while saving your content. Solr will not reindex this properly and throw this error.
http://en.wikipedia.org/wiki/UTF-8#Codepage_layout
You can use something like this:
name.gsub!(/\p{Cc}/, "")
edit:
If you want to override it globally I think it could be possible by overriding value_for_methods in AttributeExtractor and if needed BlockExtractor.
https://github.com/sunspot/sunspot/blob/master/sunspot/lib/sunspot/data_extractor.rb
I wasn't checking this.
If you manage to add some global patch, please let me know.
I had lately same issue.

is that a ruby on rails strange behaviour with overwrite params? Or do I just dont aderstand ruby, again?

pre annotation: I have a solution, I want to understand what happens here, and if this behaviour is intended
edit a try for a better readable shortcut:
if you have the following code in Rails Controller:
def get_page
prepare_anythig params
if is_it_monday?
params=monday_default_paramms
end
finish_any_other_thing params
end
this works only on monday
Following functioning little controller function, not very intersting, I know
class SvgTestController < SiteController
def get_the_page
require "base64"
#main_width="auto"
params[:ci]||=['default']
puts "? params:",params
generate_drawing(params, false)
render ...
end
end
the console shows me how expected:
? params:
{"ci"=>"not default", "controller"=>"svg_test", "action"=>"get_the_page"}
Then I made a small (ok, erroneous or not valid as I now know - or think) change, I extended my get_the_page with 'get params via base64 encode json'
class SvgTestController < SiteController
def get_the_page
require "base64"
#main_width="auto"
params[:ci]||=['default']
# add here
puts "? params:",params
json=params[:json]
puts "json?",json.inspect
if json
plain = Base64.decode64(json)
puts "we are in here:", plain
params=JSON.parse(plain).with_indifferent_access
puts "? params now:",params
end
# end
puts "? params:",params
generate_drawing(params, false)
render ...
end
end
Solution working fine and the output like this:
? params:
{"json"=>"eyJjaSI6eyIwMDAwMDAwMDAyMDQ4MDgiOnsic3J2IjoxfX19", "controller"=>"svg_test", "action"=>"get_the_page", "ci"=>["default"]}
json?
"eyJjaSI6eyIwMDAwMDAwMDAyMDQ4MDgiOnsic3J2IjoxfX19"
we are in here:
{"ci":{"000000000204808":{"srv":1}}}
? params now:
{"ci"=>{"000000000204808"=>{"srv"=>1}}}
? params:
{"ci"=>{"000000000204808"=>{"srv"=>1}}}
later I got, working not with JSON-logic
NoMethodError in SvgTestController#get_the_page
undefined method `[]' for nil:NilClass
and my console shows me:
? params:
{"ci"=>"10.203.192.83", "controller"=>"svg_test", "action"=>"get_the_page"}
json?
nil
? params:
_(nothing to read here)_
So ruby overwrites my params (ok its a method, my fault) even if not in if ... end?
Again I ask: Is this wanted? And if, how to prevent such errors without knowing all and all the time about whats behind words like params?
edit
My solution, but not the answer to my question
...
params_used=params
json=params[:json]
if json
plain = Base64.decode64(json)
params_used=JSON.parse(plain).with_indifferent_access
end
puts "? params:",params_used
generate_drawing(params_used, false)
I think the "error" is because you're actually creating a variable. Annotation of your code:
def get_the_page
require "base64"
#main_width="auto"
params[:ci]||=['default'] # params method
# you modified #params, a mutable hash
# add here
puts "? params:",params # params method
json=params[:json] # params method
# you accessed #params[:json]
puts "json?",json.inspect
if json
plain = Base64.decode64(json)
puts "we are in here:", plain
params=JSON.parse(plain).with_indifferent_access # params variable
puts "? params now:",params # params variable
end
# end
puts "? params:",params # params variable
generate_drawing(params, false) # params variable
render ...
end
What's happening, I'd wager, is that the Ruby interpreter picks up the fact that a variable named params continues to be used after if block, so proceeds to initialize it (to nil) immediately before your if block irrespective of whether the block is visited or not.

Is there a way to access method arguments in Ruby?

New to Ruby and ROR and loving it each day, so here is my question since I have not idea how to google it (and I have tried :) )
we have method
def foo(first_name, last_name, age, sex, is_plumber)
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{SOMETHING}"
end
So what I am looking for way to get all arguments passed to method, without listing each one. Since this is Ruby I assume there is a way :) if it was java I would just list them :)
Output would be:
Method has failed, here are all method arguments {"Mario", "Super", 40, true, true}
In Ruby 1.9.2 and later you can use the parameters method on a method to get the list of parameters for that method. This will return a list of pairs indicating the name of the parameter and whether it is required.
e.g.
If you do
def foo(x, y)
end
then
method(:foo).parameters # => [[:req, :x], [:req, :y]]
You can use the special variable __method__ to get the name of the current method. So within a method the names of its parameters can be obtained via
args = method(__method__).parameters.map { |arg| arg[1].to_s }
You could then display the name and value of each parameter with
logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
Note: since this answer was originally written, in current versions of Ruby eval can no longer be called with a symbol. To address this, an explicit to_s has been added when building the list of parameter names i.e. parameters.map { |arg| arg[1].to_s }
Since Ruby 2.1 you can use binding.local_variable_get to read value of any local variable, including method parameters (arguments). Thanks to that you can improve the accepted answer to avoid evil eval.
def foo(x, y)
method(__method__).parameters.map do |_, name|
binding.local_variable_get(name)
end
end
foo(1, 2) # => 1, 2
One way to handle this is:
def foo(*args)
first_name, last_name, age, sex, is_plumber = *args
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{args.inspect}"
end
This is an interesting question. Maybe using local_variables? But there must be a way other than using eval. I'm looking in Kernel doc
class Test
def method(first, last)
local_variables.each do |var|
puts eval var.to_s
end
end
end
Test.new().method("aaa", 1) # outputs "aaa", 1
If you need arguments as a Hash, and you don't want to pollute method's body with tricky extraction of parameters, use this:
def mymethod(firstarg, kw_arg1:, kw_arg2: :default)
args = MethodArguments.(binding) # All arguments are in `args` hash now
...
end
Just add this class to your project:
class MethodArguments
def self.call(ext_binding)
raise ArgumentError, "Binding expected, #{ext_binding.class.name} given" unless ext_binding.is_a?(Binding)
method_name = ext_binding.eval("__method__")
ext_binding.receiver.method(method_name).parameters.map do |_, name|
[name, ext_binding.local_variable_get(name)]
end.to_h
end
end
This may be helpful...
def foo(x, y)
args(binding)
end
def args(callers_binding)
callers_name = caller[0][/`.*'/][1..-2]
parameters = method(callers_name).parameters
parameters.map { |_, arg_name|
callers_binding.local_variable_get(arg_name)
}
end
You can define a constant such as:
ARGS_TO_HASH = "method(__method__).parameters.map { |arg| arg[1].to_s }.map { |arg| { arg.to_sym => eval(arg) } }.reduce Hash.new, :merge"
And use it in your code like:
args = eval(ARGS_TO_HASH)
another_method_that_takes_the_same_arguments(**args)
If the function is inside some class then you can do something like this:
class Car
def drive(speed)
end
end
car = Car.new
method = car.method(:drive)
p method.parameters #=> [[:req, :speed]]
If you would change the method signature, you can do something like this:
def foo(*args)
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{args}"
end
Or:
def foo(opts={})
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{opts.values}"
end
In this case, interpolated args or opts.values will be an array, but you can join if on comma. Cheers
It seems like what this question is trying to accomplish could be done with a gem I just released, https://github.com/ericbeland/exception_details. It will list local variables and vlaues (and instance variables) from rescued exceptions. Might be worth a look...
Before I go further, you're passing too many arguments into foo. It looks like all of those arguments are attributes on a Model, correct? You should really be passing the object itself. End of speech.
You could use a "splat" argument. It shoves everything into an array. It would look like:
def foo(*bar)
...
log.error "Error with arguments #{bar.joins(', ')}"
end

Resources