Rails unit testing model validation :inclusion fails - ruby-on-rails

Model order.rb
class Order < ActiveRecord::Base
attr_accessible :address, :email, :name, :payment_type_id
belongs_to :payment_type
PAYMENT_TYPES = PaymentType.pluck(:id)
validates :name, :address, :email, :payment_type_id, :presence => true
validates :payment_type_id, :inclusion => {:in => PAYMENT_TYPES}
end
Model payment_type.rb
class PaymentType < ActiveRecord::Base
attr_accessible :name, :id
has_many :order
end
From browser, validation works fine, if it is wrong it give an error, else go forward.
But problem is when I run rake test:functionals from terminal. Test didn't pass the validation. If I comment this line:
validates :payment_type_id, :inclusion => {:in => PAYMENT_TYPES}
all is ok. I don't understand why it is working in one plase, but in tests not ? ...
Fixtures are all ok.
Please help.

Most likely the problem is, that you are storing your payment types in a constant.
For your tests to work, the PaymentTypes have to be available in the database before rails loads your Order model, and this might not be the case.
One way to get around this, would be to use a (memoized) class method to store your payment types. As long as you access this class method after all your PaymentTypes are in the database, you should be fine.
class Order < ActiveRecord::Base
validates :payment_type_id, :inclusion => { :in => self.payment_types }
def self.payment_types
##payment_types ||= PaymentType.pluck(:id)
end
end

Related

Upgrading attr_accessible from Rails 3 to Rails 4

I'm trying to systematically upgrade from rails 3 to rails 4 and all of my 25 models are based on attr_accessor! So before getting into that can anyone provide me a simple example on how to do this. I've read the documentation and other topics but it's not clear on how to do it since this is my first upgrade Rodeo.
class Settings < ActiveRecord::Base
image_accessor :favicon
attr_accessible :company_name, :show_hot_jobs, :show_students, :subheading, :show_testimonials, :show_on_boarding, :max_concurrent_applications
attr_accessible :image_uid, :max_concurrent_application_groups
attr_accessible :primary_color, :white_color, :gray_color, :opacity, :locale, :lang_nl, :lang_fr, :lang_de, :lang_en, :privacy_page
attr_accessible :show_evp, :show_contact_person, :show_jobs_for_you
attr_accessible :favicon, :favicon_uid, :remove_favicon, :retained_favicon
attr_accessible :home_url, :show_correspondence, :show_appointment
attr_accessible :sliderone_uid, :slidertwo_uid, :sliderthree_uid, :sliderfour_uid, :sliderfive_uid
attr_accessible :sliderone_link, :slidertwo_link, :sliderthree_link, :sliderfour_link, :sliderfive_link
attr_accessible :sliderone_testoverview, :slidertwo_testoverview, :sliderthree_testoverview, :sliderfour_testoverview, :sliderfive_testoverview
attr_accessible :sliderone_page, :slidertwo_page, :sliderthree_page, :sliderfour_page, :sliderfive_page
validate :any_lang_present?
validates :max_concurrent_applications, :numericality => { :greater_than_equal_to => 1 }
validates :max_concurrent_application_groups, :numericality => { :greater_than_equal_to => 1 }
# Fav Icon Validation
validates_property :ext, of: :favicon, :in => ['ico', 'png', 'gif']
has_paper_trail
has_many :setting_translations, :foreign_key => :setting_id
accepts_nested_attributes_for :setting_translations, :allow_destroy => true, :reject_if => :all_blank
attr_accessible :setting_translations_attributes, :allow_destroy => true
translates :subheading, :company_name, :image_uid, :home_url, :sliderone_uid, :slidertwo_uid, :sliderthree_uid, :sliderfour_uid, :sliderfive_uid
translates :sliderone_link, :slidertwo_link, :sliderthree_link, :sliderfour_link, :sliderfive_link
translates :sliderone_testoverview, :slidertwo_testoverview, :sliderthree_testoverview, :sliderfour_testoverview, :sliderfive_testoverview
translates :sliderone_page, :slidertwo_page, :sliderthree_page, :sliderfour_page, :sliderfive_page
attr_accessible can be converted like so:
From
class Settings
attr_accessible :home_url
accepts_nested_attributes_for :setting_translations
end
class SettingTranslation
attr_accessible :etc
end
To
class SettingsController
def create
#settings = Settings.new(settings_params)
# ...
end
private
def settings_params
params.require(:settings).permit(
:home_url,
:setting_translations_attributes => [:id, :_destroy, :etc]
)
end
end
Note, you have to include :_destroy if you want to allow destroy on that model (:allow_destroy => true), and you have to include all attributes that should be accessible from any nested attributes. Though you remove attr_accessible when you've permitted, you do not remove accepts_nested_attributes_for.
Just remove attr_accessible from model. and add permit params according to need in controller.
like below :
class SupportTicketsController < ApplicationController
def create
#support_ticket = SupportTicket.create(house_params)
......
end
private
def house_params
params.require(:support_ticket).permit(:subject, :message, ....)
end
end
and if you don't want to make this much changes then add "protected_attributes" gem https://github.com/rails/protected_attributes in your gemfile And everything would work as before.

Rails Admin NoMethodError in RailsAdmin::Main#delete

I have model Order:
class Order < ActiveRecord::Base
has_one :shipping_address
has_and_belongs_to_many :books
validates :first_name, :surename, :email, :street1, :country, :zipcode, presence: true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
validates :zipcode, numericality: true
accepts_nested_attributes_for :shipping_address
end
and model Book:
class Book < ActiveRecord::Base
DEFAULT_PRICE = 55.15
NEXT_BOOK_PERCENT = 5
has_and_belongs_to_many :pages
has_and_belongs_to_many :orders
validates :name, presence: {message: "Name can't be blank."}
validates_length_of :name, minimum: 3, maximum: 12, message: "Sorry, we can't create this book right now. Please contact us for further information."
validate :same_letter_validation
validate :validates_for_non_alphabetic
before_save :compile
#......
end
Also I have table books_orders (book_id, order_id)
When I try do delete order from RailsAdmin panel I get next error:
NoMethodError in RailsAdmin::Main#delete
undefined method `orders_books' for #
It says that error in this line:
- #abstract_model.each_associated_children(object) do |association, child|
Have you defined that "orders_books" method anywhere in your code? If so, can you please add it to your question. If you haven't, then the root cause of your issue is just that, that you're calling the "orders_books" method but it is not yet defined
Given that you reference "#books_orders" in your question, I believe it likely that you just swapped "books_orders" and "orders_books" at some point in your code
Thanks. It's bug of a Rails 4.1.1. I have update it to 4.1.4 and all works OK.

Ruby on Rails through Heroku object associations :belongs_to :has_many

Real newb question here: I've got courses
class Course < ActiveRecord::Base
attr_accessible :name, :number
has_many :posts
validates :name, presence: true
validates :number, presence: true
validates :number, :format => { :with => /\A\d\d[-]?\d\d\d\z/}
end
and I've got posts
class Post < ActiveRecord::Base
attr_accessible :comments, :course_id, :difficulty, :enjoyability, :hours, :professor, :ranking, :semester, :usefulness
belongs_to :course
end
Almost everything I have was auto-generated by Rails. There are a couple of things I try to do that I can't get to work:
When I "show" a course, I want to show each post associated with that course. However, everything I've tried has given me an error.
After even entering one post into the database (heroku forced me to use PostgreSQL) the index form no longer renders.
I'm almost positive I'm missing something with my associations between them. Does anybody have any ideas as to what I'm doing wrong?
You may get all posts through Course instance:
#course = Course.find(params[:id])
#posts = #course.posts

validates_uniqueness_of can't check on unsaved data?

I have a model called Science Subject Choice
class ScienceSubjectChoice < SubjectChoice
belongs_to :subject
belongs_to :subject_preference
validates_associated :subject
validates_associated :subject_preference
#TODO: validation
validates :priority, :presence => true, :numericality => true, :inclusion => {:in => 1..SubjectPreference::MAX_SCIENCE_SUBJECT_CHOICE}
validates_uniqueness_of :subject_id, :scope => :subject_preference_id
validates_uniqueness_of :priority, :scope => :subject_preference_id
end
the uniqueness validator don't work on unsaved data?
How can I solve it?
Solution:
Instead of validating in itself, the parent object should do the validation:
def validate_science_subject_choices_uniqueness
if science_subject_choices.map(&:priority) != science_subject_choices.map(&:priority).uniq
errors[:base] << "Duplicated priority in science subject"
end
end
Validations do not work like that. They are dynamic by nature. If you want database constraints, you have to specify it in your migrations. For instance, a :uniq => true would make sure that a value is unique in your model.

Rails: is there a model shortcut for listing my many "attr_accessible" + "validates :presence => true" attributes?

In many of my rails models, I have a number of fields which are what I think of as "normal" model attributes, ie things which are set by the user, then later displayed, and are mandatory parts of a model instance. It seems kind of overly verbose to have to always do this:
class Person < ActiveRecord::Base
attr_accessible :name
attr_accessible :age
attr_accessible :height
validates :name, :presence => true
validates :age, :presence => true
validates :height, :presence => true
end
Ideally I'd like to just tell rails "everything but the auto-generated ID field should be validated present and accessible for mass assignment". How can I do that, given that it's said to be bad security practice to just make everything available for mass assignment?
Update: The existing way also seems bad in that I type my list of attributes twice, which is quite error prone.
Define your own class method, say on ActiveRecord::Base:
class ActiveRecord::Base
def self.validate_presence_and_make_accessible *args
attr_accessible *args
validates_presence_of *args
end
end
Then in your models:
class Person < ActiveRecord::Base
validate_presence_and_make_accessible :name, :age, :height
end
I suck at naming methods sometime, btw. Feel free to rename to something better.
A little less verbose way:
class Person < ActiveRecord::Base
attr_accessible :name, :age, :height
validates :name, :age, :height, :presence => true
end
Specifying only protected attributes:
class Person < ActiveRecord::Base
attr_protected :id
validates :name, :age, :height, :presence => true
end
This is simpler:
class Person < ActiveRecord::Base
attr_protected :id
validates_presence_of :name, :age, :height
end
And regarding your comment about security practice of mass assignments, I think you should read this: http://b.lesseverything.com/2008/3/11/use-attr_protected-or-we-will-hack-you

Resources