I'm extending and including those files but still receive: undefined method after_initialize for Play:Class
class Play
extend ActiveModel::Callbacks
extend ActiveModel::Naming
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
include ActiveModel::Conversion
after_initialize :process_data
#...
end
I'm using Rails 4.
I don't know if you need all the ActiveModel overhead, but you could do it with less code:
class Play
include ActiveModel::Model
def initialize(attributes)
super(attributes)
after_initialize
end
private
def after_initialize
...
end
end
Try out following code
class Play
extend ActiveModel::Naming
extend ActiveModel::Callbacks
define_model_callbacks :initialize, :only => :after
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
include ActiveModel::Conversion
attr_accessor :name
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
run_callbacks :initialize do
puts 'initialize callback'
end
end
def attributes
return #attributes if #attributes
#attributes = {
'name' => name
}
end
end
#1.9.2-p290 :001 > Play.new(:name => 'The name')
#initialize callback
# => #<Play:0x00000006806050 #name="The name">
#1.9.2-p290 :002 >
Related
I am attempting to use the validates_with custom validations helper with Rails 4.
The following code is working in my application:
class Photo
validates_with CleanValidator
include ActiveModel::Validations
end
class CleanValidator < ActiveModel::Validator
def validate(record)
if record.title.include? "foo"
record.errors[:title] << "Photo failed! restricted word"
end
end
end
However I want to pass this helper to multiple attributes in multiple models, not just :title.
There is an example in validates_with section of guide that contains the following example:
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if options[:fields].any?{|field| record.send(field) == "Evil" }
record.errors[:base] << "This person is evil"
end
end
end
class Person < ApplicationRecord
validates_with GoodnessValidator, fields: [:first_name, :last_name]
end
This is what I want to achieve, substituting [:fields] for [:title] in my code example so that I can use CleanValidator for multiple models and multiple attributes (User.name, Photo.title etc).
I think you want the other example from the guides, each validator. You should be able to do
class CleanValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless ["Evil", "Other", "Restricted", "Words"].include?(value)
record.errors[attribute] << (options[:message] || "is a restricted word")
end
end
end
class Photo
include ActiveModel::Validations
attr_accessor :title
validates :title, clean: true
end
I have created class using Active Model,it is working as expected ,but am having few requirements
1) I want to use all method for that class.
2) Same way i want to use some query methods like where method for that class.
3) I want to create ActiveRecord::Relation so that i can do method chaining.
See my class:
require 'active_model'
require 'active_record'
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :content
validates_presence_of :name
validates_length_of :content, :maximum => 500
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
m = Message.new(:email => "test",:name => "test")
puts m.name #=> test
puts m.class #=> Message
puts m.valid? #=> true
Message.all
Message.where(:name => "test")
some more methods:
where
limit
having
group
select
order
uniq
Could you please help me to achieve this or give some guide lines.
So I have a object and I want to define a lifecycle hook such as before_create, after_create, etc.
I want to call this after_retire and have it setup so I can do the following:
class User < ActiveRecord::Base
include Active
after_retire :method
def method
#do stuff
end
end
So far I have a module setup like so but I keep getting a method undefined after_retire error on my User model.
module Active
extend ActiveSupport::Concern
included do
define_callbacks :retire
set_callback :retire, :after, :after_retire
default_scope { where(:retired => false) }
scope :retired, where(:retired => true)
end
def retire!
run_callbacks :retire do
update_attribute :retired, true
update_attribute :retired_at, Time.now.to_datetime
end
end
end
How should I be setting this up?
The before/after_callback syntax is handled in ActiveModel::Callbacks through #define_model_callbacks. The raw ActiveSupport::Callbacks will require you to use #set_callback without any syntactic sugar:
module Active
extend ActiveSupport::Concern
included do
define_callbacks :retire
default_scope { where(:retired => false) }
scope :retired, where(:retired => true)
end
def retire!
run_callbacks :retire do
update_attribute :retired, true
update_attribute :retired_at, Time.now.to_datetime
end
end
end
class User < ActiveRecord::Base
include Active
set_callback :retire, :after, :method
def method
#do stuff
end
end
If you want to have the after/before syntax, since you are working with an ActiveRecord (and thus ActiveModel) class, you can use:
module Active
extend ActiveSupport::Concern
included do
define_model_callbacks :retire
default_scope { where(:retired => false) }
scope :retired, where(:retired => true)
end
def retire!
run_callbacks :retire do
update_attribute :retired, true
update_attribute :retired_at, Time.now.to_datetime
end
end
end
class User < ActiveRecord::Base
include Active
after_retire :method
def method
#do stuff
end
end
Authlogic requires many Active Record features that are not available in Active Model and thus not available in Mongoid. How can Authlogic be used with Mongoid?
It is possible for Authlogic to be used with Mongoid by monkey patching Authlogic. YMMV.
First, add both Authlogic and Mongoid to the Gemfile.
gem 'mongoid', github: 'mongoid/mongoid' # Currently required for Rails 4
gem 'authlogic', '~> 3.3.0'
Next monkey patch Authlogic. First make the directory lib/modules and create file lib/modules/login.rb
# lib/modules/login.rb
module Authlogic
module ActsAsAuthentic
module Login
module Config
def find_by_smart_case_login_field(login)
# Case insensitivity for my project means downcase.
login = login.downcase unless validates_uniqueness_of_login_field_options[:case_sensitive]
if login_field
where({ login_field.to_sym => login }).first
else
where({ email_field.to_sym => login }).first
end
end
end
end
end
end
Tell rails to load your new module by editing config/application.rb
# config/application.rb
module YourApp
class Application < Rails::Application
config.autoload_paths += %W(#{config.root}/lib) # ADD THIS
end
end
Tell rails to require your module by creating config/initializers/authlogic.rb and add one line:
# config/initializers/authlogic.rb
require 'modules/login.rb'
The next step is to make a concern for being authenticable. Concerns are only available by default in Rails 4. You will need to add this directly to your authenticable class in Rails 3.
# app/models/concerns/authenticable.rb
module Authenticable
extend ActiveSupport::Concern
included do
include Authlogic::ActsAsAuthentic::Base
include Authlogic::ActsAsAuthentic::Email
include Authlogic::ActsAsAuthentic::LoggedInStatus
include Authlogic::ActsAsAuthentic::Login
include Authlogic::ActsAsAuthentic::MagicColumns
include Authlogic::ActsAsAuthentic::Password
include Authlogic::ActsAsAuthentic::PerishableToken
include Authlogic::ActsAsAuthentic::PersistenceToken
include Authlogic::ActsAsAuthentic::RestfulAuthentication
include Authlogic::ActsAsAuthentic::SessionMaintenance
include Authlogic::ActsAsAuthentic::SingleAccessToken
include Authlogic::ActsAsAuthentic::ValidationsScope
end
def readonly?
false
end
module ClassMethods
def <(klass)
return true if klass == ::ActiveRecord::Base
super(klass)
end
def column_names
fields.map &:first
end
def quoted_table_name
self.name.underscore.pluralize
end
def default_timezone
:utc
end
def primary_key
if caller.first.to_s =~ /(persist|session)/
:_id
else
##primary_key
end
end
def find_by__id(*args)
find *args
end
def find_by_persistence_token(token)
where(persistence_token: token).first
end
# Replace this with a finder to your login field
def find_by_email(email)
where(email: email).first
end
def with_scope(query)
query = where(query) if query.is_a?(Hash)
yield query
end
end
end
That is basically it. Here is a sample User class.
# app/models/user.rb
class User
include Mongoid::Document
include Mongoid::Timestamps
include Authenticable
field :name
field :email
field :crypted_password
field :password_salt
field :persistence_token
field :single_access_token
field :perishable_token
field :login_count, type: Integer, default: 0
field :failed_login_count, type: Integer, default: 0
field :last_request_at, type: DateTime
field :last_login_at, type: DateTime
field :current_login_at, type: DateTime
field :last_login_ip
field :current_login_ip
field :role, type: Symbol, default: :user
index({ name: 1 })
index({ email: 1 }, { unique: true })
index({ persistence_token: 1}, { unique: true })
index({ last_login_at: 1})
validates :name, presence: true
validates :email, presence: true, uniqueness: true
validates :crypted_password, presence: true
validates :password_salt, presence: true
acts_as_authentic do |config|
config.login_field = 'email' # Remember to add a finder for this
end
end
The UserSession would then look like this:
# app/models/user_session.rb
class UserSession < Authlogic::Session::Base
def to_key
new_record? ? nil : [self.send(self.class.primary_key)]
end
def to_partial_path
self.class.name.underscore
end
end
The application controller helpers are the same as always.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
private
def current_user_session
#current_user_session = UserSession.find unless
defined?(#current_user_session)
#current_user_session
end
def current_user
#current_user = current_user_session &&
current_user_session.user unless defined?(#current_user)
#current_user
end
end
That is it. Everything else should work exactly as it used to.
How do I get ActiveRecord attributes method functionality? I have this class:
class PurchaseForm
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name,
:surname,
:email
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
def initialize(attributes = {}, shop_name)
if not attributes.nil?
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
What I need to do, to have an attributes method to list all names and values from PurchaseForm object?
Here is the refactored variant:
class PurchaseForm
include ActiveModel::Model
def self.attributes
[:name, :surname, :email]
end
attr_accessor *self.attributes
# your validations
def to_hash
self.class.attributes.inject({}) do |hash, key|
hash.merge({ key => self.send(key) })
end
end
end
Now you can easily work with this class:
irb(main):001:0> a = PurchaseForm.new({ name: 'Name' })
=> #<PurchaseForm:0x00000002606b50 #name="Name">
irb(main):002:0> a.to_hash
=> {:name=>"Name", :surname=>nil, :email=>nil}
irb(main):003:0> a.email = 'user#example.com'
=> "user#example.com"
irb(main):004:0> a
=> #<PurchaseForm:0x00000002606b50 #name="Name", #email="user#example.com">
irb(main):005:0> a.to_hash
=> {:name=>"Name", :surname=>nil, :email=>"user#example.com"}
Even more, if you want to make this behaviour reusable, consider extraction of .attributes and #to_hash methods into separate module:
module AttributesHash
extend ActiveSupport::Concern
class_methods do
def attr_accessor(*args)
#attributes = args
super(*args)
end
def attributes
#attributes
end
end
included do
def to_hash
self.class.attributes.inject({}) do |hash, key|
hash.merge({ key => self.send(key) })
end
end
end
end
Now, just include it to your model and you're done:
class PurchaseForm
include ActiveModel::Model
include AttributesHash
attr_accessor :name, :surname, :email
# your validations
end
#instance_values could do the job:
class PurchaseForm
attr_accessor :name, :email
def attributes
instance_values
end
end
Output sample:
purchase.attributes #=> {"name"=>"John", "email"=>"john#example.com"}
I've managed to solve problem with this code:
class PurchaseForm
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :attributes,
:name,
:surname,
:email
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
def initialize(attributes = {})
#attributes = attributes
end
def persisted?
false
end
end
Let's try this
self.as_json
=> {:name=>"Name", :surname=>nil, :email=>"user#example.com"}
would it not be better to use
include ActiveModel::Serialization
def attributes
JSON.parse(self.to_json)
end