Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion lib/mobility/backends/active_record/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,25 @@ def visit_Mobility_Plugins_Arel_Attribute(object)
end
end

# @!macro backend_writer
def write(locale, value, **options)
super
if @cache_name
cache.values.each do |translation|
self.translations.push(translation) unless self.translations.include?(translation)
end
end
end

# Returns translation for a given locale, or builds one if none is present.
# @param [Boolean] for_read will be set if trying to read a value, in which case blank
# translation records will not be attached to the parent record.
# @param [Symbol] locale
def translation_for(locale, **)
def translation_for(locale, for_read: false, **)
translation = translations.in_locale(locale)
if for_read
return translations.klass.new(locale: locale) if translation.nil?
end
Comment on lines +311 to +313
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will repeatedly create instances on each read, which could be a problem in some hot spots.

I think a better approach would be to replace the build with an array of instances created with new (as you have above) rather than build, so that they are not saved by default.

Then we would need to add a save hook to assign the instances before saving. I believe this is how Globalize does it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can definitely try this out! Hope to get to it in the next few days. Thanks for the feedback!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shioyama question... isn't this basically requiring the use of a cache? Essentially instead of saving the instance variables into a "normal" ActiveRecord collection, we'd put them in an array (I assume accessible via instance variable) and assign them before saving. When I looked, that's basically what we're already doing with the cache, except for the last bit.

Any suggestions about this? Should we only have this work when cache is enabled? Or ignore cache entirely and store it in two spots? Or...?

translation ||= translations.build(locale: locale)
translation
end
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backends/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ module Table
# @!group Backend Accessors
# @!macro backend_reader
def read(locale, **options)
translation_for(locale, **options).send(attribute)
translation_for(locale, for_read: true, **options).send(attribute)
end

# @!macro backend_writer
Expand Down Expand Up @@ -131,12 +131,12 @@ def table_alias(locale)
# Simple hash cache to memoize translations as a hash so they can be
# fetched quickly.
module Cache
def translation_for(locale, **options)
def translation_for(locale, for_read: false, **options)
return super(locale, options) if options.delete(:cache) == false
if cache.has_key?(locale)
cache[locale]
else
cache[locale] = super(locale, **options)
cache[locale] = super(locale, for_read: for_read, **options)
end
end

Expand Down
71 changes: 67 additions & 4 deletions spec/mobility/backends/active_record/table_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
describe "cleaning up blank translations" do
let(:title_backend) { backend_for(article, :title) }

it "builds nil translations when reading but does not save them" do
it "does not build nil translations" do
Mobility.locale = :en
article = Article.new(title: "New Article")
association_name = article.mobility_backends[:title].association_name
Expand All @@ -102,7 +102,70 @@
article.title

aggregate_failures do
expect(article.send(association_name).size).to eq(3)
expect(article.send(association_name).size).to eq(1)
article.save
expect(article.title).to be_nil
expect(article.reload.send(association_name).size).to eq(1)
end
end

it "removes nil translations when saving persisted record" do
Mobility.locale = :en
article = Article.create(title: "New Article")
association_name = article.mobility_backends[:title].association_name

aggregate_failures do
expect(article.send(association_name).size).to eq(1)

Mobility.locale = :ja
article.title = "新規記事"
expect(article.send(association_name).size).to eq(2)

article.save
expect(article.send(association_name).size).to eq(2)

article.title = nil
article.save
article.reload
expect(article.send(association_name).size).to eq(1)
end
end
end
end

describe "translations association with cache" do
plugins :active_record, :reader, :writer, :cache
before { translates Article, :title, :content, backend: :table }

describe "cleaning up blank translations" do
let(:title_backend) { backend_for(article, :title) }

it "saves correctly by writing after reads" do
Mobility.locale = :en
article = Article.new
association_name = article.mobility_backends[:title].association_name

article.title # cached read
article.title = "New Article"

expect(article.send(association_name).size).to eq(1)
article.save
expect(article.send(association_name).size).to eq(1)
end

it "does not build nil translations" do
Mobility.locale = :en
article = Article.new(title: "New Article")
association_name = article.mobility_backends[:title].association_name

Mobility.locale = :ja
article.title

Mobility.locale = :fr
article.title

aggregate_failures do
expect(article.send(association_name).size).to eq(1)
article.save
expect(article.title).to be_nil
expect(article.reload.send(association_name).size).to eq(1)
Expand Down Expand Up @@ -166,10 +229,10 @@
expect(title_backend.read(:de)).to eq(nil)
end

it "builds translation if no translation exists" do
it "does not build a translation if no translation exists" do
expect {
title_backend.read(:de)
}.to change(subject.send(title_backend.association_name), :size).by(1)
}.not_to change(subject.send(title_backend.association_name), :size)
end

describe "reading back written attributes" do
Expand Down
Loading