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.
Related
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 >
A model Country has a attribute code which is automatically converted to lowercase by a before_save callback. Is it possible to force this behaviour on "magic" methods without rewriting large chunks of ActiveRecord::Base?
class Country < ActiveRecord::Base
attr_accessible :code
validates :code, :presence => true
validates_uniqueness_of :code, :case_sensitive => false
before_save do |country|
country.code.downcase! unless country.code.nil?
end
end
RSpec
describe Country do
describe 'data normalization'
before :each do
#country = FactoryGirl.create(:country, :code => 'DE')
end
# passes
it 'should normalize the code to lowercase on insert' do
#country.code.should eq 'de'
end
# fails
it 'should be agnostic to uppercase finds' do
country = Country.find_by_code('DE')
country.should_not be_nil
end
# fails
it 'should be agnostic to uppercase finds_or_creates' do
country = Country.find_or_create_by_code('DE')
country.id.should_not be_nil # ActiveRecord Bug?
end
end
This is what I came up with, altough I really hate that approach (as mentioned in the question). An easy alternative would be to set the column, table or whole database up to ignore case (but this is db dependendt).
class Country < ActiveRecord::Base
attr_accessible :code
validates :code, :presence => true
validates_uniqueness_of :code, :case_sensitive => false
before_save do |country|
country.code.downcase! unless country.code.nil?
end
class ActiveRecord::Base
def self.method_missing_with_code_finders(method_id, *arguments, &block)
if match = (ActiveRecord::DynamicFinderMatch.match(method_id) || ActiveRecord::DynamicScopeMatch.match(method_id))
attribute_names = match.attribute_names
if code_index = attribute_names.find_index('code')
arguments[code_index].downcase!
end
end
method_missing_without_code_finders(method_id, *arguments, &block)
end
class << self
alias_method_chain(:method_missing, :code_finders)
end
end
end
I have created following class just to have validation and custom validation for parameters for my ajax message:
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :attributes
validates_inclusion_of :temp_inside, :in => 0..30
validates_inclusion_of :temp_outside, :in => -50..20
validates_presence_of :isol
validates_inclusion_of :height, :length, :width, :in => 1..500
validate :temp_outside_cannot_be_greater_than_temp_inside
def temp_outside_cannot_be_greater_than_temp_inside
errors.add(:temp_outside, "can't be greater than total value") if
temp_outside > temp_inside
end
def initialize(attributes = {})
#attributes = attributes
end
def read_attribute_for_validation(key)
#attributes[key]
end
def persisted?
false
end
end
and getting following error inside "temp_outside_cannot_be_greater_than_temp_inside"
NoMethodError (undefined method `>' for :temp_outside:Symbol):
any idea?
You need to check the value of temp_outside, as it seems to be a symbol in this case, not a number
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
After searching for a tableless model example I came across this code which seems to be the general consensus on how to create one.
class Item < ActiveRecord::Base
class_inheritable_accessor :columns
self.columns = []
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
def all
return []
end
column :recommendable_type, :string
#Other columns, validations and relations etc...
end
However I would also like it to function, as a model does, representing a collection of object, so that I can do Item.all.
The plan is to populate Items with files and each Item's properties will be extracted from the files.
However currently if I do Item.all I get a
Mysql2::Error Table 'test_dev.items' doesn't exist...
error.
I found an example at http://railscasts.com/episodes/219-active-model where I can use model features and then override static methods like all (should have thought of this before).
class Item
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :content
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
class << self
def all
return []
end
end
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
Or you could do it like this (Edge Rails only):
class Item
include ActiveModel::Model
attr_accessor :name, :email, :content
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
end
By simply including ActiveModel::Model you get all the other modules included for you. It makes for a cleaner and more explicit representation (as in this is an ActiveModel)