When I use Shoulda's validates_presence_of, it stumbles on a before_validation callback.
before_validation :set_document, :set_product, :set_price
I'm trying to get this spec to pass:
it { should validate_presence_of(:quantity).with_message("Please a quantity.") }
I have database defaults of 0 for a line item's quantity, unit_price, tax_rate, and price. Before a line item is validated I compute the price from the other attributes in case they have changed.
I get this error, and similar errors, for all of the attributes involved in this computation:
3) LineItem
Failure/Error: it { should validate_presence_of(:quantity).with_message("Please a quantity.") }
NoMethodError:
undefined method `*' for nil:NilClass
# ./app/models/line_item.rb:153:in `total_price'
# ./app/models/line_item.rb:223:in `set_price'
# ./spec/models/line_item_spec.rb:32:in `block (2 levels) in <top (required)>'
My callback, set_price, is very simple:
def set_price
self.price = total_price
end
And the total_price method is very simple as well:
def total_price
quantity * unit_price * (1 + tax_rate/100)
end
I'd appreciate any help with this one as I'm completely stumped. I did see some people post about custom validation methods. This seems so basic I can't figure it out how to proceed.
Since total_price runs before validation, quantity can be nil at the time the callback is executed. This is in fact what happens behind the scenes when the Shoulda matcher runs, which is why you get an error. It's trying to send the * method to quantity, which is nil.
Use after_validation or before_save instead.
Related
I am new to Rails and Ruby development but I am trying to create an object called Currency which takes in two params and does some calculations on them. I am using attr_accessor to set up the params and I put the file inside the lib directory.
Whenever I run rails console and try to do c = Currency.new(100, "CAD") I get the following error:
ArgumentError: wrong number of arguments (given 2, expected 0)
from (irb):5:in `initialize'
from (irb):5:in `new'
from (irb):5
I did make sure to include the file in application.rb. Here is a skeleton of my class:
class Currency
class << self
attr_accessor :input_value, :currency_iso
USD_ISO = "USD"
USD_TO_DM = 2.8054
def converted_value
convert_to_dm
end
private
def convert_to_dm
#input_value / USD_TO_DM
end
end
end
I have looked all over and I am stumped on what this issue may be. I have tried with and without an initialize method and I have tried creating a more basic version.
The problem here is that you are defining the method as a class method. And you are not defining the initialize method with those two params. Let's check the code below:
class Currency
attr_accessor :input_value, :currency_iso
USD_ISO = "USD"
USD_TO_DM = 2.8054
def initialize(input_value, currency_iso)
#input_value = input_value
#currency_iso = currency_iso
end
def converted_value
convert_to_dm
end
private
def convert_to_dm
input_value / USD_TO_DM
end
end
Also, due to you have already defined the attr_accessor you don't need to use the # when calling those attributes.
I found this post. It can help you to understand better the difference between class method and instance method.
So I'm writing code in which we need to issue refunds. I wrote a refund validator that checks to ensure that the refund is not for more than the original charge. However, in my specs I came to the realization that the associated charge isnt' present yet. Using FactorGirl. How can I make something like this work?
Validator
class RefundValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value <= record.charge.amount
record.errors[:attribute] << "is greater than original charge"
end
end
end
Validation
validates :amount, refund: true
Factory
FactoryGirl.define do
factory :refund do
association :client
association :charge
amount 99
end
end
Spec
context 'validations' do
%i(client_id therapist_id appointment_id booking_id value).each do |attr|
it { is_expected.to validate_presence_of attr }
end
it { is_expected.to validate_numericality_of(:value).is_greater_than_or_equal_to(0).is_less_than_or_equal_to(5) }
end
Error (i get this for all 4 attributes in the presence spec, not just client_id):
1) Refund validations should require client_id to be set
Failure/Error: it { is_expected.to validate_presence_of attr }
NoMethodError:
undefined method `amount' for nil:NilClass
# ./app/validators/refund_validator.rb:3:in `validate_each'
# ./spec/models/refund_spec.rb:20:in `block (4 levels) in <top (required)>'
I have tried explicitly creating the Factory model with a charge factory explicitly specified, but to no avail. No clue what's going on. Any help would be appreciated.
I would change unless x <= y to if x > y for the sake of readability
In any case, you should use record.try(:charge).try(:amount) instead of record.charge.amount to prevent NoMethodErrors in your validator.
Let me know if that fixes the problem already.
I'm currently working on fixing a bug in Rails plugin and I just found the method which causes the issue. My guess is that it's somehow related to one of the getter methods being overwritten in its ActiveRecord class Sprint. The method name is burndown and there is also an attribute called burndown which stores serialized Hash.
class Sprint < ActiveRecord::Base
serialize :burndown, Hash
...
def touch!
... do stuff ...
self.burndown = nil
self.save!
end
def burndown
... some crazy-ass method ...
end
end
So the burndown method somehow gets executed on save but I'm not really sure why, as there are no callbacks defined for the Sprint class. Is it possible that overwriting the getter method causes that?
Getter method gets called on save even if you don't have any validations or callback.
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.13/lib/active_model/dirty.rb:143:in `attribute_change'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.13/lib/active_model/dirty.rb:117:in `block in changes'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.13/lib/active_model/dirty.rb:117:in `map'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.13/lib/active_model/dirty.rb:117:in `changes'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/attribute_methods/dirty.rb:23:in `save'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/transactions.rb:259:in `block (2 levels) in save'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/transactions.rb:313:in `block in with_transaction_returning_status'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/transactions.rb:208:in `transaction'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/transactions.rb:311:in `with_transaction_returning_status'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/transactions.rb:259:in `block in save'"
"/home/user/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/transactions.rb:270:in `rollback_active_record_state!'"
"/home/usha/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.13/lib/active_record/transactions.rb:258:in `save'"
So if you override getter for an attribute, make sure that it still performs its original function
I have this error
ArgumentError (wrong number of arguments (1 for 0)):
lib/law/production.rb:20:in `clone'
lib/law/production.rb:20:in `clone_law'
lib/law/production.rb:11:in `initialize'
app/controllers/laws_controller.rb:86:in `new'
app/controllers/laws_controller.rb:86:in `prod_law'
app/controllers/laws_controller.rb:44:in `create'
when using this
module Law
class Production
attr_accessor :law
attr_accessor :creator
def initialize(law,current_user)
#law = law
#creator = current_user
clone_law
end
def current__user
User.find_by_authentication_token(session[:_csrf_token])
end
def clone_law
clone(#law)
end
end
end
where clone, create, prod_law are some methods
I assume Rails is expecting a hash but I don't understand why
Firstly, clone is a standard Ruby method.
Secondly, it expects no arguments at
all (as error message says), it should be called on the object you want to clone, like this:
#law.clone
I have two problems but I'll post them as 2 different questions. Let's start with the first one.
class Order < AbstractOrder
def update_status
self.all_created.each do |order|
order.status = :in_progress
order.save
end
end
end
In my specs when I try to call
Order.update_status
I get an error saying :
Failure/Error: Order.update_status
NoMethodError:
undefined method `update_status' for #<Class:0x00000103f256a8>
# ./spec/models/order_spec.rb:17:in `block (3 levels) in <top (required)>'
Finished in 0.10439 seconds
3 examples, 1 failure
Why? I thought this was a class method not an instance method. If I create an order and do order.update_status it works. What is wrong and how do I fix it?
To bind the method to the class object you need to define it as self.update_status, and the self before all_created shouldn't be necessary:
class Order < AbstractOrder
def self.update_status
all_created.each do |order|
order.status = :in_progress
order.save
end
end
end