I have the following model:
class Article < ActiveRecord::Base
attr_accessible :body, :date_time, :excerpt, :permalink, :title, :premium, :status
before_create :set_defaults
validates_inclusion_of :status , :in => %w(draft pending published) , :allow_blank => false
def set_defaults
self.status ||= 'draft'
end
end
However when I go to create one like this:
article = Article.new( {
"title" => "test",
"body" => "test" ,
"excerpt" => "test"
} )
article.save
It fails and provides the error:
[:status, "is not included in the list"]
I'm trying to figure out why a default is not being set. Any ideas?
I think you want before_validation :set_defaults , :on => :create, validations run and then before create callbacks.
Related
Below is error and the spec listed for my failing test , not sure why it keep getting failed with following message even though I see the method getting invoked on #user.save
Failure/Error: expect(#performer).should_receive(:store_performer)
(#<RSpec::Expectations::ExpectationTarget:0x007f9ec42b5808>).store_performer(any args)
expected: 1 time
received: 0 times
Once again
if I comment the should_receive I do see the method getting invoked (confirmed by virtue of puts) on #user.save call (During that I also examine the object_id of #performer inside the spec and inside the invoked method and found them to be same)
Note: Basically the store_performer , update_minimum_payout and update_to_be_paid are after_save callback
FYI the test case
it 'should invoke callback method' do
new_single_performer
#performer = #user.performer
expect(#performer).should_receive(:store_performer)
expect(#performer).should_receive(:update_minimum_payout)
expect(#performer).should_receive(:update_to_be_paid)
#user.save
end
User.rb
class User < ActiveRecord::Base
# Activity Log (formerly called User Activity)
cattr_accessor :current_user
has_one :performer,
:dependent => :destroy, :validate => true,:inverse_of => :user,:include => :profile # auto load the profile
## Some more association
scope :performers, where(:is_performer => true)
scope :active, where(:active => true)
## Some more scope
## Validation
validates_presence_of :first_name, :last_name, :email #, :password, :password_confirmation
validates_presence_of :site_id, :unless => :is_referrer?
validates_format_of :first_name, :message => "Characters not allowed: $\##!^&*(){}", :with => /^[\w\s]+z/
validates_format_of :last_name, :message => "Characters not allowed: $\##!^&*(){}", :with => /^[\w\s]+z/
validates_format_of :email, :message => "Invalid Email address", :with => /[\w\.\-\#]+z/
before_save :geocode, :if => :should_change_geocode?
before_save :check_role
after_create :update_vendor_id
after_create :notify_admin
after_save :store_changes,:if => :if_changed?
after_save :setup_account,:if => :studio_active_changed?
after_save :approval_notification,:if => :send_notification_present?
end
Performer.rb
class Performer < ActiveRecord::Base
belongs_to :user, :conditions => {:is_performer => true},:inverse_of => :performer
# validations
validates :date_of_birth, :address_1, :city, :state, :zipcode, :country, :phone_number,
after_save :store_performer
after_destroy :destroy_performer_source
after_save :update_minimum_payout ,:if => :minimum_payout_changed?
after_save :update_to_be_paid ,:if => :include_in_payouts_changed?
private
def store_performer
## Performer Source is Mongo document
performer_source = PerformerSource.find_or_create_by(:performer_id => self.id)
performer_source.update_attributes(performer_hash)
performer_source
end
def update_minimum_payout
self.current_payout.update_attributes(:minimum_payout => self.minimum_payout)
end
def update_to_be_paid
self.current_payout.update_attributes(:to_be_paid => self.include_in_payouts)
end
end
PerformerSource
class PerformerSource
include Mongoid::Document
end
JFI my Rspec Version is 2.12.2
I am able to create and order and successfully run it and save it. I am using an order model and an order_transaction model. I added a field so that when filling out the order form and subscribing, the user can upload a photo of themselves that will be featured on the site. The whole process still works fine, except in the case where the user attaches a photo. When that happens, it just fails the credit card validation. Take off the attachment, and then it works again just fine.
order.rb
class Order < ActiveRecord::Base
has_many :transactions, :class_name => "OrderTransaction"
attr_accessor :card_number, :card_verification, :card_expires_on, :response
after_create :log_transaction
attr_accessible :first_name,
:last_name,
:address_one,
:address_two,
:city,
:state,
:zip,
:country,
:email,
:verify_email,
:term,
:shirt_color,
:shirt_size,
:card_type,
:card_number,
:card_verification,
:transactions_attributes,
:card_expires_on_month,
:card_expires_on_year,
:photo
has_attached_file :photo,
:styles => { :medium => "320x320>", :thumb => "100x100#" }
validates :first_name, :last_name, :address_one, :city, :state, :zip, :country, :presence = > true
validate :validate_card, :on => :create
validate :require_shirt_size, :on => :create
def purchase
self.response = GATEWAY.purchase(price_in_cents, credit_card)
errors.add(:base, response.message) unless self.response.success?
self.response.success?
end
def credit_card
#credit_card ||= ActiveMerchant::Billing::CreditCard.new(
:type => card_type,
:number => card_number,
:verification_value => card_verification,
:month => card_expires_on_month,
:year => card_expires_on_year,
:first_name => first_name,
:last_name => last_name
)
end
def price_in_cents
total = 0
if term == '1'
total = 2000
elsif term == '2'
total = 3500
elsif term == '3'
total = 4500
end
unless shirt_color == 'none'
total += 1599
end
total
end
def require_shirt_size
unless shirt_color == 'none'
if shirt_size == ''
errors.add(:base, "Shirt Size required if ordering a shirt.")
end
end
end
private
def log_transaction
self.transactions.create!(:action => "purchase", :amount => price_in_cents, :response = > self.response)
end
def validate_card
unless credit_card.valid?
credit_card.errors.full_messages.each do |message|
errors.add(:base, message)
end
end
end
end
order_transaction.rb
class OrderTransaction < ActiveRecord::Base
attr_accessible :action, :amount, :response
belongs_to :order
serialize :params
def response=(response)
self.success = response.success?
self.authorization = response.authorization
self.message = response.message
self.params = response.params
rescue ActiveMerchant::ActiveMerchantError => e
self.success = false
self.authorization = nil
self.message = e.message
self.params = {}
end
end
orders_controller.rb
def create
#order = Order.new(params[:order])
if #order.valid? && #order.purchase
render :success and return
else
render :action => 'new'
end
end
It's as if somehow, attaching a photo causes it to lose its ability to pass on the credit card data. I'm not sure where it's losing it, but it is. It feels like a bug in paperclip. Anyone had similar issues with uploading files while making a transaction with activemerchant? It's a fairly simple order type, there's three subscription options.
Even suggestions on how to better test and see what's going on would be very helpful, it's got me pretty stuck right now.
I have some class with this set :
class Campaign < ActiveRecord::Base
belongs_to :user
validates_presence_of :title, :description
def initialize
validates_format_of_many([:title, :description])
end
def validates_format_of_many(inputs)
if (!inputs.nil?)
inputs.each do |input|
validates_format_of input => /^[.]/, :allow_nil => false, :allow_blank => false
puts YAML::dump('test1')
end
else
puts YAML::dump('test2')
end
end
end
The problem is that I get some error message undefined methodinclude?' for nil:NilClass`
and in the stack
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.14/lib/active_record/attribute_methods.rb:257:in `method_missing'
/var/www/gitorious/app/models/campaign.rb:13:in `validates_format_of_many'
/var/www/gitorious/app/models/campaign.rb:12:in `each'
/var/www/gitorious/app/models/campaign.rb:12:in `validates_format_of_many'
/var/www/gitorious/app/models/campaign.rb:7:in `initialize'
/var/www/gitorious/app/controllers/campaigns_controller.rb:28:in `new'
/var/www/gitorious/app/controllers/campaigns_controller.rb:28:in `new'
Firstly if you overwrite initialize, you should call super, this will allow the super_class to perform its initialization tasks
def initialize
validates_format_of_many([:title, :description])
super
end
But it is recommended for AR to use callbacks. Check http://guides.rubyonrails.org/active_record_validations_callbacks.html
Example:
class Campaign < ActiveRecord::Base
belongs_to :user
validates_presence_of :title, :description
after_initialize { validates_format_of_many([:title, :description]) }
def validates_format_of_many(inputs)
if (!inputs.nil?)
inputs.each do |input|
validates_format_of input => /^[.]/, :allow_nil => false, :allow_blank => false
puts YAML::dump('test1')
end
else
puts YAML::dump('test2')
end
end
end
But be careful cause your validates_format_of_many will be called for Campaign.new when creating a new record, and also for Campaign.find ... when loading records from the database
I have a model, Tran that has a foreign key to the User model. In the view for creation a Tran (transaction), I have a dropdown that allows the user to select the User that started the transaction. When I post this transaction, the record is set with the correct user ID:
Then, in my Trans model I added "belongs_to", as I understand I should do this for foreign keys:
class Tran < ActiveRecord::Base
belongs_to :buying_user, :class_name => 'User'
Now, when my client passes up the params in the post, my Tran.new craps out because I am passing up a userID and not a full record. Is the
#trans_controller.rb
def create
#title = "Create Transaction"
#bombs on this call
#tran = Tran.new(params[:tran])
How am I supposed to handle this?
Update as requested:
tran.rb
class Tran < ActiveRecord::Base
has_many :transaction_users, :dependent => :destroy, :class_name => 'TransactionUser'
belongs_to :submitting_user, :class_name => 'User'
belongs_to :buying_user, :class_name => 'User'
accepts_nested_attributes_for :transaction_users, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
validates :description, :presence => true,
:length => {:maximum => 100 }
validates :total, :presence => true
validates_numericality_of :total, :greater_than => 0
validates :submitting_user, :presence => true
validates :buying_user, :presence => true
validates_associated :transaction_users
end
user.rb
class User < ActiveRecord::Base
has_many :trans
attr_accessor :password
attr_accessible :firstname, :lastname, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :firstname, :presence => true,
:length => {:maximum => 50 }
validates :lastname, :presence => true,
:length => {:maximum => 50 }
validates :email, :presence => true,
:format => {:with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
# Register callback to before save so that we can call extra code like password encryption
before_save :encrypt_password
# Class methods
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
end
# Public methods
def has_password?(submitted_password)
self.encrypted_password == encrypt(submitted_password)
end
def full_name
"#{self.lastname}, #{self.firstname}"
end
def self.active_users
# TODO
#User.find_
User.all
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt (string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
params hash on submit:
{"commit"=>"Submit",
"tran"=>{"total"=>"100",
"submitting_user"=>"1",
"description"=>"Description"},
"authenticity_token"=>"88qI+iqF92fo/M9rPfMs1CLpEXqFLGQXfj0c9krXXac=",
"utf8"=>"✓",
"user"=>"1"}
error:
User(#70040336455300) expected, got String(#70040382612480)
beginning of controller:
def create
#title = "Create Transaction"
#tran = Tran.new(params[:tran])
It crashes on the Tran.new line. Thanks so much!
Typically the User model would have has_many :transactions, :class_name => Tran
Then you would do this...
#user.transaction_create(params[:tran])
or
#user.build
it depends on what parameters are actually passed in params[:tran], but the idea is that the has_many side does the creating of the belongs_to.
I figured it out! The problem the whole time was that my db column name on my Trans table that linked to my Users table was submitting_user instead of submitting_user_id. Then, when I added the belongs_to association to submitting_user rails got confused and made that field a User, instead of an integer.
I have the following models (with corresponding database tables) in my Rails 3 application:
class User < ActiveRecord::Base
has_many :service_users
has_many :services, :through => :service_users
attr_accessor :password
attr_accessible :password, :password_confirmation, :service_ids
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
...
end
class Service < ActiveRecord::Base
has_many :service_users
has_many :users, :through => :service_users
...
end
class ServiceUser < ActiveRecord::Base
belongs_to :service
belongs_to :user
end
#User Controller:
class UsersController < ApplicationController
...
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated."
redirect_to #user
else
#title = "Edit user"
render 'edit'
end
end
...
end
I want to be able to update a User model's associated services without having to specify the password and password confirmation attributes. How can I do this?
Two options...
If you have simple logic, like only validating the password when a user is created, this will work:
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 },
:if => :new_record?
More likely, you'll want a combination so that users can update their password:
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 },
:if => :is_password_validation_needed?
# Protect the password attribute from writing an
# empty or nil value
def password=(pass)
return if !pass.present?
#password = pass
end
private
def is_password_validation_needed?
# Return true if the record is unsaved or there
# is a non-nil value in self.password
new_record? || password
end
This could help:
http://railscasts.com/episodes/41-conditional-validations
you will want to look into conditional validation to specify whether or not the password attribute in your model should be validated when you are updating/saving the model. here is a Railscast episode that, while a little dated, is still fairly straightforward and should get you off on the right path