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
Related
class Post
has_many :comments
after_update :update_comments
def update_comments(user)
comments.where(user: user).each do |comment|
# binding.pry
comment.do_something
end
end
end
class Comment
belongs_to :post
def do_something
'Ok, I will'
# binding.pry
end
end
This is my issue:
RSpec.describe Post do
describe '#update_comments' do
let(:post) { create :post }
let(:comment) { create :comment, post: post }
it 'triggers comment.do_something'
comment = post.comments.first
expect(comment).to receive(:do_something)
post.update(title: 'new title')
end
end
end
I get this error:
(#<Comment id: 1, ..., created_at: "2018-06-15 01:31:33", updated_at: "2018-05-16 02:51:39">).api_update(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
But if I use binding.pry in either (or both) def(s), I get the console, so I know it's actually being called.
I compared my comment variable in RSpec with self in the Comment class and they match. I tried using post.reload and comment.reload in RSpec to make sure the association was solid. Not sure what else I can do.
The fact that it triggers pry inside the method I'm saying should be received is so baffling to me.
What am I missing?
After a lot more digging, the issue is that my comment variable isn't actually exactly the same instance as the variable that RSpec sees in the loop. They differ by the Rails object ID that is assigned on the fly.
I needed to use a spy or stub the method to avoid this issue.
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
To prevent removal of related records, I am applying the before_destroy callbacks approach on each model
I defined several related-records validation methods in a module, so that they can be shared to different models' before_destroy callbacks:
class Teacher < ActiveRecord::Base
include RelatedModels
before_destroy :has_courses
...
end
class Level < ActiveRecord::Base
include RelatedModels
before_destroy :has_courses
...
end
module RelatedModels
def has_courses
if self.courses.any?
self.errors[:base] << "You cannot delete this while associated courses exists"
return false
end
end
def has_reports
if self.reports.any?
self.errors[:base] << "You cannot delete this while associated reports exists"
return false
end
end
def has_students
if self.students.any?
self.errors[:base] << "You cannot delete this while associated students exists"
return false
end
end
...
end
But it doesn't looks very DRY
Any idea how to do it in a single method?
Meta-programming it's not among my skills
Thanks in advance
You might want to try an observer class
These are depreciated in Rails 4 (you have to use the rails-observers gem), but seem to be still part of the Rails 3 core
Observer Classes
"Listen" to specified actions in your model, and provide extended functionality. As stated by their documentation, they are particularly good for de-cluttering your models, allowing you to combine a lot of functionality into one central set of functions
Here's what you might want to do (please bare in mind I have only used these in Rails 4):
class RelatedObserver < ActiveRecord::Observer
observe :teachers, :school
def before_destroy(record)
associations = %w(teachers schools reports)
associations.each do |association|
if record.send(association).any?
self.errors[:base] << "You cannot delete this while associated #{association.pluralize} exists"
return false
end
end
end
end
Here's a good resource for you
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).
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