I have a Sequel model like this:
class User < Sequel::Model
include Notificatable
def validate
super
validates_presence [:email]
end
end
# concerns/notificatable.rb
module Notificatable
extend ActiveSupport::Concern
included do
def validate
super
validates_presence [:phone]
end
end
end
And here I got a problem: Notificatable validate method overrides the same method in the User model. So there is no :name validations.
How can I fix it? Thanks!
Why use a concern? Simple ruby module inclusion works for what you want:
class User < Sequel::Model
include Notificatable
def validate
super
validates_presence [:email]
end
end
# concerns/notificatable.rb
module Notificatable
def validate
super
validates_presence [:phone]
end
end
Related
What I need if some user has an attribute, this will not need to be confirmed.
I've seen a couple of post about this but I cannot understand it (i'm kinda newbie in Rails).
In my user.rb
def confirmation_required?
if self.name == 'Joe'
false
end
end
I tried that out but nothing happens, is like always is false. I saw another post with this code:
def confirmation_required?
!confirmed?
end
#Put your conditions and job's done !
but how can I access to user data from my user.rb (model) Note that the user comes from HTTP Post request.
Can somebody help me?
Thanks
EDIT
Also I could just re-write Devise::RegistrationsController to something like this:
class RegistrationsController < Devise::RegistrationsController
def create
super do
if resource.name == 'Joe'
resource.skip_confirmation!
resource.save
end
end
end
end
Do you think this could solve it?
Thanks.
In your user model you can conditionally call skip_confirmation! in your before_save callback
class User < ActiveRecord::Base
before_save :skip_confirm # arbitrary method name
def skip_confirm
if self.name == 'Joe'
skip_confirmation!
end
end
end
or, you can use a block on before_save
class User < ActiveRecord::Base
before_save -> do
if self.name == 'Joe'
skip_confirmation!
end
end
end
I'm struggling with some kind of issue. I have a rails model (mongoid).
class User
include Mongoid::Document
include ActiveModel::SecurePassword
validate :password_presence,
:password_confirmation_match,
:email_presence,
field :email
field :password_digest
def password_presence
end
def email_presence
end
def password_confirmation_match
end
end
My goal is to call validations depends on which decorator I will use. Let's say I've got two decorators:
class PasswordDecorator < Draper::Decorator
def initialize(user)
#user = user
end
end
def RegistraionDecorator < Draper::Decorator
def initialize(user)
#user = user
end
end
So now when I create/save/update my user object inside RegistraionDecorator I would like to perform all validation methods.
RegistraionDecorator.new(User.new(attrbiutes))
But when I will do it inside PasswordDecorator I want to call for example only password_presence method.
PasswordDecorator.new(User.first)
When I move validations to decorator it won't work cuz its different class than my model.
How can I achieve that?
Try to use a Form Object pattern instead.
Here is an example (from a real project) of how it could be done with reform.
class PromocodesController < ApplicationController
def new
#form = PromocodeForm.new(Promocode.new)
end
def create
#form = PromocodeForm.new(Promocode.new)
if #form.validate(promo_params)
Promocode.create!(promo_params)
redirect_to promocodes_path
else
render :edit
end
end
private
def promo_params
params.require(:promocode).
permit(:token, :promo_type, :expires_at, :usage_limit, :reusable)
end
end
class PromocodeForm < Reform::Form
model :promocode
property :token
property :promo_type
property :expires_at
property :usage_limit
property :reusable
validates_presence_of :token, :promo_type, :expires_at, :usage_limit, :reusable
validates_uniqueness_of :token
validates :usage_limit, numericality: { greater_or_equal_to: -1 }
validates :promo_type, inclusion: { in: Promocode::TYPES }
end
Bonus: The model does not trigger validations and much easy to use in tests.
I am trying to setup belongs_to, validates, and default scopes in a module.
module MultiTenancy
class TenantNotSetError < StandardError ; end
def self.included(model)
class << model
belongs_to :tenant
validates :tenant_id, presence: true
default_scope -> {
raise TenantNotSetError.new unless Tenant.current_tenant
where(tenant_id: Tenant.current_tenant.id)
}
def multi_tenanted?
true
end
end
end
end
I keep getting a
NoMethodError: undefined method `belongs_to' for #<Class:User>
error.
What am I doing wrong?
This should work:
def self.included(base)
base.class_eval do
# your code goes here
end
end
The reason it doesn't work is you try to call belongs_to on metaclass of User, not on User.
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
I have a Module A, and there are several Class need to Mixin it, there is a method should be wrote as Class Method of that Module, but this method need to get data from the Tables which match these Classes. It that realizable?
module Authenticate
def password=(password)
if password.present?
generate_salt
self.hashed_password = Authenticate.encrypt_password(password, salt)
end
end
class << self
def encrypt_password(password,salt)
Digest::SHA2.hexdigest(password + salt)
end
end
private
def generate_salt
self.salt = self.object_id.to_s + rand.to_s
end
end
require 'authenticate_module'
class Administrator < ActiveRecord::Base
validates :password, :confirmation => true
attr_accessor :password_confirmation
attr_reader :password
include Authenticate
end
This is that method:
def authenticate(name,password)
if user = ???.find_by_name(name)
if user.hashed_password == Authenticate.encrypt_password(password,user.salt)
user
end
end
end
Use ActiveSupport::Concern to add class methods to every class that includes your module, then calling self in that method will return the class name.
It will be something like:
module Authenticate
extend ActiveSupport::Concern
module ClassMethods
def authenticate(name, password)
self.class # returns the name of the class that includes this module
end
end
end
class User
include Authenticate
end
# Now You can call
User.authenticate(name, password)
What ActiveSupport::Concern does is that whenever a class includes the module, it extends that class with the ClassMethods which here is equivalent to doing
class User
include Authenticate
extend Authenticate::ClassMethods
end