How globalize gem decides which content goes to translated table? - ruby-on-rails

In my rails 4.2 app I have set it as the default locale by setting it in config/application.rb:
config.i18n.default_locale = :it
I have a simple "product" model
class Product < ActiveRecord::Base
translates :name, :description
end
My need is when I18n.locale = :it the content should be written in "products" table while for all others locales the content shoud go in the "product_translations" table.
Currently what happens is the following:
if
config.i18n.default_locale = :en
content is written to the "products" table, for all different locales the content goes to the "product_translations" table.
How can I change this?
EDIT
Using the console to test globalize behaviour I found that maybe I did not understand how globalize should work.
I was expecting that the "products" table is filled with default_locale (in my case :it) and the "product_translations" table is filled with other locales (:en, :fr, :de and so on).
Instead I see that whichever the locale is, fields that are indicated as
translates :name, :description
are always written in "product_translations" and "product" table only contains those fields that are not translated (in my case uom (unit of measure).
This is the output of the console after saving a new product with :en as locale.
[18] pry(main)> en_p=Product.create(:name=>"butter",
:description => "82% min fat butter",
:uom => "kg") (0.3ms)
BEGIN
SQL (0.7ms)
INSERT INTO "products" ("uom", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"
[
["uom", "kg"],
["created_at", "2015-07-14 05:49:09.097092"],
["updated_at", "2015-07-14 05:49:09.097092"]
]
SQL (0.7ms)
INSERT INTO "product_translations" ("locale",
"name",
"description",
"product_id",
"created_at",
"updated_at")
VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
[
["locale", "en"],
["name", "butter"],
["description", "82% min fat butter"],
["product_id", 5],
["created_at", "2015-07-14 05:49:09.116683"],
["updated_at", "2015-07-14 05:49:09.116683"]
]
(15.9ms)
COMMIT
=> #<Product:0xb63d3568
id: 5,
name: "butter",
description: "82% min fat butter",
uom: "kg",
created_at: Tue, 14 Jul 2015 05:49:09 UTC +00:00,
updated_at: Tue, 14 Jul 2015 05:49:09 UTC +00:00>
[19] pry(main)> I18n.locale=:it
=> :it
[20] pry(main)> it_p=Product.create(:name=>"olio di oliva EVO",
:description => "Olio di oliva extravergine spremuto a freddo",
:uom => "kg") (0.4ms)
BEGIN
SQL (0.5ms)
INSERT INTO "products" ("uom", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"
[
["uom", "kg"],
["created_at", "2015-07-14 05:51:34.772755"],
["updated_at", "2015-07-14 05:51:34.772755"]
]
SQL (0.8ms)
INSERT INTO "product_translations" ("locale",
"name",
"description",
"product_id",
"created_at",
"updated_at")
VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
[
["locale", "it"],
["name", "olio di oliva extravergine"],
["description", "Olio di oliva extravergine ottenuto unicamente per spremitura"],
["product_id", 6],
["created_at", "2015-07-14 05:51:34.779220"],
["updated_at", "2015-07-14 05:51:34.779220"]
]
(16.1ms)
COMMIT
=> #<Product:0xb6315ce8
id: 6,
name: "olio di oliva extravergine",
description: "Olio di oliva extravergine ottenuto unicamente per spremitura",
uom: "kg",
created_at: Tue, 14 Jul 2015 05:51:34 UTC +00:00,
updated_at: Tue, 14 Jul 2015 05:51:34 UTC +00:00>
Is this the default behaviour?
Do I need to remove translatable fields from the original table?

Related

Return virtual Postgres column after create

I have a Rails 7 model that uses Postgres' virtual column feature:
create_table :time_entries do |t|
# ...
t.virtual :duration, type: :interval, as: %(("to" - "from")::interval), stored: true, null: false
# ...
end
The problem is, that after I create a record via Rails create(...) these virtual column is nil:
[16] pry(main)> TimeEntry.create(from: Time.zone.now, to: 1.day.from_now)
TRANSACTION (0.3ms) BEGIN
TimeEntry Create (0.5ms) INSERT INTO "time_entries" ("from", "to", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"
[["from", "2022-11-18 06:45:11.419000"], ["to", "2022-11-19 06:45:11.420000"], ["created_at", "2022-11-18 06:45:11.420862"], ["updated_at", "2022-11-18 06:45:11.420862"]]
TRANSACTION (0.9ms) COMMIT
=> #<TimeEntry:0x0000ffff86f1ae10
id: 13,
from: Fri, 18 Nov 2022 06:45:11.419000000 UTC +00:00,
to: Sat, 19 Nov 2022 06:45:11.420000000 UTC +00:00,
duration: nil,
created_at: Fri, 18 Nov 2022 06:45:11.420862000 UTC +00:00,
updated_at: Fri, 18 Nov 2022 06:45:11.420862000 UTC +00:00>
When you reload the model, duration is set.
I found out, that this is due to Rails only returning the id column using RETURNING "id" at the end of the INSERT INTO statement. When you execute the query in Postgres directly you can return the generated duration column directly after the insert:
app_development=# INSERT INTO "time_entries" ("from", "to", "created_at", "updated_at") VALUES ( '2022-11-18 06:34:46.889000', '2022-11-18 06:34:56.889000', '2022-11-18 06:34:46.889000', '2022-11-18 06:34:46.889000') RETURNING "duration", "id";
duration | id
----------+----
00:00:10 | 11
(1 row)
Is it possible to customize the RETURNING "id" part of the SQL query in my model, so that the instance of TimeEntry already have the duration set after I create it?
EDIT:
I found the code segment inside the Postgres Adapter and tried to monkey patch it like this:
require "active_record/connection_adapters/postgresql/database_statements"
module PostgresReturningPatch
def sql_for_insert(...)
sql, *args = super
if sql.include?(TimeEntry.table_name) && sql.ends_with?(%(RETURNING "id"))
returning_virtual_columns = TimeEntry::columns.select(&:virtual?).map do |column|
quote_column_name(column.name)
end.join(", ")
sql += ", #{returning_virtual_columns}"
end
binding.pry
[sql, *args]
end
end
ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements.module_eval do
prepend PostgresReturningPatch
end
Unfortunately, it's still nil when Rails returns the instance of my model, despite that the SQL ends now with RETURNING "id", "date", "duration".

Timezone issue in Rails

I've never had this before, but somehow when I'm combining a date and a time to a concatenated datetime and store it in the db, I'm off by 2 hours. I just noticed when I started using time_ago_in_words, it's always two hours from now when I just saved something new.
Any idea how to overcome this differnece. I think this is the code that's responsible for the difference:
def create_timestamp
self.date = day.to_datetime + time.seconds_since_midnight.seconds
end
In case anyone is wondering, I'm using a seperate date & time picker on my form, so this the before_save method to create one timestamp for the user input.
Upon request, the SQL for an INSERT at 10:47 CEST, Rails app is in UTC btw:
INSERT INTO "reports" ("account_id", "description", "report_category_id", "day", "time", "user_id", "date", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id" [["account_id", 2], ["description", ""], ["report_category_id", 4], ["day", "2016-07-30"], ["time", "2000-01-01 10:47:00.000000"], ["user_id", 1], ["date", "2016-07-30 10:47:00.000000"], ["created_at", "2016-07-30 08:47:53.828175"], ["updated_at", "2016-07-30 08:47:53.828175"]]

has_many through middle model not creating, but creating duplicate of one model

I have two models join by a middle model
class Integration < ActiveRecord::Base
has_many :integration_records
has_many :records, through: :integration_records
end
class IntegrationRecord < ActiveRecord::Base
belongs_to :integration
belongs_to :record
end
class Record < ActiveRecord::Base
has_many :integration_records
has_many :integrations, through: :integration_records
end
i = Integration.create(whatever)
i.records.create(whatever)
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
i.records
=> [one record]
Record.all.count
=> 2
i.integration_records
=> #<IntegrationRecord id: nil, integration_id: 1, record_id: 2 >
Notice id is nil
IntegrationRecord.all
=> #<ActiveRecord::Relation []>
IntegrationRecord.create
=> #<IntegrationRecord id: nil, integration_id: nil, record_id: nil>
Notice id is nil
Record.create.integrations.create
=> (0.1ms) BEGIN
SQL (8.7ms) INSERT INTO "integrations" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:02 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:02 UTC +00:00]]
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
Not sure why this is happening, in the case of i.records.create(whatever) it should output:
SQL (0.6ms) INSERT INTO "records" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", Sat, 03 May 2014 00:31:06 UTC +00:00], ["updated_at", Sat, 03 May 2014 00:31:06 UTC +00:00]]
(0.4ms) COMMIT
INSERT INTO "integration_records" ("created_at", "data", "integration_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Sat, 03 May 2014 15:57:05 UTC +00:00], ["data", {}], ["integration_id", 5], ["updated_at", Sat, 03 May 2014 15:57:05 UTC +00:00]]
I should note that for some reason, when I create a new app in rails 4.0.4, I still had this problem. But when I change the names of the models and specifically Record model, it works perfectly fine. So when I changed it to Recordrow no problem at all.
As per your current association setup, all you need to do is, just follow these simple instructions step by step
Create an Integration record
## this will create an "integration" record in "integrations" table
integration = Integration.create(whatever)
Create an associated Record record
## this will create an associated "record" entry in "records" table
## PLUS this will also created an associated record in "integration_records" table
integration.records.create(whatever)
View the associated records and associated integration_records
## Display all the "records" entries associated to this particular integration
integration.records
## Display all the "integration_records" entries associated to this particular integration
integration.integration_records
UPDATE
i.records.create(whatever) was creating 2 records, found out that the issue was with the name records of the table. Once changed everything works fine. It looks like records is reserved word.
Also, OP found this link which states records is reserved

Create new user works with ruby 1.9.2 but fails with ruby 1.9.3

I am using Rails 3.2.8 and Devise 2.1.2 (latest) and am baffled as to why I can create a user just fine with Ruby 1.9.2 but not with Ruby 1.9.3, using exactly the same codebase.
Rails console:
User.find_or_create_by_email('example#example.com', :password => 'example', :first_name => 'Super', :last_name => 'Admin', :terms_and_conditions => true)
The output in 1.9.3 shows ROLLBACK (see below), but I just can't seem to figure out why.
Output (partial) when using 1.9.2:
SQL (0.3ms) INSERT INTO "versions" ("created_at", "event", "item_id", "item_type", "object", "whodunnit") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Wed, 10 Oct 2012 13:57:07 UTC +00:00], ["event", "create"], ["item_id", 20], ["item_type", "OrderType"], ["object", nil], ["whodunnit", nil]]
Currency Load (1.1ms) SELECT "currencies".* FROM "currencies" WHERE "currencies"."code" = 'USD' LIMIT 1
SQL (2.4ms) INSERT INTO ..
(continues with the next insert statement)
Output (partial) when using 1.9.3:
SQL (0.3ms) INSERT INTO "versions" ("created_at", "event", "item_id", "item_type", "object", "whodunnit") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Wed, 10 Oct 2012 12:29:49 UTC +00:00], ["event", "create"], ["item_id", 16], ["item_type", "OrderType"], ["object", nil], ["whodunnit", nil]]
Currency Load (0.3ms) SELECT "currencies".* FROM "currencies" WHERE "currencies"."code" = 'USD' LIMIT 1
(0.1ms) ROLLBACK
=> #<User id: 4, first_name: "Super", last_name: "Admin", admin_notes: nil, email: "example#example.com", ...
(stops)
I am thinking this could be an issue with Devise but am not sure.
Would appreciate any help!
Update Oct 19:
I found out that the cause of the problem is an after_create callback in user.rb that creates a "broker" (through the broker model) where it sets commission = 9.95
From the annotation in broker.rb:
# commission :decimal(, )
And more from broker.rb:
belongs_to :user
PRICE_REGEX = /^([1-9]\d{0,20}|0)(\.\d{0,3})?$/
PRICE_ERROR_MESSAGE = "Must be a number. If you want to include decimals, please use comma as decimal separator. Periods (.) = thousand separator cannot be used."
validates :commission, :format => {:with => PRICE_REGEX, :message => PRICE_ERROR_MESSAGE }, :allow_blank => true
As written above, setting broker commission to 9.95 works fine with ruby 1.9.2 but fails with ruby 1.9.3.
When saving it through rails console in 1.9.3, I get an "ActiveRecord::RecordInvalid: Validation failed..." error. No error in 1.9.2.
If I set it to 10 instead of 9.95 it works in both 1.9.2 and 1.9.3 (broker commission is set to 10).
If I set it to 9,95 (comma, not period) the user and broker is created fine but the broker commission is set to 0.
As the validation error message says, periods (.) are not allowed so it makes sense that the validation fails, but then it would have been a bug in ruby 1.9.2 that had it work before I switched to 1.9.3.
Any good explanation would be welcome :-)
After failed save check #user.errors it may have a clue
EDIT
In your Rails console
#user = User.find_or_create_by_email('example#example.com', :password => 'example', :first_name => 'Super', :last_name => 'Admin', :terms_and_conditions => true)
#user.errors
or
#user.errors.full_messages

CarrierWave. Impossible to save the file with STI

I have model Document:
class Document < ActiveRecord::Base
belongs_to :company
validates :name, :presence => true
end
And two classes inherited from Document:
License:
class License < Document
mount_uploader :file, DocumentUploader
end
And Certificate
class Certificate < Document
mount_uploader :file, DocumentUploader
end
And when I try to do current_company.licenses.create(...) or same action for the certificate, always all params are saved, besides file, which always is nil
Also I've tried to mount file inside of Document model... Help me please.
Here is logs:
Started POST "/companies/1/verified" for 127.0.0.1 at Mon Mar 19 09:33:41 +0200 2012
Processing by CompaniesController#verified as HTML
Parameters: {"verified"=>{"certificate"=>{"name"=>"Certificate", "file"=>"test.png"}, "insured"=>"2000000", "suppliers"=>"", "license"=>{"name"=>"License", "file"=>"test.png"}}, "authenticity_token"=>"0hIn41Tjonm/AXZBKM1PE/tjQxJDLqZaojMTHDoZq2k=", "id"=>"1", "utf8"=>"✓", "commit"=>"Update verifications"}
Company Load (0.7ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = 1 LIMIT 1
(0.1ms) BEGIN
SQL (0.8ms) INSERT INTO "documents" ("company_id", "created_at", "file", "name", "type", "updated_at", "verified") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["company_id", 1], ["created_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["file", nil], ["name", "License"], ["type", "License"], ["updated_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["verified", false]]
(0.6ms) COMMIT
(0.1ms) BEGIN
SQL (0.5ms) INSERT INTO "documents" ("company_id", "created_at", "file", "name", "type", "updated_at", "verified") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["company_id", 1], ["created_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["file", nil], ["name", "Certificate"], ["type", "Certificate"], ["updated_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["verified", false]]
(0.4ms) COMMIT
Redirected to http://localhost:3000/company/profile
Completed 302 Found in 18ms (ActiveRecord: 3.3ms)
Thanks.
Are you sure you've set the form that you've uploading the file with to have a multipart payload?
If not the file won't get sent on submission, and rails will only receive the textual form data. Inside the form_helper tag you'll need to add.
:html => {:multipart => true}

Resources