Uniqueness validation for polymorphic model fails - validation

So I have this really weird problem. Here is my model:
class Clip < ApplicationRecord
belongs_to :owner, polymorphic: true
default_scope -> { order(created_at: :desc) }
validates :address, presence: true, uniqueness: { scope: [:owner_type, :owner_id] }
before_save { self.adress=adress.split("=").last }
validates :owner, presence: true
end
and the test
test "invalid creations" do
get new_clip_path
assert_no_difference "Clip.count" do
post clips_path, params: { clip: { address: "",
description: "bum bum bum" } }
end
assert_template "clips/new"
assert_match "blank", response.body
assert_no_difference "Clip.count" do
#clip allready in db, should refure
post clips_path, params: { clip: { address: "fWNaR-rxAic",
description: "bum bum bum" } }
end
assert_template "clips/new"
assert_no_match "blank", response.body
assert_match "Address has already been taken", response.body
end
The test is passing without a problem but I just realized by accident, that I can still create clips with the same address and owner, as often as I want to. This confuses me for two reasons: a) what is wrong with this line validates :address, presence: true, uniqueness: { scope: [:owner_type, :owner_id] } and b) why does the test not fail?

Related

Can't Use Validates Presence On Picture Rails 5.x

micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :tag1, presence: true, length: { maximum: 20 }
validates :tag2, presence: true, length: { maximum: 20 }
validates :tag3, presence: true, length: { maximum: 20 }
validates :picture, presence: true
validate :picture_size
validates :ispublic, inclusion: { in: [ true, false ] }
private
# Validates the size of an uploaded picture.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB in size.")
end
end
end
microposts_controller.rb snippet:
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save!
flash[:success] = "Post created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def micropost_params
params.require(:micropost).permit(:tag1, :tag2, :tag3, :picture, :ispublic)
end
Test that is failing snippet:
tag1 = "This"
tag2 = "Is"
tag3 = "Sparta"
image = File.open("test/fixtures/p_avatar.png")
assert_difference 'Micropost.count', 1 do
post microposts_path, params: {micropost: { tag1: tag1, tag2: tag2, tag3: tag3, picture: "image",
ispublic: false }}
end
Test error:
Validation failed. Picture can't be blank.
I am using CarrierWave...based on other answers I was wondering if I can't use the presence: true validation with a picture, but I thought the #micropost.save would just store whatever as a string (varchar). For testing purposes, I just want to make sure a valid string is passed, but maybe that isn't even necessary if CarrierWave and picture_size validation is enough.
Found it. In the Rails spec, there is a function called fixture_file_upload() which does all the heavy lifting of creating the image object for me (some sort of upload to a cache it seems) and making the Model.save() method validate it and store the string in the db.
Thanks to this site it informed me a bit on image upload testing without going through FactoryBot: https://jeffkreeftmeijer.com/carrierwave-rails-test-fixtures/
Ultimately I decided I micropost doesn't require a picture (A la Twitter).

Rails: Uninitialized constant User::VALID_EMAIL_REGEX

Background - I am working with rails version 5.1.2. I am using Cloud 9 IDE. My problem relates to Ch 11 Rails Tutorial by Michael Hartl.
Problem details - typing $ rails test:mailers:
Error:
UserMailerTest#test_account_activation:
NameError: uninitialized constant User::VALID_EMAIL_REGEX
app/models/user.rb:6:in <class:User>'
app/models/user.rb:1:in'
bin/rails test test/mailers/user_mailer_test.rb:5
app/models/user.rb:1 - app/models/user.rb:6:
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
Looks like you are missing the regex in your User model
class User < ApplicationsRecord
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
...
end
Just to add to Antarr Byrd's answer.
I had a similar experience when working on a Rails Application.
The issue was that I was defining the REGEX constant below the point of validation call.
That is, instead of this:
class User < ApplicationRecord
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
end
Do this rather:
class User < ApplicationRecord
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX }
end
That's all.
I hope this helps

Mongo, Mongoid join data from different models in Ruby on Rails

I'm adapting a Ruby on Rails application to work with MongoDB instead of PostgreSQL. I have this 2 class:
module Cms
class Content
include Mongoid::Document
include Mongoid::Orderable
field :title, type: String
field :position, type: String
field :text_body, type: String
field :expiration_date, type: Date
field :active, type: Boolean, default: false
validates :title, :text_body, presence: true
orderable base: 0
belongs_to :cms_content_category, class_name: 'Cms::ContentCategory'
scope :ordered, ->() { order(position: :asc) }
scope :not_expired, ->() { any_of({ expiration_date: nil }, { :expiration_date.gte => Date.today }) }
end
end
module Cms
class Submenu
include Mongoid::Document
include Mongoid::Orderable
field :title, type: String
field :position, type: String
field :external_url, type: String
field :open_as, type: String, default: '_self'
field :active, type: Boolean, default: false
delegate :url_helpers, to: :'Rails.application.routes'
validates :title, presence: true
validates :external_url, presence: true, if: Proc.new { |obj| obj.cms_content_id.nil? }
validates :cms_content_id, presence: true, if: Proc.new { |obj| obj.external_url.blank? }
orderable base: 0
belongs_to :cms_content, class_name: 'Cms::Content'
belongs_to :cms_menu, class_name: 'Cms::Menu'
scope :ordered, ->() { order(position: :asc) }
def url
self.external_url.present? ? self.external_url : url_helpers.content_show_path(self.cms_content_id)
end
end
end
The problem is that I don't know how to adapt the following SQL query to work with MongoDB.
scope :active, joins("FULL JOIN contents ON contents.id = cms_submenus.content_id")
.where("cms_submenus.active IS TRUE AND (
(cms_submenus.content_id IS NOT NULL AND ((contents.expiration_date >= :today OR expiration_date IS NULL) AND contents.archived IS NOT TRUE)) OR
(cms_submenus.external_url IS NOT NULL AND cms_submenus.external_url <> '')
)", today: Date.today)
This scope has to be called from Submenu model.
Someone has an idea on how to solve this problem?
I'm using Rails 4.2.0 with Ruby 2.2.0 and Mongoid 4.0.2 with MongoDB 2.6.8.
thank you

Validation doesn't work in "sexy" style

It seems that Rails doens't let me pass in more than one parameter when using this validation syntax. It always has a syntax method for an unexpected comma after the first argument.
class Apartment < ActiveRecord::Base
geocoded_by :location
after_validation :geocode
has_many :image_attachments
validates_associated :image_attachments
accepts_nested_attributes_for :image_attachments
validates :location, presence: true
validates :description, presence: true
validates :price, :presence => true
,:format => { with: /^[0-9]+$/, message: 'must be a number' }
validates :longitude, presence: true
end
It's bad formatting (and very "unsexy") to have the comma at the beginning of the next line.
Better to do...
validates :price, :presence => true,
:format => { with: /^[0-9]+$/, message: 'must be a number' }
...which should work fine.
A more consistent style is to use the Ruby 1.9 convention for key/value when key is a symbol.
validates :price, presence: true,
format: { with: /^[0-9]+$/, message: 'must be a number' }

FactoryGirl ActiveRecord::RecordInvalid: Validation failed: Name has already been taken

I have three models, Course, Category and partner, a course can have many categories and a course belongs to one partner. When i create my course factory i get the following error:
Partner has a valid factory for course
Failure/Error: expect(FactoryGirl.create(:course)).to be_valid
ActiveRecord::RecordInvalid:
Validation failed: Name has already been taken
Here are my models:
class Category < ActiveRecord::Base
has_many :categorisations
has_many :courses, :through=> :categorisations
belongs_to :user
#validation
validates :name, presence: true , uniqueness: { scope: :name }
end
class Partner < ActiveRecord::Base
has_many :courses
belongs_to :user
validates :name, presence: true, uniqueness: { scope: :name }
validates :short_name, presence: true
VALID_HEX_COLOR= /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/
validates :primary_color, presence: true, format: { with: VALID_HEX_COLOR}
validates :secondary_color, presence: true, format: { with: VALID_HEX_COLOR}
end
class Course < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: [:slugged, :history]
has_many :categorisations, :dependent => :destroy
has_many :categories, :through=> :categorisations
belongs_to :partner
belongs_to :user
# validates_uniqueness_of :title
validates :title, presence: true
# validates :start_date, presence: true
# validates :duration, presence:true
# validates :state, presence:true
validates :categories, length: { minimum: 1 , message:"please select"}
validates :partner_id, presence: true, allow_nil: false
end
Here are my factories:
factory :partner do |f|
f.name Faker::Name.name
f.short_name "UCT"
f.primary_color "#009bda"
f.secondary_color "#002060"
end
factory :course do |f|
f.title "Introduction to Accounting short course"
f.start_date "2014-02-27 00:00:00"
f.duration "10 WEEKS ONLINE"
partner
categorisation
end
factory :categorisation do |categorisation|
categorisation.category {|category| category.association(:category)}
categorisation.course {|course| course.association(:course)}
end
I am not to sure what i am doing wrong, if anyone could advise me on what the problem may be or the process i can go about fixing this problem may be that would be a great help
try this out:
factory :partner do |f|
f.sequence(:name) { |n| "#{Faker::Name.name} #{n}" }
f.short_name "UCT"
f.primary_color "#009bda"
f.secondary_color "#002060"
end
factory :category do |f|
f.sequence(:name) { |n| "Category #{n}" }
end
All that i had to do was to add the following line to my course factory:
categories {[FactoryGirl.create(:category)]}
couse factory:
factory :course do |f|
f.title "Introduction to Accounting short course"
f.start_date "2014-02-27 00:00:00"
f.duration "10 WEEKS ONLINE"
partner
categories {[FactoryGirl.create(:category)]}
end

Resources