rails - Create new job within a delayed job - ruby-on-rails-3

I am creating a delayed job in my controller using the delay method as below:
JobProcessor.delay(:run_at => Time.now).process_job(options)
Now inside the process_job method I am doing
chunks = options[:collection].each_splice(10).to_a
chunks.each do |chunk|
self.delay(:run_at => Time.now).chunk_job
end
This is giving me error stack level too deep when I send request to the URL
What might be the issue? Please help.

I was able to make it work for me by doing some changes.
I changed the outer method process_job to be an instance method
And kept the rest of the code as it is.
So now my code looks like
JobProcessor.new.process_job(options)
in the controller, and inside the JobProcessor class I have
class JobProcessor
def process_job(options)
chunks = options[:collection].each_splice(10).to_a
chunks.each do |chunk|
self.class.delay(:run_at => Time.now).chunk_job(options)
end
end
handle_asynchronously :process_job, :run_at => Proc.new { 2.seconds.from_now }
def self.chunk_job(options)
# perform chunk job here
end
end
This works for me.

Related

How to test a helper Grok view that makes a redirect

I have a content type that needs to be modified in some way after calling a helper Grok view that checks some condition, makes some changes, sets a message and redirects to the original object.
my helper view only has a render method and I want to write some tests for it but I have no idea how to handle this.
I would like to check for an error message when some condition is not met, and for an info message when everything goes fine.
my code looks like this:
class MyHelperView(grok.View):
grok.context(IMyType)
grok.layer(IMyLayer)
grok.name('helper-view')
grok.require('my.permission')
def render(self):
variable = self.request.form.get('variable', None)
if not variable:
msg = _(u'Required input is missing.')
api.portal.show_message(message=msg, request=self.request, type='error')
else:
do_something()
msg = _(u'Information processed.')
api.portal.show_message(message=msg, request=self.request)
self.request.response.redirect(self.context.absolute_url())
when I call the view obviously I ended with a None object, as the view returns nothing. I don't know where to look for messages... request? response? any hint?
I would avoid using transaction commits in test code. The test framework is specifically designed to roll back the transactions at the end of each test. Your setUp override goes against this.
To check status messages in a unit test you should be able to do something like:
from Products.statusmessages.interfaces import IStatusMessage
IStatusMessage(request).show()
This is an adapter that adapts the request.
I ended up with test with a layer based on FunctionalTesting:
....
from plone.app.testing import TEST_USER_NAME
from plone.app.testing import TEST_USER_PASSWORD
from plone.testing.z2 import Browser
....
import transaction
...
class HelperViewTestCase(unittest.TestCase):
layer = FUNCTIONAL_TESTING
def setUp(self):
self.app = self.layer['app']
self.portal = self.layer['portal']
self.request = self.layer['request']
directlyProvides(self.request, IMyLayer)
with api.env.adopt_roles(['Manager']):
self.foo = api.content.create(self.portal, 'MyType', 'foo')
transaction.commit()
def test_response(self):
browser = Browser(self.app)
browser.handleErrors = False
browser.addHeader(
'Authorization',
'Basic {0}:{1}'.format(TEST_USER_NAME, TEST_USER_PASSWORD)
)
browser.open(self.foo.absolute_url())
browser.getControl('Do Something').click()
self.assertIn(
'Required input is missing.', browser.contents)
two things you need to check that make me spent some time debugging:
you must use transaction.commit() to reflect object creation on the ZODB
you must add an authorization header before trying to open the page
everything else is working.

Rspec. Issuing a save on an existing, but modified, activerecord does not run before_update callback methods that are in included modules

I'm starting to attempt to incorporate more testing into my code, but I've hit a wall.
My model looks something like this
class Image < ActiveRecord:Base
before_create :do_something_general
before_update :do_something_on_update, :do_something_general
belongs_to :captureable, polymorphic: true
mount_uploader :image, SomeUploader
...
end
My rspec looks something like
describe SomeModel do
before :each do
#image = FactoryGirl.create(:image)
end
...
describe "moving image" do
context "change the parent of the image" do
it "moves" do
new_parent = FactoryGirl.create(:parent)
current_file_path = #image.image.file.path
#image.captureable = new_parent
#image.save!
#image.image.file.path.should_not == current_file_path
end
end
end
end
When I first create an Image, it will get stored in a file tree structure that depends on its parents. When a parent changes, the Image should be moved, and this is done with the before_update callback :do_something_on_update. My test should verify that when the Image has had its parent changed, it is located in a new location.
The problem is, when #image.save.should be_valid an except is returned because :do_something_general is run before :do_something_on_update (the order is important). It seems that the rspec thinks I'm creating a new object (using debugger I've checked that the object id doesn't change when modifying it), and thus runs before_create instead of before_update.
Edit: it seems that before_update is working, but only on callback methods that are in the class, but not in the module. In this case, :do_something_on_update is located in an included module.
End Edit
When I try this in the console in development mode, it works as expected.
Other things to note: I'm using Carrierwave for uploading (the image column is a carrierwave uploader) and when the :image factory is called, it also creates several parents and grandparent objects. Using Rspec 2.10, Rails 3.2.8, Ruby 1.9.3
Looking forward to your responses.
Thanks.
I would expect image.save.should be_valid to fail, because it's going to invoke image.save, which returns true or false, then it's going to invoke #valid? on that boolean result, which should likely fail.
You might consider writing your test like so:
describe SomeModel do
let(:image) { FactoryGirl.create(:image) }
context "when changing the parent of the image" do
let(:parent_change) { lambda {
image.captureable = FactoryGirl.create(:parent)
image.save!
} }
it "updates the image's path" do
expect parent_change.to change { image.image.file.path }
end
end
end
This ensures that you only have one assertion in the test (that the file path is changing), and that if the save fails, it will instead raise an exception.

Rspec: Stubbing out a where statement in a controller test

I'm writing the following test:
let!(:city_areas) { FactoryGirl.create_list(:city_area, 30) }
before {
#city_areas = mock_model(CityArea)
CityArea.should_receive(:where).and_return(city_areas)
}
it 'should assign the proper value to city areas variable' do
get :get_edit_and_update_vars
assigns(:city_areas).should eq(city_areas.order("name ASC"))
end
to test the following method:
def get_edit_and_update_vars
#city_areas = CityArea.where("city_id = '#{#bar.city_id}'").order("name ASC").all
end
However, it fails out, saying that there's no method 'city_id' for nil:NilClass, leading me to believe it's still attempting to use the instance variable #bar.
How do I properly stub out this where statement to prevent this?
Why are you doing #city_areas = mock_model(CityArea) and then you never use #city_areas again?
I would test it this way:
inside the model CityArea create a named scope for this: where("city_id = '#{#bar.city_id}'").order("name ASC")
then in your controller spec you do
describe 'GET get_edit_and_update_vars' do
before(:each) do
#areas = mock('areas')
end
it 'gets the areas' do
CityArea.should_receive(:your_scope).once.and_return(#areas)
get :get_edit_and_update_vars
end
it 'assign the proper value to city areas variable' do
CityArea.stub!(:your_scope => #areas)
get :get_edit_and_update_vars
assigns(:city_areas).should eq(ordered)
end
end
and you should also create a spec for that new scope on the model spec
just a tip, you shouldn't use should_receive(...) inside the before block, use stub! inside before and use should_receive when you want to test that method is called
also, you shouldn't need to use factorygirl when testing controllers, you should always mock the models, the model can be tested on the model spec

How I can setup controller/response object from scratch to run render_to_string?

How I can setup controller/response objects from scratch to ran render_to_string?
I developed an application which generate a PDF file.
I used erb based template to generate TeX file, then process it to convert to PDF.
It's working fine, but I now start thinking it is better to generate the report in background. Thus, I created a queuing system using delayed_job and it's working well.
Now, when combine two of them, I realized that renderer implemented as part of controller.
What I want to do is run render_to_string to generate the PDF. It looks like I need to setup controller and response object from scratch.
I found a question (How do I call controller/view methods from the console in Rails?) relate to this, but setting up a response object by calling post seems awkward.
How I can setup controller and response object to achieve this?
Here's one way to do it (generating the PDF via wicked_pdf):
Controller:
class FoosController < ApplicationController
def queue_pdf_generation
#foo = Foo.find(params[:id])
Delayed::Job.enqueue GeneratePdfJob.new(#foo.id)
end
end
Delayed Job:
class GeneratePdfJob < Struct.new(:foo_id)
def perform
# get the Foo object when the job is run
foo = Foo.find(foo_id)
# create an instance of ActionView, so we can use render method outside of a controller
av = ActionView::Base.new()
av.view_paths = ActionController::Base.view_paths
pdf_html = av.render :template => "foos/pdf.html.erb", :layout => nil, :locals => {:foo => foo}
# use wicked_pdf gem to create PDF from the foo HTML
foo_pdf = WickedPdf.new.pdf_from_string(pdf_html, :page_size => 'Letter')
# save PDF to disk
pdf_path = Rails.root.join('tmp', "#{foo.id}.pdf")
File.open(pdf_path, 'wb') do |file|
file << foo_pdf
end
end
A couple notes:
The "perform" method doesn't take a parameter; inheriting from a
struct lets us pass a parameter to the GeneratePdfJob object, and it
sets up a local variable called "foo_id" that we can use in the
"perform" method.
Your view needs to reference the local variable
"foo" rather than an instance variable "#foo".

Can my app be notified when a session is destroyed? If so, how?

My Devise/Warden-based app stores a model ID in the session[] variable. I want destroy the object when the session[] is destroyed.
Is there a callback or some mechanism to notify my app when the session is destroyed?
Is the mechanism dependable, or should I run a nightly cleanup script to vacuum up any orphaned objects?
To make it clear, here's a snippet of my controller code:
class WizardsController < ApplicationController
before_filter :find_or_create_wizard
...
private
def find_or_create_wizard
#wizard = Wizard.find_by_id(session[:wizard_id]) || Wizard.create.tap {|w| session[:wizard_id] = w }
end
end
To restate the question: how and when should I destroy the Wizard object?
Warden::Manager.before_logout do |user,auth,opts|
# callback
end
Use the Warden::Hooks https://github.com/hassox/warden/blob/master/lib/warden/hooks.rb to do things after sign out or authentication.
By session, do you mean when the user logs out?
Try monkey patching the sign_out method in your application_controller.rb
You can find the relevant Gem code in lib/devise/controllers/helpers.rb
def sign_out(resource_or_scope=nil)
Wizard.find_by_id(session[:wizard_id]) || Wizard.create.tap {|w| session[:wizard_id] = w }
super(resource_or_scope)
end
Session data is also cleared whenever a user signs in or signs up via a function called expire_session_data_after_sign_in!, could override that too:
def expire_session_data_after_sign_in!
Wizard.find_by_id(session[:wizard_id]) || Wizard.create.tap {|w| session[:wizard_id] = w }
super
end