Why Rails data loaded from Fixtures are broken? - ruby-on-rails

I got two fixture files for Locales and Translations.
Locales are loaded fine, but Translations are broken:
Fixture
translation_05064:
id: 5064
key: control.base_search_users.panel.title
value: Поиск пользователей
interpolations:
locale: ru
locale_id: 16
is_proc: false
Becomes the record:
#<Translation id: 5064,
key: "control.base_search_users.panel.title",
value: "Поиск пользователей",
interpolations: nil,
locale: nil,
locale_id: 1019186233,
is_proc: false>
For some reason locale instead of 'ru' becomes nil, while locale_ib instead of 16 becomes 1019186233 for every fixture in a file.
I load fixtures such way:
require 'active_record/fixtures'
ActiveRecord::Fixtures.reset_cache
fixtures_folder = File.join(Rails.root, 'test', 'fixtures')
fixtures = Dir[File.join(fixtures_folder, '*.yml')].map {|f| File.basename(f, '.yml') }
ActiveRecord::Fixtures.create_fixtures(fixtures_folder, fixtures)
Translation model
class Translation < ActiveRecord::Base
validates :key, :uniqueness => {:scope => :locale_id}
validates :key, :locale, :locale_id, :value, :presence => true
belongs_to :locale
attr_accessible :key, :value, :locale_id, :locale
end
The migration
class CreateTranslations < ActiveRecord::Migration
def change
create_table :translations do |t|
t.string :key
t.text :value
t.text :interpolations
t.string :locale
t.integer :locale_id
t.boolean :is_proc, :default => false
end
add_index :translations, [:key, :locale]
end
end
I see in a test.log that inserts to DB contain broken data. When I load the fixture file in rails concole with YAML.load_file 'test/fixtures/translations.yml' I get correct Hash data.
Why that happens? How to fix that?
Rails-2.3.8, PostgreSql-8.4
UPDATE:
Tried named fixtures. In locales.yml:
locale_00016:
id: 16
code: ru
name: Русский
and in translations.yml all locale key values set to locale_00016
translation_05064:
id: 5064
key: control.base_search_users.panel.title
value: Поиск пользователей
locale: locale_00016
is_proc: false
YES, that works!
Translation id referred to existing and correct Locale record, but locale was still nil, to fix it I ran Locale.find_by_code('ru').translations.update_all(:locale => 'ru')

If locale_id is set, it seems ok; locale will be filled by Rails when you need it (the first time you will request it). 1019186233 is the id generated by rais when the fixtures are created.
Most of the time, you do not need to specify ids in fixtures, rails generate them for you, so fixtures like below should be fine (you should not define both localeand locale_id in the Translation fixture):
locales.yml:
ru:
what_ever_attr: value
...
translations.yml:
ru_title_translation:
key: control.base_search_users.panel.title
value: Поиск пользователей
interpolations:
locale: ru
is_proc: false

Related

Rails 5.2: One-time CSV import into model fails with UnknownAttributeError

I'm running Rails 5.2 on Windows. I followed advice from #Tom De Leu (Ruby on Rails - Import Data from a CSV file) to import CSV data into a table as follows:
task :import_currencies => :environment do
require 'csv'
filename = File.join Rails.root, "lib/tasks/forex20sep2018.csv"
CSV.foreach(filename, :headers => true) do |row|
Currency.create!(row.to_hash)
end
end
The CSV itself looks like this:
code,name,symbol
AED,UAE Dirham,د.إ
ALL,Lek,Lek
AMD,Armenian Dram,Lek
ANG,Netherlands Antillean Guilder,ƒ
...
but when I run the rake task:
rails import_currencies
it fails with this error:
rails aborted!
ActiveModel::UnknownAttributeError: unknown attribute 'code' for Currency.
So I modified my code as follows:
task :import_currencies => :environment do
require 'csv'
filename = File.join Rails.root, "lib/tasks/forex20sep2018.csv"
CSV.foreach(filename, :headers => true) do |row|
c = Currency.new
c.code = row[0]
c.name = row[1]
c.symbol = row[2]
c.save
puts c.errors.full_messages
end
end
And this runs perfectly well. The trouble is it's neither flexible nor elegant so I'd like to know why the original code didn't work.
(FWIW, I tried changing the CSV header as follows:
'code', 'name', 'symbol'
"code", "name", "symbol"
:code, :name, :symbol
None of these helped at all.)
#Vishal asked for the table:
create_table "currencies", force: :cascade do |t|
t.string "name"
t.string "code"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "symbol"
t.index ["code"], name: "index_currencies_on_code"
end
After as few suggestions, I have just made a dummy CSV with one record:
code,name,symbol
AAA,Dollar,$
and running this code:
task :import_currencies_org => :environment do
require 'csv'
filename = File.join Rails.root, "lib/tasks/forex20sep2018dummy.csv"
CSV.foreach(filename, :headers => true) do |row|
puts row.to_hash
Currency.create!(row.to_hash)
end
end
... still get this error:

RAILS: undefined method `map' caused by missing I18n translation

I changed the format for a datetime field in a RAILS 4 new.html.erb from :string to datetime and it caused error as below:
undefined method `map' for "translation missing: zh-CN.date.order":String
The view code causing the error above is:
<%= f.input :start_time, :label => t("Start Time"), required: true, :as => :datetime, :ampm => true, :minute_step => 10, :start_year => Date.today.year - 1, :end_year => Date.today.year + 1, :format => 'YYYY/MM/DD/HH/MM', :use_month_numbers => true, :include_blank => true %>
The RAILS source code blows up is in actionview/helpers/date_helper.rb:
def translated_date_order
date_order = I18n.translate(:'date.order', :locale => #options[:locale], :default => [])
date_order = date_order.map { |element| element.to_sym } #<<<<<<===blows up
forbidden_elements = date_order - [:year, :month, :day]
if forbidden_elements.any?
raise StandardError,
"#{#options[:locale]}.date.order only accepts :year, :month and :day"
end
date_order
end
I do have a file zh-CN.yml under /config/locale/ and it is providing translations for others except this one.
UPDATE portion of zh-CN.yml:
zh-CN:
#maint_recordx
Mfg Batches : '订单批次一览'
New Batch : '新批次'
Update Batch : '更新批次'
Edit Batch : '更新批次'
...........
After being bitten by this same error, I found that Rails sets the following key:
:'date.order'
to the value:
["year", "month", "day"]
for the default :en locale
You can confirm this by running the following snippet in rails console for a default rails install:
date_order = I18n.translate(:'date.order', :locale => :en, :default => [])
Notice I just switched #options[:locale] for the default :en value
The rails helper you reference, expects an array for the date_order value, and will blow up if it doesn't get one.
In my case, I improperly configured the I18n::Backend::ActiveRecord gem and therefore it interfered with the value being returned by I18n. You probably have a similar issue preventing the correct value for the :'date.order' key being returned.
EDIT:
In order to fix this, you should probably just need to install the gem 'rails-i18n'. It will handle returning the correct date formats for supported locales. In my case I had a custom configuration in my es.yml locale file that returned an incorrect date format.
Bingo !!! You just have to add the missing key translation to your local translations.
I solved it by adding
en:
date:
order: ["day", "month", "year"]
to
config/locales/en.yml

Why is Rails setting my attribute to nil on create?

Ruby 2.2.0 on Rails 4.2.2
I'm attempting to write a custom date validator (that is going to be expanded to handle some other cases once I get this part working), but right now it's failing to appropriately validate (and return false) on strings - it doesn't even run. It seems that rails is completely ignoring the date_ended value when it's set to a string. When I try the same test except with an integer it correctly validates and fails that validation. If I don't allow nil values, the validator correctly prevents record creation on a string value, but only because it rejects the nil value. Any and all suggestions are appreciated.
The same exact problem exists for date_started.
Edit: I've confirmed that the validation is failing on the second expectation and not the first validation by double-checking that CommitteeMember.count is 0 before the first expectation.
CommitteeMember:
class CommitteeMember < ActiveRecord::Base
belongs_to :committee
belongs_to :member
validates :committee_id, presence: true
validates :member_id, uniqueness: { scope: :committee_id }, presence: true
validates :date_started, date: true, allow_nil: true
validates :date_ended, date: true, allow_nil: true
end
schema.rb relevant lines:
create_table "committee_members", force: :cascade do |t|
t.integer "committee_id", null: false
t.integer "member_id", null: false
t.date "date_started"
t.date "date_ended"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
DateValidator (custom validator):
(note the printed value in the middle)
class DateValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
puts "Validating #{value}"
unless value.kind_of?(Date)
record.errors[attribute] << "must be of type date"
end
end
end
CommiteeMember relevant spec:
(note the printed value in the middle)
it 'should fail with a string for date_ended' do
expect(CommitteeMember.count).to eq(0)
CommitteeMember.create!(member_id: 1, committee_id: 1, date_ended: "S")
ap CommitteeMember.first
expect(CommitteeMember.count).to eq(0)
end
Spec Output:
$ rspec spec/models/committee_member_spec.rb
......#<CommitteeMember:0x00000007d1f888> {
:id => 1,
:committee_id => 1,
:member_id => 1,
:date_started => nil,
:date_ended => nil,
:created_at => Tue, 11 Aug 2015 19:22:51 UTC +00:00,
:updated_at => Tue, 11 Aug 2015 19:22:51 UTC +00:00
}
F
Failures:
1) CommitteeMember validations should fail with a string for date_ended
Failure/Error: expect(CommitteeMember.count).to eq(0)
expected: 0
got: 1
(compared using ==)
# ./spec/models/committee_member_spec.rb:52:in `block (3 levels) in <top (required)>'
Finished in 0.54864 seconds (files took 2.43 seconds to load)
7 examples, 1 failure
Failed examples:
rspec ./spec/models/committee_member_spec.rb:48 # CommitteeMember validations should fail with a string for date_ended
Since the attributes are date attributes, Rails automatically parses any values that you attempt to assign. If it cannot parse as a date, it will leave the value as nil, e.g. in the console:
c = CommitteeMember.new
c.date_started = 'S'
=> "S"
c.date_started
=> nil
In fact, Rails will actually parse a string into a Date:
c.date_started = '2016-1-1'
=> "2016-1-1"
c.date_started
=> Fri, 01 Jan 2016
c.date_started.class
=> Date
This means that you don't need to validate that your date fields are dates at all, because Rails won't store them otherwise. Instead, just validate that they exist:
validates_presence_of :date_started, :date_ended

Rails: using binary value to query a binary column returns nothing

I have a binary column which contains 256-bit checksums. I can store the checksums ok, but when I try to query via the checksum, nothing is returned.
d = Digest::SHA2.new
d.update "large str i'm creating the hash with"
begin
codebase = Codebase.find_or_create_by_checksum(d.digest)
rescue ActiveRecord::StatementInvalid => e
# handle duplicate record error
end
I've tried where and different versions of find. Nothing returns. When I use find_or_create_by_checksum, since it doesn't find anything it tries to create it and an exception is raised since I have a uniq index on the checksum column, but still I need to be able to get the record with the matching checksum.
create_table :codebases do |t|
t.binary :checksum, :null => false, :limit => 32
end
add_index :codebases, :checksum, :unique => true, :name => 'name_of_the_codebas_uniq_index'
Anybody know how to do this?
So if its binary on the database, I couldn't reproduce:
migration:
class Checksum < ActiveRecord::Migration
def up
create_table :checksums do |t|
t.binary :checksum, :null => false, :limit => 32
end
end
def down
end
end
And then trying it on the rails console:
ruby-1.9.2-p290 :009 > Checksum.create(:checksum => digest.digest)
SQL (0.4ms) INSERT INTO "checksums" ("checksum") VALUES (?) [["checksum", ",&\xB4kh\xFF\xC6\x8F\xF9\x9BE<\x1D0A4\x13B-pd\x83\xBF\xA0\xF9\x8A^\x88bf\xE7\xAE"]]
=> #<Checksum id: 1, checksum: ",&\xB4kh\xFF\xC6\x8F\xF9\x9BE<\x1D0A4\x13B-pd\x83\xBF\xA0\xF9\x8A^\x88bf\xE7\xAE">
ruby-1.9.2-p290 :010 > Checksum.first
Checksum Load (0.2ms) SELECT "checksums".* FROM "checksums" LIMIT 1
=> #<Checksum id: 1, checksum: ",&\xB4kh\xFF\xC6\x8F\xF9\x9BE<\x1D0A4\x13B-pd\x83\xBF\xA0\xF9\x8A^\x88bf\xE7\xAE">
ruby-1.9.2-p290 :011 > Checksum.find_by_checksum(digest.digest)
Checksum Load (0.1ms) SELECT "checksums".* FROM "checksums" WHERE "checksums"."checksum" = x'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' LIMIT 1
=> #<Checksum id: 1, checksum: ",&\xB4kh\xFF\xC6\x8F\xF9\x9BE<\x1D0A4\x13B-pd\x83\xBF\xA0\xF9\x8A^\x88bf\xE7\xAE">
So it works as expected.....

Rails is not inputting user data to postgresql

I am having an issue with Rails not inputting values to postgresql. The database itself is connected. When I run db:create:all (snippet from database.yml)
development:
adapter: postgresql
encoding: unicode
database: website_development
username: postgres
password: *******
host: 127.0.0.1
port: 9435
(test: is the same but with database: website_test instead of website_development) all the databases are created for test and development. When I run my db:migration the user table is also created e.g. snippet from migration file "date"_create_user.rb
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :username
t.string :email
t.timestamps
end
end
def self.down
drop_table :users
end
end
(I have checked in pgAdmin and found the tables that where created)But when I try to insert data from the console e.g.(this was run in sandbox)
irb(main):001:0> User.create!(:username => "John", :email => "john#example.com)
=> #<User id: 1, username: nil, email: nil, created_at: "2011-04-26 22:00:28", u
pdated_at: "2011-04-26 22:00:28">
here is the sql produced on a different create! I had run
[1m[35mSQL (2.0ms)[0m INSERT INTO "users" ("username", "email", "created_at", "updated_at") VALUES (NULL, NULL, '2011-04-26 20:53:43.363908', '2011-04-26 20:53:43.363908') RETURNING "id"
Any help as to why rails is creating the databases and tables fine but can't find the proper username and email to enter into sql.
P.S. I am running Rspec for my tests and have made several tests regarding the values of username and email not being nil to which all succeed.
......................
Finished in 1.62 seconds
22 examples, 0 failures
Notification failed: 201 - The destination server was not reachable
Notification failed: 201 - The destination server was not reachable
As you can see all Rspec tests are green but it to is having trouble connecting to the postgres server
Thank you in advance for any advice.
Update: added user model snippet
class User < ActiveRecord::Base
attr_accessor :username, :email
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
username_regex = /\A[\w\d]+\z/i
validates :username, :presence => true,
:format => { :with => username_regex },
:length => { :maximum => 30},
:uniqueness => { :case_sensitive => false }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
end
==Answer==
These were my mistakes:
Part 1: By changing attr_accessor to attr_accessible all my tests worked properly, and everything that needed to went to red, this also allowed me to add :email details but not :username details which leads to part 2.
Part 2: For some reason rails didn't like the fact that my table was named :user and my column was named :username. So I tried changing :username to :loginname which fixed the problem entirely.
Thank you everyone for all your help.
To isolate this you may want to construct a unit test to replicate the problem, then repair it as required. At first I suspected it would be a case of protected attributes, but it appears you have made them accessible, which is the correct thing to do.
Calling create! directly is somewhat hazardous as you are not easily able to capture the object that is half-created in the event of an exception. This is because although the exception contains a reference to a model, it is not clear if the User model or some other model caused the exception in the first place without additional digging.
A more reliable approach is this:
def test_create_example
user = User.new(:username => "John", :email => "john#example.com")
assert_equal 'John', user.username
assert_equal 'john#example.com', email
user.save
assert_equal [ ], user.errors.full_messages
assert_equal false, user.new_record?
end
If an error occurs in the validation stream you will see the error listed alongside what should be an empty array. It also checks that the record has been saved by testing that it is no longer a new record as records can be valid but fail to save if a before_save or before_create filter returns false, something that happens by accident quite often.
If you call new and then save you have an opportunity to inspect the newly prepared object before it is saved, as well as after.

Resources