Overriding a parent method and making it private/protected - ruby-on-rails

I'm trying to make the create method private/protected for an ActiveRecord model. I want to do something like this:
class Product < ActiveRecord::Base
def self.create(options)
private
super(options)
end
end
so that I am unable to do Product.create(...). However, I need to do this
class Pencil < Product
def self.create(options)
options["category"] = "stationary"
super(options)
end
end
so that I can do this Pencil.create(...). Thanks in advance!

class Product < ActiveRecord::Base
class << self
def create(options)
super(options)
end
private :create
end
end
class Pencil < Product
class << self
def create(options)
options["category"] = "stationary"
super(options)
end
end
end

Related

Pundit::NotAuthorizedError / Problem with pundit authorize

I'm trying to update user's adress in a form but i dont understant why i'm not authorize to perform, this is my code :
class AddressesController < ApplicationController
def update
#address = current_user.addresses.last
authorize #address
#address.update!(address_params)
end
private
def address_params
params.require(:address).permit(:first_name, :last_name, :city, :country, :postcode, :phone_number, :street_address, :optional_address, :user_id)
end
end
class AddressPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
def update?
true
end
end
end
and this is the error :
Pundit::NotAuthorizedError in AddressesController#update
not allowed to update? this Address
You've defined the update? method within the nested Scope class, but it's supposed to be defined directly in the policy class.
Instead of this:
class AddressPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
def update?
true
end
end
end
You need to do this:
class AddressPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def update?
true
end
end

Should I avoid overriding a rails model method when possible?

More specifically, if I have two options: do a check in the controller or override an association method in the model, which one should I prefer?
Edit:
class Book < ApplicationRecord
belongs_to :author
def author
super || build_author
end
end
Is the code above ok or should I prefer another solution like the one below?
class BooksController < ApplicationController
def update
set_author
if #book.update_attributes(params[:book])
#redirect
else
#render show page - unprocessable entity
end
end
def set_author
a = Author.where(fields)
#book.author = a || #book.build_author
end
end

Using the created nested attribute object after parent is saved in Rails 4

I'm trying to access a created object that is created using nested attributes when the parent item is saved, and I'm not sure how to do that. My current setup is such:
foo-model.rb
class Foo < ActiveRecord::Base
has_many :bars, dependent: :destroy
accepts_nested_attributes_for :bars
end
bar-model.rb
class Bar < ActiveRecord::Base
belongs_to :foo
end
foos-controller.rb
class FoosController < ApplicationController
def create
#foo = Foo.create(foo_params)
if #foo.save
# What I want to do is essentially this
bar.do_something
# Redirect
redirect_to path
end
end
private
def foo_params
params.require(:foo).permit(:attribute, bar_attributes: [:id, :attribute1])
end
end
First of all, use ::new, not ::create on Foo.
def create
#foo = Foo.new(foo_params)
if #foo.save
After the #save call, you are then free to do any action on the associated Bars:
if #foo.save
#foo.bars.each do |bar|
bar.do_something
end
Foo.create will trigger the 'save' event. Instead, do this
class FoosController < ApplicationController
def create
#foo = Foo.new(foo_params)
if #foo.save
redirect_to path
end
end
end
In the model, add
class Foo < ActiveRecord::Base
has_many :bars, dependent: :destroy
accepts_nested_attributes_for :bars
after_save :bars_do_something
def bars_do_something
bars.each{|b| b.do_something}
end
end
Instead of updating the foo.bars in an each (not a good idea, according to law of Demeter), you could update bar after it's created
class Bar < ActiveRecord::Base
belongs_to :foo
after_create :do_something
def do_something
do_something
end
end

Namespacing issue in rails

I have a class A in module M like below
Module M
class A
def method1
# how to instantiate a model having same name as A
#like A.first
end
end
end
In my models I have a class A
class A < ActiveRecord::Base
end
You can access the global scope using the :: operator, e.g:
Module M
class A
def method1
::A.first
end
end
end

Modularize an ActiveRecord class

If I have classes like this,
class A < ActiveRecord::Base
include ExampleModule
end
class B < ActiveRecord::Base
include ExampleModule
end
module ExampleModule
module ClassMethods
...
end
def included(base)
...
end
end
how do I get the a reference to class A or B inside of ExampleModule upon referencing including this module into either one of those classes? I'm asking this question because I wanted to do something like adding has_one :association or after_create :do_something to class A or B via including ExampleModule such as below.
class A < ActiveRecord::Base
include ExampleModule
end
class B < ActiveRecord::Base
include ExampleModule
end
module ExampleModule
has_one :association
after_create :do_something
module ClassMethods
...
end
def included(base)
...
end
end
Is there a better way to do this as well? Thanks!
If you extend ActiveSupport::Concern, you should be able to do it when the module is included:
module ExampleModule
extend ActiveSupport::Concern
def do_something
# ...
end
included do
has_one :association
after_create :do_something
end
end
If what you're wanting to do is call has_one or after_create depending on which class is including the module you can do this
module Extender
def self.included(base)
if base.name == A.name
# do stuff for A
has_one :association
elsif base.name == B.name
# do stuff for B
after_create :do_something
end
end
end

Resources