Having more than one method in a sidekiq worker class - ruby

I have a sidekiq worker class. I currently implemented it this way. It works when I call PROCESS, and it will queue the method called PERFORM. But i would like to have more than one method that I can queue.
As a side note, is there a difference doing this and simply doing SocialSharer.delay.perform?
# I trigger by using SocialSharer.process("xxx")
class SocialSharer
include Sidekiq::Worker
def perform(user_id)
# does things
end
def perform_other_things
#i do not know how to trigger this
end
class << self
def process(user_id)
Sidekiq::Client.enqueue(SocialSharer,user_id)
end
end
end

SocialSharer.delay.perform would delay a class method called perform. Your perform method is an instance method.
Workers are designed to be one class per job. The job is started via the perform method. You can use delay to kick off any number of different class methods on a class, like so:
class Foo
def self.a(count)
end
def self.b(name)
end
end
Foo.delay.a(10)
Foo.delay.b('bob')

Well, if you really want to have all "performable" methods in one class, I'd suggest you to rename perform method to something different (say, perform_something), and create a new perform method which would dispatch the control flow:
class SocialSharer
include Sidekiq::Worker
# the 3 lines below may be replaced with `alias_method :perform, :public_send`
def perform(method, *args)
self.public_send(method, *args)
end
def perform_something(user_id)
# does things
end
def perform_other_things
# does other things
end
def self.process(user_id)
Sidekiq::Client.enqueue(SocialSharer, :perform_something, user_id)
Sidekiq::Client.enqueue(SocialSharer, :perform_other_things)
end
end

Related

Testing nested, asynchronous Sidekiq jobs with rspec

Lets say I have the following base Ruby class:
class BaseWorker
include Sidekiq::Worker
end
and the following two queues and one helper class:
class Finder
class << self
def find(id)
AwesomeDBClass.find(id)
end
end
end
class Queue1 extend BaseWorker
def perform(variable)
if variable.some_attribute
...
end
end
end
class Queue2 extend BaseWorker
def perform id
local_variable = Finder.find(id)
Queue1.perform_async(local_variable)
end
end
And the following test harness:
let(:local_variable) do
double(:local_var, some_attribute: true)
end
let(:local_variable2) do
OpenStruct(some_attribute: true)
end
before do
allow(Finder).to_receive(:find).and_return(:local_variable)
... code that triggers queue2 ...
queue2.drain
queue1.drain
end
This fails if local_variable (the double) is returned with:
undefined method `some_attribute' for {"__expired"=>false, "name"=>"local_variable"}:Hash
And if local_variable2 (the openstruct) is returned with:
undefined method `some_attribute' for {"table"=>{"some_attribute"=>true}}:Hash
interestingly, if inspecting either in queue2 (with a puts say) their values are correct.
Also, when reading the code for the two libraries:
https://github.com/ruby/ruby/blob/trunk/lib/ostruct.rb#L93
https://github.com/rspec/rspec-mocks/blob/31baac3ace4ec0aff97d9e4e1928b06d18c685f8/lib/rspec/mocks/test_double.rb#L10
It seems that the state suggest that the constructor has run, but some other thing (method_missing binding, eg other method definitions on the class and/or assign_stubs running) has not happened. Any ideas as to how or why this is happening would be appreciated.
TL;DR Use simple hashes instead of OpenStruct instances as params to perform.
Sidekiq serializes everything to Redis, please refer to Best Practices. OpenStruct is not a good candidate as a parameter to be passed to perform.
Minor sidenote: since Sidekiq overrides included callback, it would be more idiomatic to:
class BaseWorker
def self.extended base
base.send :include, Sidekiq::Worker
end
end

can common overriding code be shared

I am new to ruby and I'm hoping someone can help me. I am writing tests using test::unit and within my tests I needed to run some code before an assert was called so I overrided the assert methods like so:
class TestSomething < Test::Unit::TestCase
def assert_equal(expected, actual, message = nil)
mycode ..
super(expected, actual, message)
end
def assert(object, message)
my code ...
super(object, message)
end
def assert_not_nil(object, message = "")
my code ...
super(object, message)
end
def setup
end
def test_case1
end
def test_case1
end
def teardown
end
end
The above structure works fine and the asserts call my code. The thing is I have 100s of test classes. The assert overriding will be the same for all of them. Do I have to copy the assert overrides to the top of every class or is there a way for all of them to get the assert overrides in one go?
One more question. Is there a way of catching an error if it occurs anywhere within the entire class?
A
I am not sure why you do not want to use before filter, but the question as it was stated has an answer: since ruby classes are open, one might do the following.
class Test::Unit::TestCase
# store the original
alias_method :assert_equal_original, :assert_equal
# override
def assert_equal *args
# mycode ..
assert_equal_original *args
end
# the same for other methods
end
Once it is done, any derived class will call my_code before calling the original method.
To catch an error within the scope of the class is impossible, AFAIK.

Lazy object in ruby

How can I create an opbjet that's totally lazy by itself? I have a block, and I want to pass around (as a dependency) the "current value" (at call time) of the block instead of the value at dependency injection time.
I can't actually pass around a lambda because all the services expect an actual object, so they won't send :call to them, just access them.
This (oversimplified) example might clarify the situation:
class Timer
def initialize(current_time)
#current_time = current_time
end
def print_current_time
print #current_time
end
end
class Injector
def current_time
# a lazy object that when accessed actually calls the lambda below
# every single time.
end
def current_time_lazy
-> { Time.now }
end
def instantiate(class_name)
# search for the class, look at the constructor and
# create an instance with the dependencies injected by
# name
# but to be simple
if class_name == "Timer"
Timer.new(current_time)
end
end
end
timer = Injector.new.instantiate("Timer")
timer.print_current_time # => some time
sleep 2
timer.print_current_time # => some *different* time
The actual situation implies passing around the current_user but depending on the situation the current user might change after those values are injected.
I would really appreciate any suggestion (even if for now I will carefully sort the dependency injection code so this doesn't happen, but I think it's pretty fragile)
This should help :
class Timer
def initialize(current_time)
#current_time = current_time
end
def print_current_time
puts #current_time
end
end
class LazyMaker < BasicObject
def self.instantiate(class_name, lambada)
if class_name == 'Timer'
::Timer.new(new(class_name, lambada))
end
end
def initialize(class_name, lambada)
#lambada = lambada
end
def method_missing(method, *args)
#lambada.call.send(method, *args)
end
end
timer = LazyMaker.instantiate('Timer', -> { Time.now })
timer.print_current_time # some time
sleep 2
timer.print_current_time # some other time
I'm trying to use delegation to implement it, so that I can call the block first, get a new object and redirect the method call to it. Why this way ? Because basically, accessing an object to do something means to call a method on it. For instance, in print #current_time, it sends #current_time.to_s.
But since almost all objects will have a few methods inherited from standard base classes in Ruby like Object, LazyMaker also has methods like to_s. So I thought of making just the LazyMaker inherit from BasicObject, which is a blank class. So almost all of the methods get delegated.
But yeah, there might be another way to do this.

access an instance method of a class from another module's instance method

I have a class which declares a number of instance methods
class User < Group
def get_name
end
def show_profile
end
def get_task(task_id)
#some logic
end
end
And I want to call some of these methods from within a module. I can do it with Include statement.
include Users brings in all of the methods from User class. In this case I only want get_task and explicitly do not want get_name, show_profile etc..
I have a Tasks module which also has set of methods and one of those methods calls get_task method of User class.
module Tasks
class Project
def design
tid = 12
design_task = get_task(tid)
end
end
end
How this can be achieved without including other unnecessary methods of that class. Please help.
If the get_task method isn't depending on the user, you can use a class method like below. If it is depending on the user, you will have to get the server before you can run the method.
class User < Group
def get_name
end
def show_profile
end
def self.get_task(task_id)
#some logic
end
end
class Project
def design
tid = 12
design_task = User.get_task(tid)
end
end

Event declarations

I got the following code from #avdi's Ruby Tapas episode for today:
module Eventful
def self.included(other)
other.extend(Macros)
end
def add_listener(listener)
(#listeners ||= []) << listener
end
def notify_listeners(event, *args)
(#listeners || []).each do |listener|
listener.public_send("on_#{event}", *args)
end
end
module Macros
def event(name)
module_eval(%Q{
def #{name}(*args)
notify_listeners(:#{name}, *args)
end
})
end
end
end
class Dradis
include Eventful
event :new_contact
end
class ConsoleListener
def on_new_contact(direction, range)
puts "DRADIS contact! #{range} kilometers, bearing #{direction}"
end
end
dradis = Dradis.new
dradis.add_listener(ConsoleListener.new)
dradis.new_contact(120, 23000)
I understand the concept of events and listeners and the observer pattern, but don't get how/why this syntax is working, and haven't seen it in any manuals. The class Dradis has this:
event :new_contact
At first, I thought that event was a method and :new_contact was an argument so that I would call event on an instance of Dradis, something like:
dradis = Dradis.new
dradis.event
but instead, new_contact is called on an instance of Dradis like:
dradis = Dradis.new
dradis.add_listener(ConsoleListener.new)
dradis.new_contact(120, 23000)
and that triggers the event method in the Macro module.
Can anyone explain why it works like this? calling :new_contact on an instance dradis to trigger the event method?
I didn't watch the episode, but look, it's right there.
module Macros
def event(name)
module_eval(%Q{
def #{name}(*args)
notify_listeners(:#{name}, *args)
end
})
end
end
event is a method which defines another method (new_contact) which calls notify_listeners from Eventful.
and that triggers the event method in the Macro module
Incorrect. That method has finished its work a long time ago and it doesn't get invoked again. It produced a new method using module_eval / def and that new method (new_contact) is what's getting called.
It's important to understand that event method runs only once, when the Dradis class is parsed and loaded. It does not get run on every instantiation of Dradis.
Several separated features of ruby is used: In the line event :new_contact the "evnet" is class method (http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html#UE).
Usually class methods are defined by:
class A
def A.my_class_method
#code
end
end
A.my_class_method #executing class_method
or
class A
def <<self #usefull when you want delare several class methods
def A.my_class_method
#code
end
end
end
A.my_class_method #executing class_method
In the code the method is included by the module Macros.
The key thing is, that (class method) event is dynamicaly creating new (instance) method (in this case new_contact)
The name of the method is passed as argument to event). And this method providing calling of the listener.
Can anyone explain why it works like this? calling :new_contact on an instance dradis to trigger the event method?
This is by the dynammically created method new_contact which is calling notify_listeners(:#{name}, *args)

Resources