I am using apartment gem in one of my projects. And I have a requirement to log one particular type of activity in each of the tenants. For this I created a excluded model, and in the action where the activity happens I added the function to log it. Due to the data I am trying to log, lot of queries are run when i run this method. Thus i decided to move it to a background worker (Sidekiq). But when the worker runs its saying that its giving errors like.
Undefined method name for nil class
Now the code which gives this error is post.author.name.
This code works properly if we call it directly but breaks when we do it through sidekiq. Has this issue happened to anyone else before? any known solutions?
Worker code is
def perform(post_id, subdomain)
LogTransaction.create_post(post_id, subdomain)
end
The LogTransaction.create_post
def self.create_post post_id, subdomain
post = Post.find(post_id)
Apartment::Tenant.switch('public')
create(post_name: post.name, subdomain: subdomain, author_name: post.author.name)
end
Use this gem in your application, this gem will store which schema from which the job was initiated and it will run in that schema.
https://github.com/influitive/apartment-sidekiq
Related
My counter cache is locking the row under heavy load so I found wanelo/counter-cache gem which seems to be perfect for my problem but I can't set it up and it must be something really simple but I can't see it.
https://github.com/wanelo/counter-cache
I want it to use my already working delayed jobs and Redis.
In my config file
Counter::Cache.configure do |c|
c.default_worker_adapter = here??? DelayedJob ??
c.recalculation_delay = 5.hours
c.redis_pool = Redis.new
c.counting_data_store = Counter::Cache::Redis
end
If I don't put the line c.default_worker_adapter when executing it says
undefined method 'enqueue' for nil:NilClass
Any idea on what's going on? What should I put in the Worker Adapter? Nothing seems to work.
Thank you for your time
default_worker_adapter is the name of the class that will be handling your updates. An example is given on the github page of the gem. For example if you're using sidekiq, you would make sidekiq worker class and name it whatever you want. On the github page, this class is called CounterWorker and you can copy it exactly as its given, though you can use whatever delayed job framework you want. From then on, any counter_cache_on definitions on your models will use that class to make the counter updates.
I am using Rails 4 w/ the impressionist and resque gem.
I am using impressionist to log unique session hits on my article show page. Due to performance issues and no need to display hits to users (it is for admins only), I would like to move logging impressions off into the background.
Normally I would log an impression using impressionist(#article, unique: [:session_hash]) but to move it off into the bg via resque I am now doing something like this...
articles_controller:
def show
.
.
.
Resque.enqueue(ImpressionLogger, #article.id)
end
app/workers/impression_logger.rb:
class ImpressionLogger
#queue = :impression_queue
def self.perform(article_id)
article = Article.find(article_id)
impressionist(article, unique: [:session_hash])
end
end
When I set it up like this, when resque tries to process the job, it is returning undefined method "impressionist" for ImpressionLogger:Class. What do you guys think the best way to go about this is? I am not sure how to include impressionist methods inside of my resque worker.
The issue
Your problem stems from the fact that it looks like Impressionist works on the controller level due to including a module with the impressionist method in an engine initializer on any instances of ActionController:
https://github.com/charlotte-ruby/impressionist/blob/master/lib/impressionist/engine.rb#L11
You're trying to call the impressionist method from a regular class being invoked in a Resque job, so it's not going to have that method defined.
Solution
It's kind of gross, but if you really want to use impressionist, we can delve into this... Looking at the actual implementation of the impressionist method found here, we see the following:
def impressionist(obj,message=nil,opts={})
if should_count_impression?(opts)
if obj.respond_to?("impressionable?")
if unique_instance?(obj, opts[:unique])
obj.impressions.create(associative_create_statement({:message => message}))
end
else
# we could create an impression anyway. for classes, too. why not?
raise "#{obj.class.to_s} is not impressionable!"
end
end
end
Assuming that you'd be calling something like this manually (as you want to from a resque job) the key are these three lines:
if unique_instance?(obj, opts[:unique])
obj.impressions.create(associative_create_statement({:message => message}))
end
The if wrapper only seems to be important if you want to implement this functionality. Which it looks like you do. The call to associative_create_statement seems to be pulling parameters based off of the controller name as well as parameters passed from Rack such as the useragent string and ip address (here). So, you'll have to resolve these values prior to invoking the Resque job.
What I would suggest at this point is implementing a Resque class that takes in two parameters, an article_id and the impression parameters that you want. The resque class would then just directly create the impression on the impressionable object. Your Resque class would become:
class ImpressionLogger
#queue = :impression_queue
def self.perform(article_id, impression_params = {})
article = Article.find(article_id)
article.impressions.create(impression_params)
end
end
And your controller method would look something like this:
def show
.
.
.
Resque.enqueue(ImpressionLogger, #article.id, associative_create_statement({message: nil})) if unique_instance?(#article, [:session_hash])
end
Disclaimer
There's a fairly big disclaimer that comes with doing it this way though... the method associative_create_statement is marked protected and unique_instance? is marked private... so neither of these is part of the impressionist gem's public API, so this code might break between versions of the gem.
Is impressionist installed properly with bundler? If so Rails should be loading it into your environment. I would check whether you can access impressionist functionality elsewhere in your Rails code (i.e. without going through Resque) as the first step to debugging this.
How are you starting your resque workers? If you need your Rails environment loaded, try rake environment resque:work.
https://github.com/resque/resque/wiki/FAQ#how-do-i-ensure-my-rails-classesenvironment-is-loaded
I'm using the latest version of the Impressionist and Rails Admin gems, and wondering if anyone could shed some light on an annoying conflict I'm experiencing. The problem is roughly documented here - https://github.com/sferik/rails_admin/issues/1315, yet the vaguely described solution is not working for me. When I have the line is_impressionable in my Listing model, I get an error when starting my Rails server with rails s:
...rvm/gems/ruby-2.0.0-p247/gems/activerecord-4.0.2/lib/active_record/dynamic_matchers.rb:22:in `method_missing': undefined local variable or method `is_impressionable' for Listing(no database connection):Class (NameError)
If I first start the server, and then add the 'is_impressionable' line, everything works fine, so the problem only occurs during initialization. I don't fully understand the initialization process, so am not sure how to go about getting this to work.
I have tried moving all my rails_admin model configuration options to their respective models, rather than in the initializer, which had no effect. I also have the following line in my initializer:
config.included_models = [Listing,ListingImage,AllOtherModelsHere...]
I have tried adding single quotes around these model names, which results in the following errors, as described in the github issue here
[RailsAdmin] Could not load model Listing, assuming model is non existing. (undefined local variable or method `is_impressionable' for Listing(no database connection):Class)
Any ideas what else I can try to make these gems work together? I don't want to have to remove the is_impressionable line every time I want to restart the server or generate a migration...
Not sure if the same issue that I had but yet I will post what worked for me just in case someone struggles with this too:
Im on a ruby 2.1.5 project with rails 4.2.0 and among other gems I'm using rails admin.
I run into several weird problems trying to set this up. For instance if I added the is_impressionable call within one of my models for some reason the execution of that file stopped there and I started getting weird errors like any method declared below the is_impressionable failed with undefined error.
So what I end up doing was:
class MyModel < ActiveRecord::Base
include Impressionist::IsImpressionable
is_impressionable
end
So this solved my issue and now i can access #my_model_instance.impression_count as expected.
I changed every occurrence of Klass to 'Klass'.constantize in initializer.
I am having many problems running DJ. Primarily, I cannot get delayed_job running any methods that change the database. I am testing locally with a sqlite3 database, DJ 3.0.0, and I even added the delayed_job_active_record gem.
I have, for example, tried to run the following method in the background:
#user = User.find(1)
#user.delay.recorder_method
Where this method is:
def recorder_method
self.relevant_field +=1
update
end
This creates a delayed job, and the handler has the appropriate info for the user and the appropriate method name. The script runs the job, thinks it has succeeded and thus deletes the record from the delayed_job table. BUT the user is unchanged (the database is unchanged).
What on earth is going wrong? Note that, when I run the same code ("#user.delay.recorder_method") from rails console, it works.. and the difference is the handler created by the rails console call is:
--- !ruby/object:Delayed::PerformableMethod
object: !ruby/ActiveRecord:User
attributes:
... (attribute info and rest of file)
Whereas the one created in by the call in a controller action is:
--- !ruby/struct:Delayed::PerformableMethod
object: !ruby/ActiveRecord:User
attributes:
Not the difference in line 1 of both things (ruby/object vs ruby/struct.. the former works and the latter doesn't). Maybe this is something that might signal what is going wrong. Any ideas, anybody?
Your Rails app is running an old version of the DJ gem and your console is running a new one. About six months ago, Delayed::PerformableMethod was refactored from a Struct into a regular class: https://github.com/collectiveidea/delayed_job/commit/7b8a79a72c0ee5d8bac4bc0b183d1cce9cedff85 (So your Rails app is running a gem at least six months old and your console is running one newer than that).
This is an easy fix. First update the appropriate line in your Rails Gemfile. You'll see a line like this:
gem 'delayed_job_active_record'
If it has a version specification, make sure it's updated to the newest. Then from the command line (in the Rails root), run:
bundle update delayed_job_active_record
For anyone interested, removing the following lines from boot.rb solved this issue for me:
require 'yaml'
YAML::ENGINE.yamler = 'syck'
In my current database setup I have a user and a server table. I am able to do things like current_user.server from the rails console, but I am not able to do this from inside a resque worker. I am however, able to call current_user.name because of this code in my resque worker:
current_user = User.find(current_id)
I was able to do this by passing in the current_user's id to the worker. I thought I could use the same process for the user's server table; it's a has_one user->server relationship.
So I tried this:
current_server = Server.where(:user_id => current_user.id)
current_server[:cloudserver] = 123 #Just an example number
current_server.save
The first line worked fine, it returned the right server row. However, when I also include the second line I get an error:
TypeError can't convert Symbol into Integer
I know I created the cloudserver column as an integer type, and I made sure I loaded the rails environment in the resque workers. I can set the correct number by using the rails console:
current_user.server[:cloudserver] = 123
current_user.save
Since I can set it outside of the resque worker, I'm pretty sure it's a resque problem.
Oh, and I'm also running rails 3.1rc4 if that helps.
Thanks all!