custom actions for inherited_resources - ruby-on-rails-3

I have 3 custom actions for my controller and was hoping that each of these use the resource object that inherited_resources gives us. So basically, instead of:
def cancel
#job = resource.cancel!
end
def restart
#job = resource.restart!
end
def start
#job = resource.start!
end
I want to just skip that by:
def cancel
#job.cancel!
end
def restart
#job.restart!
end
def start
#job.start!
end
Problem with this is #job comes out nil. So I checked the documentation and found out about custom actions. So I added this to my controller:
custom_actions :resource => [:cancel, :start, :restart]
but #job still is nil. I also tried:
actions :all
to tell IR to apply resource to all actions and it still doesn't work. What am I doing wrong?

You have to wrap the call like this:
def cancel
cancel! do
#job.cancel!
end
end
This causes IR to run and then yield control to your block (with the resource already set).

Related

after_create is not triggered in Observer after an activity is created

There is my Observer code:
class ActivityObserver < ActiveRecord::Observer
observe PublicActivity::Activity
def after_create(activity)
if activity.trackable
Something.create(trackable: trackable)
end
end
end
My spec is
require 'spec_helper'
describe ActivityObserver do
describe '#after_create' do
it "should be triggered after an activity is created'" do
activity = create(:activity)
ActivityObserver.instance.should_receive(:after_create).with(activity)
end
end
end
But it did not pass
move and edit this line
ActivityObserver.instance.should_receive(:after_create)
above
activity = create(:activity)
you are calling create before telling Rspec to look for the after_create call, that's probably why its failing

Inherited_resources build resource as role

As an example:
def create
resource = build_resource
resource.assign_attributes(params[resource_instance_name], as: :admin)
create! do |format|
format.js {...}
end
end
The problem with above is that attributes are not being assigned with as: :admin, they are being assigned without any check and so this method is not having any effect. Is it the create! method? Attributes are being assigned to this resource elsewhere and I can't find out where it is. Appreciate any insight.
Found my answer here: https://github.com/josevalim/inherited_resources/pull/153. Had to override as_role and role_given? --
def as_role
{ as: current_user.highest_role }
end
def role_given?
true
end
This will then always apply roles to attributes for either the controller it is defined in, or all resources if you inherit from a master resources controller like I do.

Rails 3.1 route constraint fails

This question is comparable to this one, but I ask it again because the answer provided does not solve the issue, and as the person asking that question concludes: it might be a bug in Rails (but no follow up on that is given).
I have these routes:
resources :books, controller: :projects, type: "Book" do
resources "", as: :book_chapters, controller: :contributions, type: "BookChapter", except: :index, constraints: IdConstraint
end
This is the IdConstraint:
class IdConstraint
INVALID_IDS = %w[edit series new]
def self.matches?(request)
!INVALID_IDS.include? request.params[:id]
end
end
I use friedly_id, so the :id parameter is a string based on the title of a book or book chapter.
Now a request like /books/friendly-id-of-book/friendly-id-of-chapter routes to the show action on the book_chapters controller.
But I would also expect /books/friendly-id-of-book/edit to route to the edit action on the books controller, because the constraint on the book_chapters route exclude edit as id. This does not work, and it seems that the problem is in IdConstraint. If I replace the one line in the matches? method with false:
class IdConstraint
INVALID_IDS = %w[edit series new]
def self.matches?(request)
false
end
end
the .../edit route properly routes to the edit action on the books controller.
But when i only add a line with false after the original line:
class IdConstraint
INVALID_IDS = %w[edit series new]
def self.matches?(request)
!INVALID_IDS.include? request.params[:id]
false
end
end
the route fails, i.e. it routes to the book_chapters controller with id "edit", while I would really expect it to still return false, and thus route to the edit action of the books controller.
I can't figure out what's going wrong here. Any ideas?
It looks to me like what you're running up against is a scope issue. INVALID_IDS is defined outside self.matches?(), so it's not available inside self.matches?().
You could try:
class IdConstraint
def self.matches?(request)
INVALID_IDS = %w[edit series new]
!INVALID_IDS.include? request.params[:id]
end
end
Or, if you really need INVALID_IDS to be available elsewhere in IdConstraint, you can do:
# Note that you'll need to use IdConstraint.new as your constraint for
# this one, not IdConstraint
class IdConstraint
def initialize
#INVALID_IDS = %w[edit series new]
end
def matches?(request)
!#INVALID_IDS.include? request.params[:id]
end
end
There's a good example of the second method on the Rails Guide.
UPDATE
Yeah, you're right about the bug with request.params. For now, this seems to work relatively well:
class IdConstraint
INVALID_IDS = %w[edit series new]
def self.matches?(request)
end_of_path = request.path.split('/').last
!INVALID_IDS.include? end_of_path
end
end
This is caused by a bug in Rails 3.1 master (at 0e19c7c414bb). See the bug report on GitHub. Until it is fixed you can temporarily circumvent it by using request.path_parameters[:id] instead of request.params[:id].

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

Rails, creating a callback

I want to use an ActiveModel callback to be called after an object has been voted on, the issue is that the gem I'm using (voteable_mongo) to make the model votable doesnt provide like a vote model or callback in my app, so how can I create a callback for it?
set_callback(:vote, :before) do |object|
object.do_something
end
Obviously that vote action I made up, but the gem I'm using has this method, how would you properly extend this method to trigger a callback?
Taking the plugin example as source here's what you could do:
class Post
include Mongoid::Document
include Mongo::Voteable
extend ActiveModel::Callbacks
define_model_callbacks :vote
# set points for each vote
voteable self, :up => +1, :down => -1
def vote(options, value = nil)
_run_vote_callbacks do
super( options, value )
end
end
end
I did not run this code so I am not sure if this is going to work correctly or not, but in the worst case you could alias the vote method using alias_method_chain or just copy and paste the source to inside the _run_vote_callbacks block (really, really ugly, but it's a solution anyway).
EDIT
This could also be done using alias_method_chain, if the code above does not work:
class Post
include Mongoid::Document
include Mongo::Voteable
extend ActiveModel::Callbacks
define_model_callbacks :vote
# set points for each vote
voteable self, :up => +1, :down => -1
alias_method_chain :vote, :callback
def vote_with_callback(options, value = nil)
_run_vote_callbacks do
vote_without_callbacks( options, value )
end
end
end