Carrierwave+Sidekiq dynamic version issue, recreate in background - ruby-on-rails-4

When I create a new device then new version are added to my image uploader.
Then I run a job to recreate files for each MyModel record.
Sidekiq start job but it does not create files.
I use ActiveJob for declaring jobs.
This code:
MakingNewVersionImagesJob.perform_later("some_recently_added_device_name")
works fine in console after Sidekiq restart.
I think Sidekiq does not see MyModel changes.
Am I right? Is any solution here?
NOTE: it works fine when I do it directly in controller, not in background
class BaseImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
# Config/versions ...
def self.add_version(name, width, height)
version name do
process resize_to_fit: [width, height]
end
end
end
class MyModelImageUploader < BaseImageUploader
# blabla
end
class MyModel < ActiveRecord::Base
#...
mount_uploader :image, MyModelImageUploader
end
class Device < ActiveRecord::Base
after_create :add_uploaders_versions
def add_uploaders_versions
version = self.name.downcase.to_sym
MyModelImageUploader.add_version(version, self.width, self.height)
end
end
class MakingNewVersionImagesJob < ActiveJob::Base
queue_as :regular
def perform(version)
MyModel.find_each do |record|
record.image.recreate_versions!(version.to_sym)
record.save!
end
end
end
class DevicesController < ApplicationController
#...
def create
#device = Device.new(device_params)
if #device.save
MakingNewVersionImagesJob.perform_later(#device.name.downcase)
#...
end
end
end

I found out that Sidekiq/Rails both load code to own memory area, that why Sidekiq knows nothing about class changes caused on Rails side.
In my solution I switched to DelayedJob. It loads an actual Rails context.
Also, I can duplicate definition of new version in MakingNewVersionImagesJob#perform and continue to use Sidekiq.

Related

How to extend ActiveJob to access delayed_job's failure/error hooks?

I'm using rails ActiveJob's delayed_job adaptor, but would like to still be able to access delayed_job's error and failure hooks to trigger notification mailers when a job errors or fails.
I think I've tracked down the piece of ActiveJob code I need to extend in order to incorporate delayed_job's error and failure methods.
How do I go about extending the JobWrapper class below to include delayed_job's error and failure methods, so I can then use custom error/failure code on a job-by-job basis?
require "delayed_job"
module ActiveJob
module QueueAdapters
class DelayedJobAdapter
def enqueue(job)
delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, priority: job.priority)
job.provider_job_id = delayed_job.id
delayed_job
end
def enqueue_at(job, timestamp)
delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, priority: job.priority, run_at: Time.at(timestamp))
job.provider_job_id = delayed_job.id
delayed_job
end
class JobWrapper
attr_accessor :job_data
def initialize(job_data)
#job_data = job_data
end
def perform
Base.execute(job_data)
end
end
end
end
end
I believe this is what you want
module ActiveJob
module QueueAdapters
class DelayedJobAdapter
class JobWrapper
def failure(job)
job_data = Base.deserialize(job.payload_object.job_data)
job_data.failure(job) if job_data.respond_to?(:failure)
end
end
end
end
end
I've posted this problem/patch to rails issue
https://github.com/rails/rails/issues/30205
ActiveJob implements ActiveSupport::Rescuable which means you can use rescue_from.
class MyJob < ActiveJob::Base
rescue_from Exception do |e|
# do something with e
end
# rest of your job
end

Where should I place bestseller? method - in model or somewhere else?

I've got some simple model:
class Product < ActiveRecord::Base
has_many :categories
end
Now I would like to check in some service, if product is a bestseller and do other action for it:
class ProductService
def remind
Product.all.each do |product|
puts product unless bestseller?
end
end
end
So now what is the best place to put the bestseller? method - inside model or in the service as private method?
In future it may be used in some other services or actions.
Do you think the model is right place to put this method there?
Example of bestsellers method (bestsellers are picked manualy by adding to category 'bestsellers':
def bestseller?(product)
product.categories.include?(BESTSELLER_CATEGORY_ID)
end
or
def bestseller?(product_id)
Category.find(BESTSELLER_CATEGORY_ID).products.include?(product_id)
end
I still haven't decided which one is better (both do the same)

How to implement DelayedJob custom job for Prawn PDF rendering?

Im trying to use DelayedJob to render Prawn PDFs. Following the custom job code in the docs, I've come up with this:
/lib/jobs/pdf_handling.rb
RenderPdf = Struct.new( :id, :view_context ) do
def perform
user = User(id)
pdf = UserFolder.new( id, view_context )
name = "user_folder_report.pdf"
send_data pdf.render, filename: name, type: "application/pdf"
end
end
PagesController.rb
def user_folder
respond_to do |format|
format.pdf do
Delayed::Job.enqueue RenderPdf.new(#user, view_context)
end
end
end
this results in the error:
uninitialized constant PagesController::RenderPdf
Adding required RenderPdf at the top of the PagesController doesn't help.
What am I missing? How can I implement this so PDF generation occurs via DelayedJob? Thanks.
updates
When /jobs is moved under /apps the error changes to:
can't dump anonymous module: #<Module:0x007fca7a3ae638>
/application.rb
config.autoload_paths += Dir["#{config.root}/lib/assets/"]
updates
I changed
class RenderFolder < Struct.new( :type, :rating_id, :dis, :view_context )
def perform
to
class RenderFolder < ActiveJob::Base
def perform(...)
Then, using ActiveJob, you can do
RenderFolder.perform_later(...)
This seems to be working...Im still implementing.
the lib folder is no longer loaded by default in rails. you can either add it to the autoload_path or (what i would do) just have it in some app/xxx folder. typically, i have app/support or something for arbitrary utility classes.

Mongoid records embedded in resource in active admin are not displayed

I have the following models
class User::ActiveAdmin::Partner < User::ActiveAdmin::Base
embeds_many :bonuses, class_name: 'User::Bonus'
end
and
class User::Bonus
include Mongoid::Document
embedded_in :partner, class_name: 'User::ActiveAdmin::Partner'
end
and then I register Bonuses in Active Admin
ActiveAdmin.register User::Bonus, as: 'Bonuses' do
config.filters = false
permit_params :number, :order_id
controller do
def scoped_collection
if current_admin_user.is_a? User::ActiveAdmin::Partner
current_admin_user.bonuses.page(params[:page]).per(10)
else
super
end
end
end
the collection is not empty (I have created a couple of bonuses), but in ActiveAdmin index page I see, that there are NO BONUSES. And nothing I can do to make it displayed properly. I have noticed, that User::Bonus table is empty, even if a partner does have any, but as I know, this is the way it works, and this is OK. So how can I make my table to be displayed? Thanks.
The problem in method ActiveAdmin::Helpers::Collection#collection_size. You are using old version of activeadmin-mongoid. Try update activeadmin-mongoid.
In rails4 branch, collection_size isn't correct. You should override this method in your app like here: https://github.com/elia/activeadmin-mongoid/blob/master/lib/active_admin/mongoid/helpers/collection.rb

Using CanCan with Polymorphic Associations

I am struggling to use CanCan with my polymorphic associations. It is breaking down when I try and load the polymorphic objects in my controller.
class Trip < ActiveRecord::Base
has_many :images, as: :viewable
end
class Image < ActiveRecord::Base
belongs_to :viewable, polymorphic: true
end
Now I have a controller that deals specifically with images assigned to a trip.
class TripImagesController < ApplicationController
load_and_authorize_resource :trip
load_and_authorize_resource :image, through: :trip
def index
end
end
However, when I hit this index action I get the following error:
Couldn't find Image with id= [WHERE "images"."viewable_id" = $1 AND "images"."viewable_type" = $2]
I can see the query being executed in the Rails logs:
SELECT "images".* FROM "images" WHERE "images"."viewable_id" = $1 AND "images"."viewable_type" = $2 AND "images"."id" = $3 LIMIT 1 [["viewable_id", 1], ["viewable_type", "Trip"], ["id", ""]]
So the select statement looks good, except that it's trying to find only a single image (even though I'm in an index action). It's specifying an image id (which obviously does not exist in the route params) and it's also limiting the result to a single row.
This is using Rails 4.0.2 and CanCan 1.6.10. What am I missing here?