Rails, creating a callback - ruby-on-rails-3

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

Related

Fetching last incomplete record or new in ActiveRecord

I have a Rails 3.2.21 app where I'm adding some basic timeclock functionality. I need to build a scope called current_clock_event that will look for the last record for a user where clock_out: nil so in essence, the record that will be fetched is the last ClockEvent record for the user that does not have a value in clock_out. This is what I want to pass to my controller/view.
class ClockEvent < ActiveRecord::Base
attr_accessible :clock_in, :clock_out, :total_hours, :user_id
scope :current_clock_event, where(clock_out: NIL).last
end
As you can see I wrote a very simple scope to pull a record where the clock_out: NIL so in theory that should pull the last incomplete record. I think this is working ok but I need to figure out how to access this in the controller and have some sort of conditional to either pull the current_clock_event or instantiate a new clock event if the last record is completed (both clock_in and clock_out are populated)
So I'm stubbing out my controller but am hitting a wall as to how to do this.
class ClockEventsController < ApplicationController
def index
#clock_event = current_user.current_clock_event # need to figure out this part to fetch the record or if the record is complete instantiate a ClockEvent.new for the user.
respond_to do |format|
format.html # index.html.erb
format.js
end
end
end
I wrote code 2 years ago that did all of this but lost the repo by accident so I have nothing to reference and am sort of brain-fogging on how to pull this off.
Any help would be appreciated. If you need more examples or further explanation, please let me know.
You might want to try something like this:
class ClockEvent
belongs_to :user
# you might want to add an order here...
scope :last_clock_event, -> { where("clock_out NULL").last }
def completed?
clock_in.present? && clock_out.present?
end
end
class User
has_many :clock_events
def current_clock_event
ce = clock_events.last_clock_event
ce.completed? ? ClockEvent.new : ce
end
end
class ClockEventsController < ApplicationController
def index
#clock_event = current_user.current_clock_event
render :index
end
end
The completed? method defined on the ClockEvent instance allows you to tell if your instance is considered completed or not.
The current_clock_event method defined at the User level allows you to define the logic to return either the last clock event record or a new one if completed.
The index method is pretty straight forward.
I played around with some code and was able to get some refactoring help to make things cleaner. In the User model I refactored current_clock_event to just clock_event and seem to have been able to make the code a bit cleaner, although it's not tested, just stubbed out for now. Let me know what you think.
class ClockEvent
belongs_to :user
scope :incomplete, -> { where(clock_out: nil) }
scope :complete, -> { where.not(clock_out: nil) }
def completed?
clock_in.present? && clock_out.present?
end
end
class User
has_many :clock_events
def clock_event
#clock_event ||= clock_events.incomplete.last || clock_events.new
end
end
class ClockEventsController < ApplicationController
def index
#clock_event = current_user.clock_event
render :index
end
end

Rspec. Issuing a save on an existing, but modified, activerecord does not run before_update callback methods that are in included modules

I'm starting to attempt to incorporate more testing into my code, but I've hit a wall.
My model looks something like this
class Image < ActiveRecord:Base
before_create :do_something_general
before_update :do_something_on_update, :do_something_general
belongs_to :captureable, polymorphic: true
mount_uploader :image, SomeUploader
...
end
My rspec looks something like
describe SomeModel do
before :each do
#image = FactoryGirl.create(:image)
end
...
describe "moving image" do
context "change the parent of the image" do
it "moves" do
new_parent = FactoryGirl.create(:parent)
current_file_path = #image.image.file.path
#image.captureable = new_parent
#image.save!
#image.image.file.path.should_not == current_file_path
end
end
end
end
When I first create an Image, it will get stored in a file tree structure that depends on its parents. When a parent changes, the Image should be moved, and this is done with the before_update callback :do_something_on_update. My test should verify that when the Image has had its parent changed, it is located in a new location.
The problem is, when #image.save.should be_valid an except is returned because :do_something_general is run before :do_something_on_update (the order is important). It seems that the rspec thinks I'm creating a new object (using debugger I've checked that the object id doesn't change when modifying it), and thus runs before_create instead of before_update.
Edit: it seems that before_update is working, but only on callback methods that are in the class, but not in the module. In this case, :do_something_on_update is located in an included module.
End Edit
When I try this in the console in development mode, it works as expected.
Other things to note: I'm using Carrierwave for uploading (the image column is a carrierwave uploader) and when the :image factory is called, it also creates several parents and grandparent objects. Using Rspec 2.10, Rails 3.2.8, Ruby 1.9.3
Looking forward to your responses.
Thanks.
I would expect image.save.should be_valid to fail, because it's going to invoke image.save, which returns true or false, then it's going to invoke #valid? on that boolean result, which should likely fail.
You might consider writing your test like so:
describe SomeModel do
let(:image) { FactoryGirl.create(:image) }
context "when changing the parent of the image" do
let(:parent_change) { lambda {
image.captureable = FactoryGirl.create(:parent)
image.save!
} }
it "updates the image's path" do
expect parent_change.to change { image.image.file.path }
end
end
end
This ensures that you only have one assertion in the test (that the file path is changing), and that if the save fails, it will instead raise an exception.

using build method on association in rails 3.2 creating object in memory

I have 2 models like the following
Class Post
has_many :comments, :dependent => :destroy
end
Class Comment
validates_presence_of :post
validates_presence_of :comment
belongs_to :post
end
In Comments controller,
def create
comment = #post.comments.build(params[:comment])
if comment.save
// some code
else
// some code
end
end
When the comment is invalid as per the validation, the comment is not saved. But when the #post object is accessed in the view, it contains a comment object with nil id. This did not happen in Rails 2.3.11. We are upgraded to Rails 3.1 and then now to Rails 3.2. This comment object with nil id disappears when I do #post.reload. We are using REE.
I tried to interchange build and new methods. It had the same result as build. Similar behavior is found across our application. Is it the expected behavior or am I doing something wrong?
This seems like expected behaviour to me.
via http://guides.rubyonrails.org/association_basics.html#belongs_to-association-reference
4.1.1.3 build_association(attributes = {})
The build_association method returns a new object of the associated
type. This object will be instantiated from the passed attributes, and
the link through this object’s foreign key will be set, but the
associated object will not yet be saved.
When you call #post.comments.build(...), Rails:
Creates a new Comment object
sets comment.post_id to #post.id.
Inserts it into the comments array (in memory).
When the validation fails, it doesn't delete the comment and the comment persists in the in-memory comments array. When #post gets to your view, #post.comments still includes that badly validated comment.
As for how to deal with it, I'm not sure. Maybe you could do something like (in your controller)... (Feels pretty ugly though.)
def create
comment = #post.comments.build(params[:comment])
if comment.save
// some code
else
#bad_comment = #post.comments.pop
end
end
I had a similar problem while using rails 3.2
Firstly, you need to create two separate methods in your controller. They will be as follows:
The 'new' method that is used to build your comments using 'build_association'
def new
#post = Post.new
comment = #post.build_comments
end
The 'create' method to actually create your comments using 'create_association'
def create
#post = Post.new(params[:post])
comment = #post.create_comments(params[:post][:comment_attributes])
if comment.save
// some code
else
#bad_comment = #post.comments.pop
end
end
Note: I suggest passing 'comment' attribute as a nested attribute of 'post' through your form using 'fields_for'.
Please refer:
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Cells with Declarative_Authorization

using the gems cells and declarative_authorization (along with Devise) and I'm trying to figure out how to include the permitted_to? into the cell templates. So far I've added this to my cells Cell (the Devise one works for it's helpers):
class SidebarCell < Cell::Rails
include Devise::Controllers::Helpers
helper_method :current_user
include Authorization::AuthorizationHelper
helper_method :permitted_to?
def display(args)
#object = args[:object]
#notice = args[:notice]
#alert = args[:alert]
render
end
end
But it's bombing at the fact that declarative_auth helper module uses the following code:
def permitted_to? (privilege, object_or_sym = nil, options = {}, &block)
controller.permitted_to?(privilege, object_or_sym, options, &block)
end
and obviously this gives
undefined local variable or method `controller' for ...
UPDATE:
After some more thinking, I'm not sure this would ever work with Cells. Declarative_auth needs the controller to base it's rules on, but Cells has nothing to do with that controller. It would seem to me that the two are incompatible, unless I pass a reference to the controller into Cells? Starting to think that Cells isn't the way to go.
This will work if you add
helper_method :controller
that just delegates:
def controller
parent_controller
end
Sorry for that inconvenience, but the entire helper architecture in Rails sucks: http://nicksda.apotomo.de/2011/10/rails-misapprehensions-helpers-are-shit/

What am I doing wrong with this rspec helper test?

All I'm trying to do is spec how a one line helper method for a view should behave, but I'm not sure what kind of mock object, (if any) I should be creating if I'm working in Rails.
Here's the code for events_helper.rb:
module EventsHelper
def filter_check_button_path
params[:filter].blank? ? '/images/buttons/bt_search_for_events.gif' : '/images/buttons/bt_refine_this_search.gif'
end
end
And here's my spec code, in events_helper_spec.rb:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
# create an instance of the class that should include EventsHelper by default, as the first test has verified (I think)
#event = Event.new
# call method to check output
#event.filter_check_button_path.should be('/images/buttons/bt_search_for_events.gif')
end
end
When I've looked through the docs here - http://rspec.info/rails/writing/views.html, I'm mystified as to where the 'template' object comes from.
I've also tried looking here, which I thought would point me in the right direction, but alas, no dice. http://jakescruggs.blogspot.com/2007/03/mockingstubbing-partials-and-helper.html
What am I doing wrong here?
Thanks,
Chris
You are not doing anything in that spec, just setting a stub, so it will pass, but hasn't tested anything.
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {:filter => true}
helper.stub!(:params).and_return(params)
helper.filter_check_button_path.should eql('/images/buttons/bt_search_for_events.gif')
end
end
I'm running my test without spec_helper (Ruby 1.9)
require_relative '../../app/helpers/users_helper'
describe 'UsersHelper' do
include UsersHelper
...
end
Ah,
I asked this question on the rspec mailing list, and one kind soul (thanks Scott!) explained to me that there's a handy helper object for this, that you should use instead, like so:
Rails has its own helper function
params = {:filter => true}
helper.stub!(:params).and_return(params)
I've now updated the code like so:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
helper.stub!(:filter_check_button_path).and_return('/images/buttons/bt_search_for_events.gif')
end
end
And it's working. Huzzah!