On a simple mongoid data model with a user that has many comments, I want to award the user with a specific badge when he writes at least 1 comment. So I set up an observer like this :
class CommentBadgeObserver < Mongoid::Observer
observe :comment
def after_create(comment)
CommentBadge.check_conditions_for(comment.user)
end
end
class CommentBadge < Badge
def self.check_conditions_for(user)
if user.comments.size > 1
badge = CommentBadge.create(:title => "Comment badge")
user.award(badge)
end
end
end
The user.award method :
def award(badge)
self.badges << badge
self.save
end
The following test fails (but I guess it is normal because observers are executed in background ?)
it 'should award the user with comment badge' do
@comment = Factory(:comment, :user => @user)
@user.badges.count.should == 1
@user.badges[0].title.should == "Comment badge"
end
What could be the best way to validate this behavior ?
I have got a working stand-alone adaptation of your code (see below). I had to make three small changes to get it working the way you were expecting.
To get the Observer working at all you have to instantiate it. In my example I needed to add the lines:
Mongoid.observers = CommentBadgeObserver
Mongoid.instantiate_observers
In Rails you can achieve the same thing adding this to config/application.rb (according to the docs):
config.mongoid.observers = :comment_badge_observer
I think there is also a small logic error in CommentBadge.check_conditions_for
, the > 1
should be > 0
.
Finally I changed the User#award
method to save the badge rather than the user, because the 'foreign key' field that stores the relationship is on the badge side.
class Comment
include Mongoid::Document
field :name
belongs_to :user
end
class CommentBadgeObserver < Mongoid::Observer
observe :comment
def after_create(comment)
CommentBadge.check_conditions_for(comment.user)
end
end
class Badge
include Mongoid::Document
field :title
belongs_to :user
end
class CommentBadge < Badge
def self.check_conditions_for(user)
if user.comments.size > 0
badge = CommentBadge.create!(:title => "Comment badge")
user.award(badge)
end
end
end
class User
include Mongoid::Document
field :first_name
has_many :comments
has_many :badges
def award(badge)
self.badges << badge
badge.save!
end
end
Factory.define(:user) do |u|
u.first_name 'Bob'
end
Factory.define(:comment) do |c|
c.name 'Some comment...'
end
# Observers need to be instantiated
Mongoid.observers = CommentBadgeObserver
Mongoid.instantiate_observers
describe CommentBadgeObserver do
it 'should create badges' do
@user = Factory.build(:user)
@comment = Factory(:comment, :user => @user)
@user.badges.count.should == 1
@user.badges[0].title.should == "Comment badge"
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With