Skip to content

Commit

Permalink
Merge pull request #12 from virolea/improve-key-syncing-with-job
Browse files Browse the repository at this point in the history
Replace autodiscovery queue with autodiscovery job
  • Loading branch information
virolea authored Sep 23, 2024
2 parents ad89565 + 4cc8a12 commit 5d60f36
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 32 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ Saving the new translation **will not immediately reflect in your application**.
![CleanShot 2024-09-19 at 15 15 30](https://github.com/user-attachments/assets/51a9b582-b35e-4df4-a79b-1e0f238715a0)


### Configuring a base controller
### Configuration

#### Configuring a base controller

By default, Rosetta inherits from `ActionController::Base`. If you want to restrict access to the Rosetta interface, you can make Rosetta inherit from your own base controller, by configuring it in the initializer:

Expand All @@ -163,9 +165,21 @@ end

**Note**: The class name needs to be a string.

### I18n Support
#### Setting up the queue for the Autodiscovery Job

Rosetta uses a background job to automatically discover new translations in your codebase. It enqueues jobs in the queue set by default. To configure it to target another queue:

```ruby
# config/initializers/rosetta.rb

Rosetta.configure do |config|
config.queues[:autodiscovery] = "low_priority"
end
```

### I18n Support

As of now, Rosetta does not integrate with the `i18n` gem as a custom backend. It might be done in the future, however it's been decided to build Rosetta independently for now. You still need to use `i18n` for backwards compatibility of your existing translations, localization, as well as the translations of gems that depend on it.
As of now, Rosetta does not integrate with the `i18n` gem as a custom backend. It might be done in the future, however it's been decided to build Rosetta independently for now. You still need to use `i18n` for backwards compatibility of your existing translations, localization, as well as the translations of gems that depend on it.

## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
11 changes: 11 additions & 0 deletions app/jobs/rosetta/autodiscovery_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Rosetta
class AutodiscoveryJob < Rosetta::ApplicationJob
queue_as { Rosetta.config.queues[:autodiscovery] }

discard_on ActiveRecord::RecordNotUnique

def perform(value)
TranslationKey.create!(value: value)
end
end
end
4 changes: 4 additions & 0 deletions app/models/rosetta/translation_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ class TranslationKey < ApplicationRecord
has_many :translations, dependent: :destroy
# Note: Learn about the design decisions behind this: https://github.com/virolea/rosetta/issues/3
has_one :translation_in_current_locale, -> { where(locale_id: Rosetta.locale.id) }, class_name: "Translation", dependent: :destroy

def self.create_later(value)
AutodiscoveryJob.perform_later(value)
end
end
end
2 changes: 2 additions & 0 deletions lib/rosetta/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ module Rosetta
class Configuration
attr_reader :default_locale
attr_accessor :parent_controller_class
attr_accessor :queues

DefaultLocale = Struct.new(:name, :code)

def initialize
set_default_locale(name: "English", code: "en")
@parent_controller_class = "ActionController::Base"
@queues = {}
end

def set_default_locale(name:, code:)
Expand Down
19 changes: 1 addition & 18 deletions lib/rosetta/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ def self.for_locale(locale)
def initialize(locale)
@locale = locale
@cache_expiration_timestamp = @locale.updated_at
@autodiscovery_queue = Queue.new

start_key_autodiscovery!
end

def lookup(key_value)
if translations.has_key?(key_value)
translations[key_value]
else
@autodiscovery_queue << key_value
TranslationKey.create_later(key_value)
# Set the key in the translations store to locate it
# once only.
translations[key_value] = nil
Expand Down Expand Up @@ -57,19 +54,5 @@ def load_translations

Concurrent::Hash.new.merge(loaded_translations)
end

def start_key_autodiscovery!
Thread.new do
Thread.current.name = "Rosetta #{locale.code} store thread"

loop do
key_value = @autodiscovery_queue.pop

unless TranslationKey.exists?(value: key_value)
TranslationKey.create!(value: key_value)
end
end
end
end
end
end
13 changes: 5 additions & 8 deletions test/integration/translations_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require "test_helper"

class TranslationsTest < ActionDispatch::IntegrationTest
include ActiveJob::TestHelper

teardown do
Rosetta::Store.for_locale(rosetta_locales(:french)).reload!
end
Expand All @@ -12,20 +14,15 @@ class TranslationsTest < ActionDispatch::IntegrationTest
assert_select "h3", "bonjour"
end

test "visiting the page loads up the missing keys" do
assert_changes("Rosetta::TranslationKey.count") do
test "visiting the page syncs up the missing keys" do
assert_enqueued_jobs 18 do
get root_path(locale: "fr")
sleep 2 # TODO: Find better way to wait for keys to be created
end
end

test "deploying a new translation" do
locale = rosetta_locales(:french)

# Load up the keys
get root_path(locale: locale.code)
sleep 2 # TODO: Find better way to wait for keys to be created
key = Rosetta::TranslationKey.find_by(value: "Available locales")
key = Rosetta::TranslationKey.create(value: "Available locales")

# Create the translation
patch rosetta.translation_key_translation_path(key), params: { locale_id: locale.id, translation: { value: "Langues disponibles" } }
Expand Down
21 changes: 21 additions & 0 deletions test/jobs/rosetta/autodiscovery_job_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require "test_helper"

module Rosetta
class AutodiscoveryJobTest < ActiveJob::TestCase
test "creates a new key" do
assert_difference "Rosetta::TranslationKey.count", 1 do
Rosetta::AutodiscoveryJob.perform_now("Test creation from job")
end
end

test "ignores duplicate keys" do
TranslationKey.create(value: "duplicate")

perform_enqueued_jobs do
assert_nothing_raised do
Rosetta::AutodiscoveryJob.perform_later("duplicate")
end
end
end
end
end
7 changes: 4 additions & 3 deletions test/lib/rosetta/store_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
require "minitest/mock"

class Rosetta::StoreTest < ActiveSupport::TestCase
include ActiveJob::TestHelper

setup do
@locale = rosetta_locales(:french)
@store = Rosetta::Store.for_locale(@locale)
Expand Down Expand Up @@ -31,10 +33,9 @@ class Rosetta::StoreTest < ActiveSupport::TestCase
assert_nil @store.lookup("goodbye")
end

test "looking up a key that doesn't exist creates a new key and adds the key to the translations" do
assert_difference("Rosetta::TranslationKey.count", 1) do
test "looking up a key that doesn't exist enqueues a new key creation and adds the key to the translations" do
assert_enqueued_with(job: Rosetta::AutodiscoveryJob, args: [ "missing key" ]) do
@store.lookup("missing key")
sleep 1
end

assert @store.translations.has_key?("missing key")
Expand Down

0 comments on commit 5d60f36

Please sign in to comment.