Execution order of multiple after_commit callbacks (Rails) - ruby-on-rails-3

I recently found that multiple after_commits defined in the same model get called in reverse order. For example
after_commit method1, :on => :create
after_commit method2, :on => :create
method2 gets called before method1.
Is it always called in FILO order?

This behaviour is still present in Rails 5.2.2.1.
My solution:
after_commit :after_commit_callbacks, :on => :create
def after_commit_callbacks
method1
method2
end

Related

Multiple after_commit for the same method

How can I specify multiple callbacks to the method?
after_commit :assign_awards, on: [:create, :destroy]
private
def assign_awards
AwardsWorker.perform_async(self.id, self.class.name, self.user_id)
end
Error:
/activesupport-3.2.13/lib/active_support/callbacks.rb:404: syntax error, unexpected '[', expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
...(transaction_include_action?(:[:create, :destroy]))
For some reason, the callback isn't executed with this either:
after_commit :assign_awards, on: :create
after_commit :assign_awards, on: :destroy
It only works if I two different methods like:
after_commit :assign_awards_create, on: :create
after_commit :assign_awards_destroy, on: :destroy
private
def assign_awards_create
AwardsWorker.perform_async(self.id, self.class.name, self.user_id)
end
def assign_awards_destroy
AwardsWorker.perform_async(self.id, self.class.name, self.user_id)
end
Hmm, this answer shows the opposite:
https://stackoverflow.com/a/10356903/1023609.
Maybe it is a problem with the Rails version you are using.

Issue with helper_methods from views rails

I have a very frustrating issue.
I can't call any helper method from my views in rails.This is what I have:
ApplicationController.rb
class ApplicationController < ActionController::Base
protect_from_forgery
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
helper_method :all
end
/app/views/welcome/_navi_bar.haml:
%ul.nav.nav-pills.pull-right
%li.pull-right
- if current_user
%a.btn-small{"data-toggle" => "modal", :href => log_out_path, :role => "button"}
%i.icon-user.icon-white
%strong
Log Out
- else
%a.btn-small{"data-toggle" => "modal", :href => "#LoginBox", :role => "button"}
%i.icon-user.icon-white
%strong
Login
This is what I get as error:
undefined local variable or method `current_user' for #<#<Class:0x007ff0a0544668>:0x007ff0a05414b8>
I really don't get what the problem is. Please help !
You have written your code in application controller instead of application helper
That is the reason why your method is not getting called
if you want to check if current user is logged in or not
you may just use before filter in application controller and call the method
whenever you dont need to check the method add skip before filter in that place

rails 3 routes new :new action singular form

I don't know if I'm being nit-picky with this one but how do you create a :new action member?
When I have
resources :posts, :except => [:show] do
get :another_new, :on => :collection
end
this gives me a plural one (in routes another_new_posts GET /posts/another_new(.:format)).
But when I use member, I do get singular but it wants an :id (in routes another_new_post GET /posts/:id/another_new(.:format)).
How do I create another new which would say in routes as another_new_post with /posts/new?
Place this as a standalone piece outside of your resources block:
match 'posts/another_new' => "posts#new", as: :another_new_post

How to invoke a rails sweeper in this scenario?

As you can see from code below. I am caching the show action. I also have the following method in the show action View.create_for(#song).
I would like to make it so, when View.create_for(#song) is called, it clears out the respective cache.
How would I go about this? Do I have to manually invoke the rails sweeper in the View model? If so, how?
My controller:
class SongsController < ApplicationController
caches_action :show, :cache_path => (proc do
song_path(params[:id], :user_id => user_signed_in? ? current_user.id : nil)
end)
# I tried with the following line, but this is not what I want. I only want to call the sweeper when `View.create_for(#song)` is called:
# cache_sweeper :views_sweeper, :only => [:show]
def show
#song = Song.find(params[:id])
View.create_for(#song)
end
end
My Sweeper:
class SongsSweeper < ActionController::Caching::Sweeper
observe Song
def after_save(song)
expire_fragment(/songs\/#{song.id}\/.*?/)
end
end
I think you should be referring to the songs_sweeper, not the views_sweeper:
cache_sweeper :songs_sweeper, :only => [:show]
I'm not sure of your requirements, but you could also be more specific in your SongsSweeper by changing after_save to after_create:
class SongsSweeper < ActionController::Caching::Sweeper
observe Song
def after_create(song)
expire_fragment(/songs\/#{song.id}\/.*?/)
end
end

Rails 3 routing - :delete method on :collection

I want to create a route to allow deleting all shares. RESTful way would be to use verb DELETE. How can I create a routing that points to:
DELETE /shares
I tried in the routes:
resources :shares do
delete :on => :collection
end
But this yielded an error that rails can't turn nil into a symbol.
For now I have:
resources :shares do
delete 'delete_all', :on => :collection
end
EDIT: I had a typo in controller action name and this latter way works, but produces URL /shares/delete_all which is not very RESTful.
How can I drop the _delete_all_ part?
For Rails 3 you can do it this way and have nice resourceful GET/DELETE collection actions pointing to index and delete_all respectively:
resources :shares do
delete :index, on: :collection, action: :delete_all
end
If you're using Rails 4 you can make use of concerns to DRY this up and apply it to many resources:
concern :deleteallable do
delete :index, on: :collection, action: :delete_all
end
resources :shares, concerns: :deleteallable
resources :widgets, concerns: :deleteallable
What am I missing?
match 'shares', :to => 'shares#delete_all', :via => :delete
more info: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
<subjective opinion>
This is generally a bad idea and a code/design smell. The need to be deleting all records via a RESTful interface should really be behind a protected (authenticated) action and/or the action should be scoped to the user somehow.
The right way for your case, in routes:
resources :shares, except: :destroy
resource :share, only: :destroy
Please, pay attention, that I wrote word resource for destroy action
Then redefine "destroy" in shares_controller:
def destroy
respond_to do |format|
if Share.destroy_all
format.html { redirect_to root_path, notice: 'Share collection successfully deleted' }
else
format.html { redirect_to root_path, notice: 'Share collection cannot be deleted.' }
end
end
end
Here is the non REST way of doing it:
resources :shares do
collection do
delete :destroy_all
end
end
Then in your controller you will need something like this:
def destroy_all
Share.delete_all
end
Then this is what you want to do:
resources :shares do
collection do
delete :index
end
end
Then in your controller you will need something like this:
def index
if request.method == delete #delete might need to be a string here, I don't know
Share.delete_all
else
#shares = Share.all
end
end
There's a slightly simpler syntax for this that works in Rails 4.2 at least:
resources :shares do
delete on: :collection, action: :destroy_all
end
resources :shares do
collection do
delete '/', :to => :delete_all
end
end