How can I create dummy Task object that will be immediately done?
Why?
I often check if task is not None and task.done() just to check if another task can be spawn and feel it's a boilerplate that can be avoided:
def __init__(self):
self._do_something_task: Optional[asyncio.Task] = None
async def _do_something(self):
await asyncio.sleep(10)
# something
def trigger_do_something(self):
if self._do_something_task is not None and self._do_something_task.done():
asyncio.create_task(self._do_something())
Currently my workaround is:
def __init__(self):
self._do_something_task: asyncio.Task = asyncio.create_task(asyncio.sleep(0))
async def _do_something(self):
await asyncio.sleep(10)
# something
def trigger_do_something(self):
if self._do_something_task.done():
asyncio.create_task(self._do_something())
but I feel its not very readable at the first glance.
Python is a language with duck typing, so as long as some object can answer if it's done(), it should do the job:
class DummyTask:
def done(self):
return True
print(DummyTask().done()) # True
More smart way to do it is to use mock library.
Related
Can I use the hooks of a delayed job to stop it before running?
If so how?
class Jobs::SomeJob < Struct.new
def before(job)
if should_not_run_job
# Do I raise an exception?
# Is there an official way to stop a Job from running?
end
end
def perform
# Working...
end
def queue_name
return "SomeJob";
end
end
Do I raise an error?
Which hook is recommended to use?
Raising an exception wouldn't work because then the retry mechanism would kick in and would retry running the job a couple of times.
Instead, I would just add a guard clause that in the first line of the perform method that returns without doing anything when the conditions is not true anymore. It depends on how your condition looks like but something like this might work for you:
def perform
return if job_should_not_run_anymore?
# Working
end
private
def job_should_not_run_anymore?
# Condition
end
When your condition is based on the job itself that is only available in a hook method like before but not in the perform method then I would store the result of the condition in an instance variable and check that variable in the perform method like this:
def before(job)
#outdated = job.run_at > 15.minutes.ago
end
def perform
return if #outdated
# Working
end
I'm Just wondering, Is there a chaining concept in ruby.
I wanted to execute series of async tasks or methods one after the other. Is it possible?
Thanks,
Ravi
You might want to create a process class, something like:
class MyProcess
PROCESS_STEPS = %w(
step_one
step_two
step_three
)
class << self
def next_step
new.next_step
end
end # Class Methods
#======================================================================
# Instance Methods
#======================================================================
def next_step
PROCESS_STEPS.each do |process_step|
send(process_step) if send("do_#{process_step}?")
end
end
def step_one
# execute step one task
end
def do_step_one?
# some logic
end
def step_two
# execute step two task
end
def do_step_two?
# some logic
end
def step_three
# execute step three task
end
def do_step_three?
# some logic
end
end
You would probably put that in:
app
|- processes
| |- my_process.rb
Then, at the end of each task, do something like:
MyProcess.next_step
Javascript, where Promises were first introduced, is also synchronous, promises being an abstraction over callbacks in the strictest sense
There are concurrency libraries for Ruby, some of which capture the spirit of Promises to a certain extent, a google search for promise.rb yields some promising results:
https://github.com/lgierth/promise.rb
https://github.com/ruby-concurrency/concurrent-ruby
Perhaps these are not idiomatic ruby, but they do offer some useful paradigms
As far as i can tell, promise.rb is the most commonly used gem for an async mechanism adhering to the js Promise/A+ standard.
This article does a decent job of introducing it: https://medium.com/#gauravbasti2006/lets-keep-our-promise-in-ruby-e45925182fdc
concurrent-ruby is most widely used to implement concurrency related features like promises, similar to other widely used languages. The documentation is pretty straightforward as well:
https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md
For chaining asynchronous tasks you can use the following:
https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md#chaining
I guess this question is common with Rails 4, but my situation is different.
I am using Sidekiq to delay the creation of jobs; think this is possible as with simple data, it works. By means of simple data:
def perform
Foo.create(bar: "staff")
end
Here's my data with issues:
supports_controller.rb:
def create
params = support_params // seems to be issues here?
DelayedJobs.perform_in(1.minutes, current_user.id, params)
...
end
private
def support_params
params.require(:support).permit(:foo1, :foo2, :foo3)
end
app/workers/delayed_jobs.rb:
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
support = u.supports.build(params)
Support.create(support) // create and save to db
end
end
Via web (localhost:3000/sidekiq/scheduled, I see the details. Great. But after a minute it goes to retries with the error. Any help on this one?
EDIT:
In the sidekiq web argument:
40, {"foo1"=>"a", "foo2"=>"b", "foo3"=>"c"}
Why is that the user_id (40) is outside?
The problem isn't with Sidekiq; it's an ActiveRecord problem with this line:
Support.create(support)
create only takes a hash, but you're giving it a Support.
This should work:
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
u.supports.create!(params) # `create!` will raise an error if the save fails; allowing you to catch invalid params
end
end
Protip: you can eliminate Sidekiq as a suspect by running the body of your perform method in a Rails console. You'll see that you get the same error even when Sidekiq isn't involved.
I suggest that you call save method on support object because when you are using build method it returns a new instance of support so you need only to save it.
class DelayedJobs
include Sidekiq::Worker
def perform(user_id, params)
u = User.find(user_id)
support = u.supports.build(params)
support.save // save to db
end
end
In your controller try to change params to:
def create
params = params[:support]
Sorry if this is too simple. I'm looking for a way to make my ruby code dry : I want to call a number of methods on the same instance variable #var = Model.new(param) :
#var.method1
#var.method2
#var.method3
...
Is it possible to use the send method to write one line of code ? Btw, is it possible to call a block on Model.new to produce some more concise code ?
I believe that DRY should be used to make your code more maintainable, and more readable. I don't think it should be used to shorten the number of characters you type, or show-off your code acrobatics.
Both #Arup's and #p11y's solutions are great, within a context, but as a general rule (before knowing anything about your class or methods), I believe that writing
#var.method1
#var.method2
#var.method3
is more readable and maintainable than writing either
%i[method1 method2 method3].each(&#var.method(:send))
(you need to be fluent in advanced ruby to understand this)
or
#var.method1
.method2
.method3
(again the vanishing act is more confusing to the future reader than helpful)
Always think about who will read your code in 6 months, and what will be the clearest way for him to understand what's happening.
If you build method1, method2, etc. such that they return the instance itself using self, you can build a chainable interface. For example:
class Foo
def method1
# do something
self
end
def method2
# do something
self
end
def method3
# do something
self
end
# more methods...
end
#var = Foo.new
#var.method1.method2.method3
# or if this gets too long
#var.method1
.method2
.method3
Do as below :
%i[method1 method2 method3].each { |m| #var.send(m) }
If you want to make it more short,use :
%i[method1 method2 method3].each(&#var.method(:send))
When I wrote my original answer, I missed the last sentence in your question:
Btw, is it possible to call a block on Model.new to produce some more concise code ?
And the answer to this question is YES. This pattern is a builder pattern, which is implemented in several gems in ruby (such as tire).
The pattern states that the initialize method receives a block, which is run in the context of the created object, using instance_eval. If the block receives a parameter, the instance object is passed to it instead of changing the block's scope:
class Model
def initialize(name, &block)
#name = name
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
end
def method1
# something
end
def method2
# something
end
def method3
# something
end
end
And its usage will be something either like this:
#var = Model.new('model') do
method1
method2
method3
end
or, alternatively:
#var = Model.new('model') do |m|
m.method1
m.method2
m.method3
end
Given the Thread class with it current method.
Now inside a test, I want to do this:
def test_alter_current_thread
Thread.current = a_stubbed_method
# do something that involve the work of Thread.current
Thread.current = default_thread_current
end
Basically, I want to alter the method of a class inside a test method and recover it after that.
I know it sound complex for another language, like Java & C# (in Java, only powerful mock framework can do it). But it's ruby and I hope such nasty stuff would be available
You might want to take a look at a Ruby mocking framework like Mocha, but in terms of using plain Ruby it can be done using alias_method (documentation here) e.g.
beforehand:
class Thread
class << self
alias_method :old_current, :current
end
end
then define your new method
class Thread
def self.current
# implementation here
end
end
then afterwards restore the old method:
class Thread
class << self
alias_method :current, :old_current
end
end
Update to illustrate doing this from within a test
If you want to do this from within a test you could define some helper methods as follows:
def replace_class_method(cls, meth, new_impl)
cls.class_eval("class << self; alias_method :old_#{meth}, :#{meth}; end")
cls.class_eval(new_impl)
end
def restore_class_method(cls, meth)
cls.class_eval("class << self; alias_method :#{meth}, :old_#{meth}; end")
end
replace_class_method is expecting a class constant, the name of a class method and the new method definition as a string. restore_class_method takes the class and the method name and then aliases the original method back in place.
Your test would then be along the lines of:
def test
new_impl = <<EOM
def self.current
"replaced!"
end
EOM
replace_class_method(Thread, 'current', s)
puts "Replaced method call: #{Thread.current}"
restore_class_method(Thread, 'current')
puts "Restored method call: #{Thread.current}"
end
You could also write a little wrapper method which would replace a method, yield to a block and then ensure that the original method was reinstated afterwards e.g.
def with_replaced_method(cls, meth, new_impl)
replace_class_method(cls, meth, new_impl)
begin
result = yield
ensure
restore_class_method(cls, meth)
end
return result
end
Inside your test method this could then be used as:
with_replaced_method(Thread, 'current', new_impl) do
# test code using the replaced method goes here
end
# after this point the original method definition is restored
As mentioned in the original answer, you can probably find a framework to do this for you but hopefully the above code is interesting and useful anyway.