I am having an annoying issue with using Globalize + RSpec + factory_girl.
I have a model with an attribute translated, and when creating factories using factory_girl the issue occurs. The code explains it perfectly:
Translation migration:
def self.up
CandidateBranch.create_translation_table!({
name: {type: :string, null: false, limit: 150 }
}, {
migrate_data: true
})
end
Model:
class CandidateBranch < ActiveRecord::Base
translates :name
####### Validations ---------------------------------------------------------
validates :name, presence: true, length: { in: 2..150 }
####### more code
end
Factory:
FactoryGirl.define do
factory :candidate_branch do
sequence(:id) { |id| id }
sequence(:name) { “trying out" }
end
end
Test:
require 'rails_helper'
RSpec.describe CandidateBranch, type: :model do
context "Validate" do
it "has a valid factory" do
record = FactoryGirl.attributes_for(:candidate_branch)
puts "parameters => #{record.inspect}"
record = CandidateBranch.create record
puts "parameters => #{record.inspect}"
expect(record).to be_valid
end
end
end
Logs:
▶ RAILS_ENV=test bundle exec rspec spec/models/candidate_branch_spec.rb
"parameters => {:id=>1, :name=>\"trying out\"}"
F
Failures:
1) CandidateBranch Validate has a valid factory
Failure/Error: record CandidateBranch.create record
ActiveRecord::StatementInvalid:
Mysql2::Error: Field 'name' doesn't have a default value: INSERT INTO `candidate_branches` (`id`, `created_at`, `updated_at`) VALUES (1, '2015-02-18 12:27:57.486623', '2015-02-18 12:27:57.486623’)
Mysql Transaction:
(0.3ms) BEGIN
CandidateBranch::Translation Load (0.5ms) SELECT `candidate_branch_translations`.* FROM `candidate_branch_translations` WHERE `candidate_branch_translations`.`candidate_branch_id` = 1
(0.4ms) SAVEPOINT active_record_1
SQL (0.6ms) INSERT INTO `candidate_branches` (`id`, `created_at`, `updated_at`) VALUES (1, '2015-02-18 12:27:57.486623', '2015-02-18 12:27:57.486623')
Mysql2::Error: Field 'name' doesn't have a default value: INSERT INTO `candidate_branches` (`id`, `created_at`, `updated_at`) VALUES (1, '2015-02-18 12:27:57.486623', '2015-02-18 12:27:57.486623')
(0.2ms) ROLLBACK TO SAVEPOINT active_record_1
(0.3ms) ROLLBACK
As seen in the record, although among the parameters attribute ‘name' is defined when creating the record in the database query, the translation table obviously finds nothing and then try to create the registry without the field translated, which fails.
However, if we comment translation statement in the model…
class CandidateBranch < ActiveRecord::Base
#translates :name
####### Validations ---------------------------------------------------------
validates :name, presence: true, length: { in: 2..150 }
####### more code
end
▶ RAILS_ENV=test bundle exec rspec spec/models/candidate_branch_spec.rb
"parameters => {:id=>1, :name=>\"trying out\"}"
"parameters => #<CandidateBranch id: 1, name: \"trying out\", created_at: \"2015-02-18 12:29:09\", updated_at: \"2015-02-18 12:29:09\”>"
MySQL transaction:
(0.3ms) BEGIN
(0.4ms) SAVEPOINT active_record_1
SQL (0.5ms) INSERT INTO `candidate_branches` (`id`, `name`, `created_at`, `updated_at`) VALUES (1, 'trying out', '2015-02-18 12:29:09.195756', '2015-02-18 12:29:09.195756')
(0.3ms) RELEASE SAVEPOINT active_record_1
(0.4ms) ROLLBACK
Is it a bug? Am I doing something wrong? Did this happened to anyone else?
I answer myself. Globalize does not fill translated fields in the table base model, what it does is move these fields to their table translations. This means that the validation of the attribute must be applied on the fields translated as the original field is empty.
As you found out yourself, globalize moves the translated values to the ..._translations table.
Your error message is caused by a NOT NULL database constraint on the base table, that was obviously set by a former migration with option null: false.
To make your example work, you need to add to your migration:
change_column_null :candidate_branches, :name, true
Related
I'm using Rails 5. I want one of the attributes in my model to fail validation if it consists of only letters or if it contains the pattern "\d:\d" anywhere in its value. I tried this
validates_format_of :my_attr, numericality: { greater_than: 0, :only_integer => true }, :allow_blank => true, :without => /(\d:\d|^\p{L}+$)/
But when I create a new object
2.4.0 :018 > ab = MyObjectTime.new({:my_attr => "ab"})
It is not indicating an error when I query "ab.errors" for the field in question. What's the correct way to write the regular expression above?
First and Foremost new method does not trigger any kind of validation on the object.
class Person < ApplicationRecord
validates :name, presence: true
end
>> p = Person.new
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {}
new method does not trigger any validation on an object as it is not hitting the database to save the record.
>> p.save
# => false
save method will try to create the record to the database and triggers the respective validation on the object.
>> p.valid?
# => false
When you hit the .valid? the method, it validates the object against the mentioned validation.
>> p.errors.messages
# => {name:["can't be blank"]}
>> p = Person.create
# => #<Person id: nil, name: nil>
Creating and saving a new record will send an SQL INSERT operation to the database.
Internal functionality of Person.create is Person.new and Person.save
When you are creating an object, it tries to create the valid record to the database and triggers the respective validation.
>> p.errors.messages
# => {name:["can't be blank"]}
>> p.save
# => false
>> p.save!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
>> Person.create!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
Secondly, validates_numericality_of and validates_format_of are the different set of validation helpers which you have mixed.
validates_numericality_of :my_attr, :only_integer => true, :allow_blank => true,
:greater_than => 0
validates_format_of :my_attr, :without => /(\d:\d|^\p{L}+$)/
This validation won't accept any such object :
MyObjectTime.new({:my_attr => "ab"})
MyObjectTime.new({:my_attr => "1:1"})
For more information you can take help from these validation helpers => http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html
Try something like this
validates :email, presence: true, format: { with: /\A([^\}\{\]\[#\s\,]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
I'm hitting a wall trying to set up tests for my user_tests model.
The error (one example of many) I get when running the tests (minitest):
ERROR["test_should_get_new", Admin::TestConfigsControllerTest, 2016-01-07 13:04:02 +0100]
test_should_get_new#Admin::TestConfigsControllerTest (1452168242.12s)
ActiveRecord::Fixture::FixtureError:
ActiveRecord::Fixture::FixtureError: table "user_tests" has no column named "user".
It's correct that there is no column named user, but there is a user_id and in other fixtures, this is working correctly. What is wrong with my setup that for this model the columns can't be found?
The fixture user_tests.yml:
user_test_one:
started: false
started_at:
finished: false
finished_at:
invited: true
invited_at: <%= Time.zone.now %>
user: michael
test_config: test_config_one
test_result:
test_result_type:
user_test_two:
started: false
started_at:
finished: false
finished_at:
test_result:
test_result_type:
invited: false
invited_at:
user: michael
test_config: test_config_two
test_result:
test_result_type:
Here both user, test_config and test_result should point to columns user_id, test_config_id and test_result_id. All give the same type of errors.
It works fine for users.yml where both company and user_group are links to other tables (shout out to Michael Hartle for the excellent Ruby on Rails Tutorial):
michael:
first_name: Michael
last_name: Example
email: michael#example.com
password_digest: <%= User.digest('password') %>
admin: true
activated: true
activated_at: <%= Time.zone.now %>
company: my-company
user_group: my-company-groep
archer:
first_name: Sterling
last_name: Archer
email: duchess#example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
company: my-company
user_group: my-company-groep
The first lines from the users_test.rb model:
class UserTest < ActiveRecord::Base
belongs_to :user, required: true
belongs_to :test_config, required: true
belongs_to :test_result, polymorphic: true
attr_accessor :step, :send_invitation
Can't find anything wrong with the database either. Here's a screenshot of both the users and user_tests tables in SQLite:
Any thoughts?
Update: It must have something to do with the test not finding the model user_test.rb for this fixture (user_tests.yml). I can convert the columns user and test_config to include _id but then an error occurs that the created_at and updated_at columns may not be nil (ActiveRecord::StatementInvalid: SQLite3::ConstraintException: NOT NULL constraint failed: user_tests.created_at). Those should be handled by Rails, but aren't. Why?
user_test_one:
started: false
started_at:
finished: false
finished_at:
invited: true
invited_at: <%= Time.zone.now %>
test_config_id: 1
user_id: 1
created_at: <%= Time.zone.now %>
updated_at: <%= Time.zone.now %>
Update 2: I've got a strong suspicion the problem is caused by including "Test" in the model/table name
Renaming the table and all corresponding code from user_test (and all variants) to user_survey solved the problem.
I'm having trouble understanding how to satisfy strong params when using button_to to do an update action. I'm trying to set an attribute called active to the value of true for an existing instance of a class called Plan.
(Note that I'm using HAML for my views here.)
This works:
= form_for(plan, remote: true) do |f|
= f.hidden_field :active, value: true
= f.submit 'set active'
But this doesn't:
= button_to "set active", plan_path(plan, active: true), method: :put, remote: true
Error
Completed 400 Bad Request in 7ms (ActiveRecord: 1.1ms)
ActionController::ParameterMissing - param is missing or the value is
empty: plan:
actionpack (4.2.1) lib/action_controller/metal/strong_parameters.rb:249:in 'require'
() Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:77:in 'plan_params'
() Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:45:in 'block in update'
actionpack (4.2.1) lib/action_controller/metal/mime_responds.rb:210:in 'respond_to'
() Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:44:in 'update'
Routes
user_plans GET /users/:user_id/plans(.:format) plans#index
POST /users/:user_id/plans(.:format) plans#create
new_user_plan GET /users/:user_id/plans/new(.:format) plans#new
edit_plan GET /plans/:id/edit(.:format) plans#edit
plan PATCH /plans/:id(.:format) plans#update
PUT /plans/:id(.:format) plans#update
DELETE /plans/:id(.:format) plans#destroy
Controller
# PATCH/PUT /plans/1
def update
respond_to do |format|
if #plan.update(plan_params)
format.js { flash.now[:notice] = "Plan was successfully updated." }
end
end
end
private
def plan_params
params.require(:plan).permit(:user_id, :name, :active)
end
It seems like such a silly issue but I can't figure it out and the API documentation doesn't seem to give any clues as to why it wouldn't be working.
These are but a few of the variations that I've tried (each is followed by its accompanying error message):
= button_to "set active", plan_path(plan: plan, active: true), method: :put, remote: true
ActionController::UrlGenerationError - No route matches
{:action=>"update", :active=>true, :controller=>"plans", :plan=>#,
:user_id=>"104"} missing required keys: [:id]:
= button_to "set active", plan_path(id: plan.id, active: true), method: :put, remote: true
Completed 400 Bad Request in 17ms (ActiveRecord: 2.1ms)
ActionController::ParameterMissing - param is missing or the value is
empty: plan: actionpack (4.2.1)
lib/action_controller/metal/strong_parameters.rb:249:in `require'
= button_to "set active", plan, active: true, method: :put, remote: true
ActionController::ParameterMissing - param is missing or the value is
empty: plan: actionpack (4.2.1)
lib/action_controller/metal/strong_parameters.rb:249:in 'require'
()
Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:77:in
'plan_params' ()
Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:45:in
'block in update'
I was able to finally resolve this based on the information in this thread.
Instead of placing the parameters in their own hash as another argument to button_to, I included them inside of the call to the plan_path method. The first argument needs to be the model's ID, and the second argument needs to be the model's name as a key with a hash of the desired attributes as its value. (Example below):
= button_to "set active", plan_path(plan.id, plan: { active: true }), method: :put, remote: true
If you look at the submitted params the difference is that your form results in params being
{ "id" => 123, "plan" => {"active" => true}, "controller" => "...", "action" => "..."}
Whereas the second results in
{ "id" => 123, "active" => true, "controller" => "...", "action" => "..."}
And in that case params[:plan] is nil, which leads to the error you see.
There are multiple ways to fix this. You could change the submitted parameters to match what the controller currently expects, for example
button_to set_active, plan, method: :put, remote: true, params: {"plan[active]" => 1}
(You could also have the parameters be part of the form URL as you were attempting but having as form fields feels slightly more correct to me).
Alternatively, if this update action isn't used by any other forms, then change it to match the submitted data. I wouldn't normally do this - it would be very easy but your app will be easier to think about if things behave in predictable ways.
According to http://apidock.com/rails/ActionView/Helpers/UrlHelper/button_to, params should be a separate hash
= button_to "set active", plan, method: :put, remote: true, params: { :active => true }
Let me explain first my model structure:
i have a status model:
class Status
include Mongoid::Document
include Mongoid::Search
include Mongoid::Timestamps
field :status_code, type: Integer
field :status_description, type: String
validates :status_code, :status_description, :transactiontype, :presence => true
belongs_to :transactiontype, :class_name => 'Transactiontype'
has_many :transactions, :class_name => 'Transaction', autosave: false
search_in :status_code, :status_description, :transactiontype => :transaction
def self.getStatus(transactiontype)
statuses = Status.where(:transactiontype_id => transactiontype).all
stats = []
puts "DATE DASHBOARD: #{Time.now.beginning_of_day} to #{Time.now.end_of_day}"
statuses.each do |status|
transactions = status.transactions.dateRange(Date.today.beginning_of_day, Date.today.end_of_day)
if transactions.length > 0
status.transactions = transactions
stats.push(status)
end
end
puts "SIZE : #{stats.size}"
stats
end
etc..
end
then i have another model called transactions:
class Transaction
include Mongoid::Document
include Mongoid::Search
include Mongoid::Timestamps
field :ref_no, type: String
field :trans_date, type: DateTime
belongs_to :status, :class_name => 'Status'
belongs_to :transactiontype, :class_name => 'Transactiontype'
validates :ref_no, :trans_date, :status, :presence => true
def self.dateRange(startdate,enddate)
puts "DATE : #{startdate} to #{enddate}"
if !startdate.blank?
where(:created_at => {"$gt" => startdate.beginning_of_day, "$lt" => enddate.end_of_day})
# where(:trans_date.gte => startdate.beginning_of_day, :trans_date.lte => enddate.end_of_day)
end
end
etc..
end
the weird part is that:
when im trying to execute:
Status.getStatus(params[:transactiontype_id])
i received the correct output but the transactions associated with the Status is being updated and each records before the filtered date is being updated with null status_id.
i already tried to add autosave: false but nothing works
can someone help me with this?
the solution is to convert the active record to json first
def self.getStatus(transactiontype)
statuses = Status.where(:transactiontype_id => transactiontype).all
stats = []
puts "DATE DASHBOARD: #{Time.now.beginning_of_day} to #{Time.now.end_of_day}"
statuses.each do |status|
ar_status = status.as_json
ar_status['transactions'] = status.transactions.dateRange(Date.today.beginning_of_day, Date.today.end_of_day)
if ar_status['transactions'].length > 0
stats.push(ar_status)
end
end
puts "SIZE : #{stats.size}"
stats
end
for some reason.. its auto saving the records.
I seen this question before on here but the solution on there is not working for me. Rake Aborted , on add_index(:users, :email, {:unique=>true})
In the Rails Tutorial by Michael Hartl he wants you to add_index :name, :email, unique: true I keep getting this error message.
$ bin/rake db:migrate RAILS_ENV=test
DL is deprecated, please use Fiddle
== 20141224060705 AddIndexToUsers: migrating ==================================
-- add_index(:users, :email, {:unique=>true})
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::ConstraintException: indexed columns are not unique: CREATE UNIQUE INDE
X "index_users_on_email" ON "users" ("email")c:/Sites/example/db/migrate/2014122
4060705_add_index_to_users.rb:3:in `change'
c:in `migrate'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
there are no users in the database. here is a picture of the database
You have to enclose the attributes you want to add the index into an array like so:
add_index :users, [:name, :email], unique: true
The first argument is the table name, the second can be an array of attributes or a single attribute, and then some options.
Hope this helps!