Rails after_save error - ruby-on-rails-3

Is this the correct way of using after_save callback ?
class CouponsController < ApplicationController
after_save :remove_restrictions
private
def remove_restrictions
logger.debug("in after save")
end
end
This code throws the error as
undefined method `after_save' for CouponsController:Class
What is the correct way of using after_save ?

app/models/coupon.rb
class Coupon < ActiveRecord::Base
# after_save goes to your model
after_save :remove_restrictions
private
def remove_restrictions
logger.debug("in after save")
end
end
app/controllers/coupon_controller.rb
class CouponController < ApplicationController
# after_filters goes to your controller
after_filter :remove_restrictions
private
def remove_restrictions
logger.debug("in after filters")
end
end

Related

Null Object Pattern for associations in Rails

Despite looking at a few answers here regarding Null Objects in rails, I can't seem to get them to work.
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
def profile
self.profile || NullProfile #I have also tried
#profile || NullProfile #but it didn't work either
end
end
class NullProfile
def display #this method exists on the real Profile class
""
end
end
class UsersController < ApplicationController
def create
User.new(params)
end
end
My problem is that on User creation, I pass in the proper nested attributes (profile_attributes) for the Profile and I end up with a NullProfile on my new User.
I am guessing that this means that my custom profile method is getting called on create and returning a NullProfile. How do I do this NullObject properly so that this only happens on read and not on the initial creation of the objects.
I was going exactly through and I wanted a clean new object if it wasn't present(if you're doing this just so object.display doesn't err maybe object.try(:display) is better) this too and this is what I found:
1: alias/alias_method_chain
def profile_with_no_nill
profile_without_no_nill || NullProfile
end
alias_method_chain :profile, :no_nill
But since alias_method_chain is being deprecated, if you're staying on the edge you would have to do the pattern by yourself manually... The answer here seems to provide the better and more elegant solution
2(Simplified/practical version from the answer):
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
module ProfileNullObject
def profile
super || NullProfile
end
end
include ProfileNullObject
end
note: The order you do this matter(explained in the linked answer)
On what you tried:
When you did
def profile
#profile || NullProfile
end
It won't behave as expected because the Association is lazily loaded(unless you told it to :include it in the search), so #profile is nil, that's why you're always getting NullProfile
def profile
self.profile || NullProfile
end
It will fail because the method is calling itself, so it's sort like a recursive method, you get SystemStackError: stack level too deep
I've found a simpler option than including a private module in the accepted answer.
You can override the reader method and fetch the associated object using the association method from ActiveRecord.
class User < ApplicationRecord
has_one :profile
def profile
association(:profile).load_target || NullProfile
end
end # class User
Instead of using alias_method_chain, use this:
def profile
self[:profile] || NullProfile.new
end
According to the Rails docs, the association methods are loaded into a module, so it's safe to override them.
So, something like...
def profile
super || NullProfile.new
end
Should work for you.

Rails Create has_record? for a nested model

I've two models
class Article < ActiveRecord::Base
has_one :review
end
class Review < ActiveRecord::Base
belongs_to :article
end
Now I would like to have this method in Article
class Article < ActiveRecord::Base
has_one :review
def self.has_review?
end
end
I've tried with .count, .size....but I've errors...how can I do to have the following code working
#article = Article.find(xxx)
if #article.has_revew?
....
else
...
end
The reason why I need it is becaus I will have different action in views or controller, if there is one Review or none
Regards
class Article < ActiveRecord::Base
has_one :review
def has_review?
!!review
end
end
This just defines a method on the instance (def self.method defines a class method). The method tries to load review. If the review does not exist, it will be nil. !! just inverts it twice, returning true if a review exists or false if the review is nil.

how to access a method in a model from a controller in rails

I want to put logic in the model rather than controller.
class UsersController < ApplicationController
def somemethod
d = User.methodinmodel
end
end
class User < ActiveRecord::Base
def methodinmodel
"retuns foo"
end
end
I get an error that there is no methodinmodel for the User model.
Why?
If you want to be able to call methodinmodel on the User class in general rather than a specific user, you need to make it a class method using self:
class User < ActiveRecord::Base
def self.methodinmodel
"returns foo"
end
end
Your current method definition would only work if you called it on a user:
#user = User.create!
#user.methodinmodel # Works.
User.methodinmodel # Doesn't work.
Using the new implementation using self would allow you to call it like:
User.methodinmodel # Works.

Rails 3 Custom Class Question

I am trying to create a separate model class for image uploading, which was previously in the object's controller. I also want to make it agnostic, so I can upload images using one class from multiple objects.
In my original object's model class, I have the following now:
class Object < ActiveRecord::Base
after_save :photo_store
delegate :has_photo?, :photo, :photo_path, :store_photo, :photo_filename, :to => :photo_store
def photo_store
PhotoStore.new(self)
end
end
Then, the PhotoStore class looks like this:
class PhotoStore
attr_reader :object
def initialize(object)
#object = object
end
def photo=(file_data)
unless file_data.blank?
#file_data = file_data
self.extension = file_data.original_filename.split('.').last.downcase
end
end
PHOTO_STORE = File.join RAILS_ROOT, 'public', 'photo_store'
def photo_filename
File.join PHOTO_STORE, "#{id}.#{extension}"
end
def photo_path
"/photo_store/#{id}.#{extension}"
end
def has_photo?
File.exists? photo_filename
end
private
def store_photo
if #file_data
FileUtils.mkdir_p PHOTO_STORE
File.open(photo_filename, 'wb') do |f|
f.write(#file_data.read)
end
end
end
end
However, this throws the error below when I try and use the has_photo? method in the object's view.
undefined local variable or method `id' for #
Do I need to put some other type of relationship in place between the Object and PhotoStore?
And a separate question: What's the best way to make this agnostic? Since it uses just the ID of the object, I could just include the Object's name in the filename, but is that the best way to do it?
Thanks!
Because at File.join PHOTO_STORE, "#{id}.#{extension}" you call method PhotoStore#id, but it does not exists.
You should do that
File.join PHOTO_STORE, "#{#object.id}.#{#object.extension}"

Modifying a rails model at runtime

on a previous question, I was searching for a way to
dynamic valitating my models.
Advice on "Dynamic" Model validation
The solution that I got working is:
def after_initialize
singleton = class << self; self; end
validations = eval(calendar.cofig)
validations.each do |val|
singleton.class_eval(val)
end
end
On my actual app, I have 2 models
class Calendar < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :calendar
def after_initialize
singleton = class << self; self; end
validations = eval(calendar.cofig)
validations.each do |val|
singleton.class_eval(val)
end
end
end
As you can see, the validation code that should be added to the Event class lies on the Calendar field "config".
Works fine for a existing Event, but doesn't for a new record. That's because, at the time that after_initialize is called, the association doesn't exists yet.
I can't find a way to do that besides putting the config values on Event itself.
Any advices?
Tks!
You probably want to run your validation code during the validation phase, not the initialize phase. Try this:
class Calendar < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :calendar
validate do |event|
validations = eval(calendar.cofig)
validations.each do |val|
eval(val)
end
end
end