The idea is:
Perform some time consuming action in background.
Have the results from that action be propagated back to the controller using a callback.
Store the result in an in memory session store.
Have the result in session be used and available from that point onward.
Controller receives the results in the callback:
# controller callback, saves result to session
# this method is executed as expected
# session id matches the one from other controller actions
def on_counter_value_calculated(context, new_value)
#counter = new_value
session[:counter] = #counter
end
However, stored session is lost in subsequent calls:
# although the same session is targeted (same id)
# the saved value is not the same
def index
#counter = session[:counter] || 0
end
I've created a small Rails project that demonstrates the issue:
https://github.com/elvanja/controller_callbak_store_in_session
Any input appreciated.
Checked Rails code, and if I understand correctly, session in fact comes from request:
# actionpack-3.2.11/lib/action_controller/metal.rb:132
delegate :session, :to => "#_request"
It seems session is valid only within request cycle context and although it can be accessed, the changes are not saved, as demonstrated by the project.
Hence, this will not work. As suggested # Ruby on Rails Google Group and Ruby Rogues, the best way to deal with this is to use Sidekiq, DelayedJob, Resque or similar frameworks.
EDIT: Access `session` within rails controller thread is actually the reason (background processing in the example is done in a separate thread).
Related
I am trying to use a different warden strategy to authenticate my action cable end points.
But the strategy is not getting called. I tried to place warden.authenticate!(:action_cable_auth) in a controller to test but none of the debug statements are getting printed on console.
Below are the relevant part of the code.
config/initializers/warden.rb
Warden::Strategies.add(:action_cable_auth) do
def valid?
#check if its a websocket request & for action cable?
#Rails.logger.error request.inspect
p 'checking if strategy is valid?'
true
end
def authenticate!
p 'unauthenticate the user'
fail!('user not active')
end
end
in my controller
warden.authenticate!(:action_cable_auth)
Assuming that you are setting your initializer in the proper place, please recall that if your session is already instantiated somewhere else (for example if you authenticate the user at the point your action is being called, then your strategy will never be called.
This is basically how warden works: if some valid? strategy returns a success! then no other will be called as soon as any authenticate! method in the list of strategies is successful.
Please also be sure that if you want your strategy up the list of strategies to check you may need to also shift it up on the list, such as:
manager.default_strategies(scope: :user).unshift(:action_cable_auth)
Where the manager is your Warden::Manager instance. The scope may also be optional (this is an example where the user scope is used alongside Devise), but you may check your instance .default_strategies to figure out where it is and where you want it.
I have just switched from carrierwave_backgrounder to carrierwave_direct. I have carrierwave_direct set up and functioning. That is, the main file is being uploaded and can be displayed in the view. However, my uploader versions are not being created.
Following is my job:
class ProcessReceiptJob < ApplicationJob
queue_as :process_receipt
def perform(expense_id, key)
expense = Expense.find expense_id
uploader = expense.receipt
expense.key = key
expense.remote_receipt_url = uploader.direct_fog_url(with_path: true)
expense.save!
# expense.recreate_versions!
end
after_perform do |job|
expense = Expense.find(job.arguments.first)
expense.update_column :receipt_processing, false
end
end
When exactly does carrierwave_direct process the versions---or, when is carrierwave instructed to process the versions? I'm assuming that loading the original image using expense.remote_receipt_url, and then calling save! triggers the uploader to process the versions. Is that correct?
In any case, my original image is being uploaded via a background job---however, the versions are not being created/uploaded.
Do I need to "recreate_versions" even thought they don't previously exist? Do I need to somehow explicitly process versions after pointing to the source file or should that be handled automagically?
I was not saving the model after assigning it :key BEFORE sending it the background worker. I was sending the key to the background worker as an argument and then saving the model in processing the job. This was the problem. It is mentioned in the docs the need to save the model after assigning it :key in the success action.
So, I had to update_attributes(key: params[:key]) and THEN call my background job (where incidentally the model is saved again).
I have set up a very simple rails 5 project to narrow down my problem:
https://github.com/benedikt-voigt/capybara_js_demo
In this project the data mutation done by the Capybara JS is not deleted, neither by Rails nor by the Database cleaner I added.
The following great blog argues, that no DatabaseCleaner is needed:
http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails
but this works only for fixtures, not for the mutation done by an out-of-thread Capybara test.
I added the Database cleaner, but this also needed work.
Does anybody has a sample setup?
From a quick look at your test I would say it's leaving data because the data is actually being added after DatabaseCleaner cleans. The click_on call occurs asynchronously, so when your assert_no_content call happens there's no guarantee the app has handled the request yet or the page has changed yet and since the current page doesn't have the text 'Name has already been taken' on it the assertion passes and the database gets cleaned. While that is happening the click gets processed by the app and the new data is created after the cleaning has occurred. You need to check/wait for content that will appear on the page after the click - something like
page.assert_text('New Foo Created')
You should only be asserting there is no content once you already know the page has changed, or you're expecting something to disappear from the current page.
I solved now the problem by setting the DB connection to one
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection }
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
as describe here:
https://mattbrictson.com/minitest-and-rails
I uploaded the working repo here:
https://github.com/benedikt-voigt/capybara_js_demo_working
I am seeing some odd behaviour when trying to override the create action on an Active Admin resource. My reason for overriding the action is that I want to alter the request params slightly first.
ActiveAdmin.register User do
controller do
def create
format_params
create!
end
def update
format_params
update!
end
def format_params
params[:user] = ...
end
end
end
What I am seeing is that a create action is being invoked twice - but only on the second time is it my overridden version. By that point, the new record is already created, albeit incorrectly, and the second invoking (which is the overridden version) looks like a duplicate resource.
If I empty the custom #create action and leave only a logging statement, I can see that a record is still being created prior to it being invoked, almost as if there is some sort of before_ callback, although I can't find any such thing in the code.
Even more odd - everything works fine on the overridden #update method - the params are altered and the original #update! method delegated to correctly.
I am using Active Admin 0.5.1 on Rails 3.2.13
Cheers
My thinking is to capture the session_id and store it in thread local storage, e.g.
Thread.current[:session_id] = session[:session_id]
But some rails logging occurs before my filter is called.
I thought I might capture the session_id by writing a middleware plug-in. But again, I don't seem to get it early enough for all logging.
What is the earliest that I can capture session_id?
Thanks!
Since Rails 3.2 support tagged logging using log_tags configuration array, log_tags array accept a Proc object, and the proc object is invoked with a request object, I could configure the log_tags following way:
config.log_tags = [ :uuid, :remote_ip,
lambda {|req| "#{req.cookie_jar["_session_id"]}" } ]
It's working with Rails 3.2.3, with ActiveRecord's session store is in use.
Okay, I finally figured this out.
I moved ActionDispatch::Cookies and ActionDispatch::Session::CookieStore way earlier in the rack stack. This appears to be safe, and is necessary because otherwise some logging happens before the session is initialized.
I added my own rack middleware component that sets the session_id in thread local storage.
I override the rails logger and prepend the session_id to each log message.
This is very helpful in being able to separate out and analyze all logs for particular user session.
I'd be interested to know how anyone else accomplishes this.
Based on #Felix's answer. i've done these in rails 4:
# config/application.rb
config.middleware.delete "ActionDispatch::Cookies"
config.middleware.delete "ActionDispatch::Session::CookieStore"
config.middleware.insert_before Rails::Rack::Logger, ActionDispatch::Cookies
config.middleware.insert_before Rails::Rack::Logger, ActionDispatch::Session::CookieStore
# config/environment/development.rb and production.rb
config.log_tags = [
lambda {|req| "#{req.subdomain}/#{req.session["user_id"]}" },
:uuid
]
config.log_formatter = Logger::Formatter.new
This produces logs like this:
I, [2015-11-05T15:45:42.617759 #22056] INFO -- : [verimor/2] [77e593dc-c852-4102-a999-5c90ea0c9d66] Started GET "/home/dashboard" for 192.168.1.37 at 2015-11-05 15:45:42 +0200
[verimor/2] is subdomain/user_id (this is a multitenant app).
[77e593dc-c852-4102-a999-5c90ea0c9d66] is a unique id for this request. Useful for keeping track of the lifecycle of requests.
HTH.
For Rails 3.2 with ActiveSupport::TaggedLogging, if you're using :cookie_store:
config.log_tags = [ :uuid, :remote_ip,
lambda { |r| "#{r.cookie_jar.signed["_session_id"]["session_id"]}" } ]
Note: Change the "_session_id" with your :key value at config/initializers/session_store.rb
Related: https://stackoverflow.com/a/22487360/117382