Rails 3 after_destroy observer not being called - ruby-on-rails

Here is my model...
app/controllers/registrations_controller.rb
def destroy
#registration = Registration.find(params[:id])
#registration.cancelled = true
#registration.save
respond_to do |format|
format.html { redirect_to root_url, notice: 'Registration was successfully canceled.' }
format.json { head :no_content }
NsoMailer.after_cancellation_email(#registration).deliver
end
end
app/models/registration_observer.rb
class RegistrationObserver < ActiveRecord::Observer
#close registration after seats are filled
def after_create(registration)
if registration.orientation != nil
#orientation = registration.orientation
close_orientation if seats_equal_zero
end
end
#opens registration if a registration is removed
def after_destroy(registration)
if registration.orientation != nil
#orientation = registration.orientation
open_orientation if seats_equal_zero == false
end
end
...the after_create action is working fine, but after_destroy is not. This is just name based correct? Naming the action in the observer 'after_destroy' links it the the corresponding controller action 'destroy' no?
I also added puts statements in both the controller and observer actions. I am making it to the controller action OK, but not the observer.

It doesn't link it to controller action destroy. It links it to the model.
In order for after_destroy to execute, you should do
#registration.destroy
You can have an after_save callback and have the same effect
def after_save(registration)
return unless registration.cancelled
if registration.orientation != nil
#orientation = registration.orientation
open_orientation if seats_equal_zero == false
end
end
You can have a look at the documentation for more information

Related

How to change status on a method

I'm trying to change the status of Dispute object:
class Dispute < ActiveRecord::Base
STATUSES = %w(open finished).freeze
STATUSES.each do |method|
define_method "#{method}?" do
status == method
end
end
def self.statuses
STATUSES
end
end
in create and update methods:
def create
Dispute.new
if params[:status] == 'Open'
dispute.status = dispute.statuses[0]
end
if dispute.save
redirect_to dispute_path(#dispute)
flash[:success] = 'Hooray'
else
flash[:error] = 'Error'
redirect_to :back
end
end
I handled the attribute inside the method, but the status did not change. Is it better to change the status in the model with callback instead of the controller?
To change the status you can simply do
def create
Dispute.new
if params[:status] == 'Open'
dispute.status = dispute.statuses.open!
end
if dispute.save
redirect_to dispute_path(#dispute)
flash[:success] = 'Hooray'
else
flash[:error] = 'Error'
redirect_to :back
end
end
you can visit http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html for more help on enumns.

How to invoke a method in controller?

I had the following code in my create action.
tasks_comtroller.rb
def create
#task = current_user.tasks.build(task_params)
#task.complete = false
n=1
loop do
unless Task.find_by priority: n
#task.priority=n
break
end
n+=1
end
respond_to do |format|
if #task.save
format.html { redirect_to user_tasks_path(current_user) }
format.js
else
format.html { render action: 'new' }
format.js
end
end
end
Then I took out some part I made a method inside my task model.
class Task < ActiveRecord::Base
def priority_increment
n=1
loop do
unless Task.find_by priority: n
#task.priority=n
break
end
n+=1
end
end
end
How can I now correctly invoke this method from controller?
Thats really inefficient since each iteration has to do a database query - instead do Task.order(:priority).first and let the database do the work.
class Task < ActiveRecord::Base
def self.heighest_priority
# .try lets us handle the case where there
# are no Tasks gracefully
p = Task.order(:priority).first.try(:priority)
# return the highest + 1 or 1 if there are no tasks.
p ? p + 1 : 1
end
end
Calling methods in ruby in exactly the same no matter where you are calling the function:
obj.some_method # in Ruby the parens are optional
obj.some_method(someArg)
obj.some_method(someArg) do
# blocks are awesome.
end
Here we have a class method - we call it by Task.heighest_priority.
So lets refactor the create method to use our new method:
def create
#task = current_user.tasks.build(task_params) do |t|
t.priority = Task.heighest_priority
t.complete = false
end
if #task.save
format.html { redirect_to user_tasks_path(current_user) }
format.js
else
format.html { render action: 'new' }
format.js
end
end
If you want to put that part of code into the model, you have to call self instead of the instance variable #task like so:
class Task < ActiveRecord::Base
def priority_increment
n=1
loop do
unless Task.find_by priority: n
self.priority=n
break
end
n+=1
end
end
end
Afterwards you can call it on the task instance:
#task.priority_increment

Rails actionmailer sending emails for commentable model

I'm trying to send emails through my actionmailer to the commentable owners' email after another user writes them a comment but I keep getting an error. Can someone help me out with this? Thanks in advance.
comment_mailer.rb
def email_notification(member, comment)
#member = commentable.member
#sender = comment.member
mail to: commentable.member.email, subject: "#{comment.member.full_name} (#{comment.member.user_name}) has left you a comment"
end
comment.rb
belongs_to :member
belongs_to :commentable, polymorphic: true
attr_accessible :content
after_create :send_email
def send_email
CommentMailer.email_notification(member, comment).deliver
end
error
undefined local variable or method `comment' for #<Comment:0x51c2ad8>
app/models/comment.rb:18:in `send_email'
app/controllers/comments_controller.rb:20:in `block in create'
app/controllers/comments_controller.rb:19:in `create'
comments_controller
before_filter :authenticate_member!
before_filter :load_commentable
before_filter :find_member
def index
redirect_to root_path
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(params[:comment])
#comments = #commentable.comments.order('created_at desc').page(params[:page]).per_page(15)
#comment.member = current_member
respond_to do |format|
if #comment.save
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to :back }
format.json
format.js
end
end
end
def destroy
#comment = Comment.find(params[:id])
respond_to do |format|
if #comment.member == current_member || #commentable.member == current_member
#comment.destroy
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to :back, alert: 'You can\'t delete this comment.' }
format.json
format.js
end
end
end
private
def load_commentable
klass = [Status, Medium, Project, Event, Listing].detect { |c| params["#{c.name.underscore}_id"] }
#commentable = klass.find(params["#{klass.name.underscore}_id"])
end
def find_member
#member = Member.find_by_user_name(params[:user_name])
end
You error is in the send_email method. There is no local variable called comment. You are already inside an instance of comment, the instance you want to send an email about. So you want to use the keyword self instead.
Change this:
def send_email
CommentMailer.email_notification(member, comment).deliver
end
To this:
def send_email
CommentMailer.email_notification(member, self).deliver
end
self refers to the current instance of comment, which you want to use for your mailer.

simple query on scaffolding rails

So my simple question is, i want to call a mailer if particular column is updated in database with the form based on below controller. But i dont know how to take the column name ... I am doing like if #style.category after if #style.save but not working.
Controller
def new
#style = Style.new
#users = User.where('username <> ?', current_user.username)
respond_to do |format|
format.html # new.html.erb
format.json { render json: #style }
end
end
def create
#style = Style.new(params[:style])
#style.sender = current_user
respond_to do |format|
if #style.save,
### want to call mailer here , based on if column "category" is updated or not
if #style.receiver
format.html { redirect_to user_path(#style.receiver.username), notice: "successs!"}
else
format.html { redirect_to root_path, notice: 'Success!' }
end
add a after_save callback inside the model can help
class Style < ActiveRecord::Base
after_save :send_email
def send_email
if changed_attributes.has_key?('category')
call the mailer here
end
end
end

ActiveRecord::RecordNotSaved - Forcing nested redirect

Morning All,
After spending most of the night figuring out how to put a limit on my model creation I finally got somewhere. The nested statement is now presenting me with not saved which is great news.
However I cannot seem to get the redirect or flash[:base] to work. Here is the code below:
class SnippetsController < ApplicationController
before_filter :find_book
def create
if #snippet = #book.snippets.create!(params[:snippet])
redirect_to #book
else
flash[:base]
#render
end
end
def approve
##snippet = #book.snippet.find(params[:id])
if #snippet.update_attribute(:approved, true)
redirect_to users_path
else
render root_path
end
end
def edit
#snippet = #book.snippets.find(params[:id])
end
def update
#snippet = #book.snippets.find(params[:id])
respond_to do |format|
if #snippet.update_attributes(params[:snippet])
format.html { redirect_to #book, notice: 'Comment was successfully updated.' }
else
format.html { render action: "edit" }
end
end
end
private
def find_book
#book = Book.find(params[:book_id])
end
end
Models parent (book)
class Book < ActiveRecord::Base
has_many :snippets
attr_accessible :title, :book_id, :size
def snippets_limit_reached?
if size == 0
self.snippets.count >= 2
elsif size == 1
self.snippets.count >= 3
elsif size == 2
self.snippets.count >= 4
else
return false
end
end
end
Child (Snippet)
class Snippet < ActiveRecord::Base
before_create :check_limit
belongs_to :book
attr_accessible :content, :book_id
validates :book_id, presence: true
def check_limit
if book.snippets_limit_reached?
errors.add :base, 'Snippet limit reached.'
return false
end
return true
end
end
Let me know if you need anything else, just fyi when it's running I cannot get past the nested create!
if #snippet = #book.snippets.create!(params[:snippet])
Bang methods (create!, save!) throw errors when unsuccessful, instead of returning, what evaluates to false.
Removing the bang should fix this problem.

Resources