From 03014db5ecedb0d4d85a4ea156794c1a37fb5b90 Mon Sep 17 00:00:00 2001 From: Martin Magnusek Date: Sat, 14 Dec 2019 20:16:09 +0100 Subject: [PATCH] Serve translations from TranslationCache --- Gemfile | 1 + Gemfile.lock | 16 ++ Procfile | 1 + .../api/v1/translations_controller.rb | 34 +-- app/jobs/application_job.rb | 3 + app/jobs/scheduled/cache_translations.rb | 7 + app/models/project.rb | 15 ++ app/models/translation_cache.rb | 19 +- config/environments/production.rb | 2 +- config/initializers/sidekiq.rb | 5 + config/routes.rb | 6 + config/schedule.yml | 4 + db/migrate/20150227171322_create_locales.rb | 2 +- db/migrate/20150227171335_create_keys.rb | 2 +- .../20150227172303_create_translations.rb | 2 +- db/migrate/20150227202414_create_locations.rb | 2 +- db/migrate/20150227202429_create_images.rb | 2 +- .../20150227210934_add_array_to_keys.rb | 2 +- .../20150302094322_devise_create_users.rb | 2 +- .../20150303060757_add_api_key_to_users.rb | 2 +- ...303145712_add_float_and_integer_to_keys.rb | 2 +- .../20150303172656_create_highlights.rb | 2 +- db/migrate/20150309091603_create_releases.rb | 2 +- ...150624115221_add_edited_to_translations.rb | 2 +- ...239_add_locale_to_highlights_and_images.rb | 2 +- .../20150709133711_add_indexes_to_tables.rb | 2 +- db/migrate/20150723093015_add_role_to_user.rb | 2 +- ...723093349_add_available_locales_to_user.rb | 2 +- ...20151023072435_remove_windows_new_lines.rb | 2 +- ...20151130092008_create_translation_cache.rb | 2 +- db/migrate/20160912122000_create_projects.rb | 2 +- .../20160912122302_create_default_project.rb | 2 +- .../20160912131046_add_projects_users.rb | 2 +- ...0160912131153_setup_default_connections.rb | 2 +- ...23559_add_validation_for_key_in_project.rb | 2 +- .../20161024155634_create_restricted_ips.rb | 2 +- ...61111083528_add_screenshots_to_projects.rb | 2 +- ...4163720_add_assocition_project_to_cache.rb | 15 ++ db/schema.rb | 196 +++++++++--------- .../api/v1/translations_controller_spec.rb | 12 +- spec/models/project_spec.rb | 29 +++ spec/models/translation_cache_spec.rb | 53 +---- .../api/v1/translations_controller_spec.rb | 23 +- 43 files changed, 244 insertions(+), 247 deletions(-) create mode 100644 app/jobs/application_job.rb create mode 100644 app/jobs/scheduled/cache_translations.rb create mode 100644 config/initializers/sidekiq.rb create mode 100644 config/schedule.yml create mode 100644 db/migrate/20191214163720_add_assocition_project_to_cache.rb diff --git a/Gemfile b/Gemfile index 5076926..d689c9a 100644 --- a/Gemfile +++ b/Gemfile @@ -26,6 +26,7 @@ gem 'redcarpet' gem 'responders' gem 'rest-client' gem 'sass-rails' +gem 'sidekiq-cron' gem 'simple_form' gem 'slim-rails' gem 'uglifier', '>= 2.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 7035d80..b348f00 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,6 +99,7 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.1.5) + connection_pool (2.2.2) crack (0.4.3) safe_yaml (~> 1.0.0) crass (1.0.4) @@ -123,6 +124,8 @@ GEM railties (>= 3.2, < 6.1) equalizer (0.0.11) erubi (1.8.0) + et-orbi (1.2.2) + tzinfo eventmachine (1.0.9.1) execjs (2.7.0) factory_bot (5.0.2) @@ -135,6 +138,9 @@ GEM font-awesome-rails (4.7.0.5) railties (>= 3.2, < 6.1) formatador (0.2.5) + fugit (1.3.3) + et-orbi (~> 1.1, >= 1.1.8) + raabro (~> 1.1) globalid (0.4.2) activesupport (>= 4.2.0) guard (2.15.0) @@ -245,6 +251,7 @@ GEM nio4r (~> 2.0) pundit (2.1.0) activesupport (>= 3.0.0) + raabro (1.1.6) rack (2.0.7) rack-cache (1.9.0) rack (>= 0.4) @@ -360,6 +367,14 @@ GEM shellany (0.0.1) shoulda-matchers (4.1.2) activesupport (>= 4.2.0) + sidekiq (6.0.3) + connection_pool (>= 2.2.2) + rack (>= 2.0.0) + rack-protection (>= 2.0.0) + redis (>= 4.1.0) + sidekiq-cron (1.1.0) + fugit (~> 1.1) + sidekiq (>= 4.2.1) simple_form (4.1.0) actionpack (>= 5.0) activemodel (>= 5.0) @@ -476,6 +491,7 @@ DEPENDENCIES rspec-rails (>= 3.1) sass-rails shoulda-matchers + sidekiq-cron simple_form simplecov slim-rails diff --git a/Procfile b/Procfile index f46d1df..8105f60 100644 --- a/Procfile +++ b/Procfile @@ -1 +1,2 @@ web: jemalloc.sh bundle exec puma -e ${RACK_ENV:-staging} -C config/puma.rb -p ${PORT:-3000} +worker: jemalloc.sh bundle exec sidekiq -q default -q mailers -c 3 diff --git a/app/controllers/api/v1/translations_controller.rb b/app/controllers/api/v1/translations_controller.rb index 2c37ec7..959b3ec 100644 --- a/app/controllers/api/v1/translations_controller.rb +++ b/app/controllers/api/v1/translations_controller.rb @@ -1,25 +1,17 @@ module API module V1 class TranslationsController < ApiController - def index_head - stale? etag: index_etag + stale? etag: current_project.translation_cache head :ok end def index - return unless stale? etag: index_etag - cache_key = "#{current_project.id}-#{params[:format]}" - - if translation_cache = TranslationCache.find_cache(kind: cache_key, etag: index_etag) - response.headers['CustomCache'] = index_etag.to_json - render status: 200, plain: translation_cache.cache - else - @output = Translation.dump_hash current_project.translations.include_dependencies - - TranslationCache.cache(kind: cache_key, etag: index_etag, cache: dump_cache(@output)) - - respond_with @output + if stale? etag: current_project.translation_cache + respond_to do |format| + format.yaml { render status: 200, plain: current_project.translation_cache.cache_yaml } + format.json { render status: 200, plain: current_project.translation_cache.cache_json } + end end end @@ -79,20 +71,6 @@ def create private - def dump_cache(output) - case params[:format] - when 'json' then output.to_json - when 'yaml' then YAML.dump(output).html_safe - else output.to_json - end - end - - def index_etag - translation = current_project.translations.unscope(:order).order(:updated_at).last - updated_at = translation ? translation.updated_at : '' - [updated_at] - end - def translation_params(data) text = data[:text] { text: text.is_a?(Array) ? YAML.dump(text).gsub("---\n", '') : text } diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 0000000..9b7f20f --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,3 @@ +class ApplicationJob < ActiveJob::Base + queue_as :default +end diff --git a/app/jobs/scheduled/cache_translations.rb b/app/jobs/scheduled/cache_translations.rb new file mode 100644 index 0000000..eeb9554 --- /dev/null +++ b/app/jobs/scheduled/cache_translations.rb @@ -0,0 +1,7 @@ +module Scheduled + class CacheTranslations < ApplicationJob + def perform + Project.find_each(&:cache_translations!) + end + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 14f9d5e..f6c1baa 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -8,12 +8,27 @@ class Project < ApplicationRecord has_many :images, through: :locations has_one :default_locale, class_name: 'Locale' + has_one :translation_cache has_and_belongs_to_many :users before_create :ensure_api_token validates :api_token, uniqueness: true validates :name, length: { minimum: 3 }, uniqueness: true + def cache_translations! + cache = translation_cache || build_translation_cache + last_updated_at = translations.maximum(:updated_at) + + return if last_updated_at && cache.persisted? && + cache.updated_at > last_updated_at + + output = Translation.dump_hash(translations.include_dependencies) + cache.update( + cache_yaml: YAML.dump(output).html_safe, + cache_json: output.to_json, + ) + end + def to_s name end diff --git a/app/models/translation_cache.rb b/app/models/translation_cache.rb index 0f53059..3057644 100644 --- a/app/models/translation_cache.rb +++ b/app/models/translation_cache.rb @@ -1,20 +1,3 @@ -require 'digest' - class TranslationCache < ApplicationRecord - - def self.find_cache(kind:, etag:) - find_by(kind: kind, etag: build_etag(etag)) - end - - def self.cache(kind:, etag:, cache:) - if cached = find_by(kind: kind) - cached.update(etag: build_etag(etag), cache: cache) - else - create(kind: kind, etag: build_etag(etag), cache: cache) - end - end - - def self.build_etag(etag) - Digest::MD5.hexdigest(etag.to_json) - end + belongs_to :project end diff --git a/config/environments/production.rb b/config/environments/production.rb index a8279bc..6b2448a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -69,7 +69,7 @@ # config.action_controller.asset_host = 'http://assets.example.com' # Use a real queuing backend for Active Job (and separate queues per environment) - # config.active_job.queue_adapter = :resque + config.active_job.queue_adapter = :sidekiq # config.active_job.queue_name_prefix = "translation_server_#{Rails.env}" config.action_mailer.perform_caching = false diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb new file mode 100644 index 0000000..2d07744 --- /dev/null +++ b/config/initializers/sidekiq.rb @@ -0,0 +1,5 @@ +schedule_file = "config/schedule.yml" + +if File.exist?(schedule_file) && Sidekiq.server? + Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) +end diff --git a/config/routes.rb b/config/routes.rb index 7352325..d7f1df1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,10 @@ +require 'sidekiq/web' +require 'sidekiq/cron/web' + Rails.application.routes.draw do + authenticate :user, lambda { |user| user.admin? } do + mount Sidekiq::Web => '/sidekiq' + end resources :projects do resources :releases, except: [:edit, :update], shallow: true diff --git a/config/schedule.yml b/config/schedule.yml new file mode 100644 index 0000000..d28a617 --- /dev/null +++ b/config/schedule.yml @@ -0,0 +1,4 @@ +cache_translations: + active_job: true + cron: '* * * * *' + class: 'Scheduled::CacheTranslations' diff --git a/db/migrate/20150227171322_create_locales.rb b/db/migrate/20150227171322_create_locales.rb index cd68739..34a58cc 100644 --- a/db/migrate/20150227171322_create_locales.rb +++ b/db/migrate/20150227171322_create_locales.rb @@ -1,4 +1,4 @@ -class CreateLocales < ActiveRecord::Migration +class CreateLocales < ActiveRecord::Migration[4.2] def change create_table :locales do |t| t.string :code diff --git a/db/migrate/20150227171335_create_keys.rb b/db/migrate/20150227171335_create_keys.rb index 8bdc36b..46fffa5 100644 --- a/db/migrate/20150227171335_create_keys.rb +++ b/db/migrate/20150227171335_create_keys.rb @@ -1,4 +1,4 @@ -class CreateKeys < ActiveRecord::Migration +class CreateKeys < ActiveRecord::Migration[4.2] def change create_table :keys do |t| t.string :key diff --git a/db/migrate/20150227172303_create_translations.rb b/db/migrate/20150227172303_create_translations.rb index a205a1f..dedc0e8 100644 --- a/db/migrate/20150227172303_create_translations.rb +++ b/db/migrate/20150227172303_create_translations.rb @@ -1,4 +1,4 @@ -class CreateTranslations < ActiveRecord::Migration +class CreateTranslations < ActiveRecord::Migration[4.2] def change create_table :translations do |t| t.belongs_to :key, index: true diff --git a/db/migrate/20150227202414_create_locations.rb b/db/migrate/20150227202414_create_locations.rb index a429cbc..4ae35f7 100644 --- a/db/migrate/20150227202414_create_locations.rb +++ b/db/migrate/20150227202414_create_locations.rb @@ -1,4 +1,4 @@ -class CreateLocations < ActiveRecord::Migration +class CreateLocations < ActiveRecord::Migration[4.2] def change create_table :locations do |t| t.string :path diff --git a/db/migrate/20150227202429_create_images.rb b/db/migrate/20150227202429_create_images.rb index a921c46..915f135 100644 --- a/db/migrate/20150227202429_create_images.rb +++ b/db/migrate/20150227202429_create_images.rb @@ -1,4 +1,4 @@ -class CreateImages < ActiveRecord::Migration +class CreateImages < ActiveRecord::Migration[4.2] def change create_table :images do |t| t.belongs_to :location, index: true diff --git a/db/migrate/20150227210934_add_array_to_keys.rb b/db/migrate/20150227210934_add_array_to_keys.rb index de73f03..0e3e808 100644 --- a/db/migrate/20150227210934_add_array_to_keys.rb +++ b/db/migrate/20150227210934_add_array_to_keys.rb @@ -1,4 +1,4 @@ -class AddArrayToKeys < ActiveRecord::Migration +class AddArrayToKeys < ActiveRecord::Migration[4.2] def change change_table :keys do |t| t.boolean :array, default: false diff --git a/db/migrate/20150302094322_devise_create_users.rb b/db/migrate/20150302094322_devise_create_users.rb index 1b9d546..6ac54a7 100644 --- a/db/migrate/20150302094322_devise_create_users.rb +++ b/db/migrate/20150302094322_devise_create_users.rb @@ -1,4 +1,4 @@ -class DeviseCreateUsers < ActiveRecord::Migration +class DeviseCreateUsers < ActiveRecord::Migration[4.2] def change create_table(:users) do |t| ## Database authenticatable diff --git a/db/migrate/20150303060757_add_api_key_to_users.rb b/db/migrate/20150303060757_add_api_key_to_users.rb index af85a69..72c8ae8 100644 --- a/db/migrate/20150303060757_add_api_key_to_users.rb +++ b/db/migrate/20150303060757_add_api_key_to_users.rb @@ -1,4 +1,4 @@ -class AddAPIKeyToUsers < ActiveRecord::Migration +class AddAPIKeyToUsers < ActiveRecord::Migration[4.2] def change add_column :users, :api_key, :string add_index :users, :api_key diff --git a/db/migrate/20150303145712_add_float_and_integer_to_keys.rb b/db/migrate/20150303145712_add_float_and_integer_to_keys.rb index b3b3cd0..72a0fa6 100644 --- a/db/migrate/20150303145712_add_float_and_integer_to_keys.rb +++ b/db/migrate/20150303145712_add_float_and_integer_to_keys.rb @@ -1,4 +1,4 @@ -class AddFloatAndIntegerToKeys < ActiveRecord::Migration +class AddFloatAndIntegerToKeys < ActiveRecord::Migration[4.2] def change add_column :keys, :data_type, :string, default: 'string' remove_column :keys, :array diff --git a/db/migrate/20150303172656_create_highlights.rb b/db/migrate/20150303172656_create_highlights.rb index 40444fc..72cc018 100644 --- a/db/migrate/20150303172656_create_highlights.rb +++ b/db/migrate/20150303172656_create_highlights.rb @@ -1,4 +1,4 @@ -class CreateHighlights < ActiveRecord::Migration +class CreateHighlights < ActiveRecord::Migration[4.2] def change create_table :highlights do |t| t.belongs_to :image, index: true diff --git a/db/migrate/20150309091603_create_releases.rb b/db/migrate/20150309091603_create_releases.rb index ceb8902..92b55e0 100644 --- a/db/migrate/20150309091603_create_releases.rb +++ b/db/migrate/20150309091603_create_releases.rb @@ -1,4 +1,4 @@ -class CreateReleases < ActiveRecord::Migration +class CreateReleases < ActiveRecord::Migration[4.2] def change create_table :releases do |t| t.belongs_to :locale, index: true diff --git a/db/migrate/20150624115221_add_edited_to_translations.rb b/db/migrate/20150624115221_add_edited_to_translations.rb index bc404bc..d51157f 100644 --- a/db/migrate/20150624115221_add_edited_to_translations.rb +++ b/db/migrate/20150624115221_add_edited_to_translations.rb @@ -1,4 +1,4 @@ -class AddEditedToTranslations < ActiveRecord::Migration +class AddEditedToTranslations < ActiveRecord::Migration[4.2] def change add_column :translations, :edited, :boolean, default: false end diff --git a/db/migrate/20150624151239_add_locale_to_highlights_and_images.rb b/db/migrate/20150624151239_add_locale_to_highlights_and_images.rb index 8316788..3cf5847 100644 --- a/db/migrate/20150624151239_add_locale_to_highlights_and_images.rb +++ b/db/migrate/20150624151239_add_locale_to_highlights_and_images.rb @@ -1,4 +1,4 @@ -class AddLocaleToHighlightsAndImages < ActiveRecord::Migration +class AddLocaleToHighlightsAndImages < ActiveRecord::Migration[4.2] def change change_table :highlights do |t| t.belongs_to :locale, index: true diff --git a/db/migrate/20150709133711_add_indexes_to_tables.rb b/db/migrate/20150709133711_add_indexes_to_tables.rb index 9334c92..dd64c53 100644 --- a/db/migrate/20150709133711_add_indexes_to_tables.rb +++ b/db/migrate/20150709133711_add_indexes_to_tables.rb @@ -1,4 +1,4 @@ -class AddIndexesToTables < ActiveRecord::Migration +class AddIndexesToTables < ActiveRecord::Migration[4.2] def change add_index :locales, :code add_index :locations, :path diff --git a/db/migrate/20150723093015_add_role_to_user.rb b/db/migrate/20150723093015_add_role_to_user.rb index f4aa084..c4a258e 100644 --- a/db/migrate/20150723093015_add_role_to_user.rb +++ b/db/migrate/20150723093015_add_role_to_user.rb @@ -1,4 +1,4 @@ -class AddRoleToUser < ActiveRecord::Migration +class AddRoleToUser < ActiveRecord::Migration[4.2] def change add_column :users, :role, :string diff --git a/db/migrate/20150723093349_add_available_locales_to_user.rb b/db/migrate/20150723093349_add_available_locales_to_user.rb index 2e34cca..bffb501 100644 --- a/db/migrate/20150723093349_add_available_locales_to_user.rb +++ b/db/migrate/20150723093349_add_available_locales_to_user.rb @@ -1,4 +1,4 @@ -class AddAvailableLocalesToUser < ActiveRecord::Migration +class AddAvailableLocalesToUser < ActiveRecord::Migration[4.2] def change add_column :users, :available_locales, :string, array: true end diff --git a/db/migrate/20151023072435_remove_windows_new_lines.rb b/db/migrate/20151023072435_remove_windows_new_lines.rb index a0e3547..786ef9a 100644 --- a/db/migrate/20151023072435_remove_windows_new_lines.rb +++ b/db/migrate/20151023072435_remove_windows_new_lines.rb @@ -1,4 +1,4 @@ -class RemoveWindowsNewLines < ActiveRecord::Migration +class RemoveWindowsNewLines < ActiveRecord::Migration[4.2] def up Translation.where('text like ?', '% %').each do |translation| translation.update text: translation.text.gsub(' ', '') diff --git a/db/migrate/20151130092008_create_translation_cache.rb b/db/migrate/20151130092008_create_translation_cache.rb index 0f58a79..0d5258d 100644 --- a/db/migrate/20151130092008_create_translation_cache.rb +++ b/db/migrate/20151130092008_create_translation_cache.rb @@ -1,4 +1,4 @@ -class CreateTranslationCache < ActiveRecord::Migration +class CreateTranslationCache < ActiveRecord::Migration[4.2] def change create_table :translation_caches do |t| t.string :etag diff --git a/db/migrate/20160912122000_create_projects.rb b/db/migrate/20160912122000_create_projects.rb index 1373be9..5bd1b5d 100644 --- a/db/migrate/20160912122000_create_projects.rb +++ b/db/migrate/20160912122000_create_projects.rb @@ -1,4 +1,4 @@ -class CreateProjects < ActiveRecord::Migration +class CreateProjects < ActiveRecord::Migration[4.2] def change create_table :projects do |t| t.string :name diff --git a/db/migrate/20160912122302_create_default_project.rb b/db/migrate/20160912122302_create_default_project.rb index 97a155b..108a8e9 100644 --- a/db/migrate/20160912122302_create_default_project.rb +++ b/db/migrate/20160912122302_create_default_project.rb @@ -1,4 +1,4 @@ -class CreateDefaultProject < ActiveRecord::Migration +class CreateDefaultProject < ActiveRecord::Migration[4.2] def up unless Project.where(id: 1).first Key.update_all(project_id: 1) diff --git a/db/migrate/20160912131046_add_projects_users.rb b/db/migrate/20160912131046_add_projects_users.rb index 62b078d..1a540ae 100644 --- a/db/migrate/20160912131046_add_projects_users.rb +++ b/db/migrate/20160912131046_add_projects_users.rb @@ -1,4 +1,4 @@ -class AddProjectsUsers < ActiveRecord::Migration +class AddProjectsUsers < ActiveRecord::Migration[4.2] def change create_table :projects_users do |t| t.integer :project_id, index: true diff --git a/db/migrate/20160912131153_setup_default_connections.rb b/db/migrate/20160912131153_setup_default_connections.rb index 081eb06..668a8ae 100644 --- a/db/migrate/20160912131153_setup_default_connections.rb +++ b/db/migrate/20160912131153_setup_default_connections.rb @@ -1,4 +1,4 @@ -class SetupDefaultConnections < ActiveRecord::Migration +class SetupDefaultConnections < ActiveRecord::Migration[4.2] def up users = User.all.to_a Project.all.each do |project| diff --git a/db/migrate/20161020123559_add_validation_for_key_in_project.rb b/db/migrate/20161020123559_add_validation_for_key_in_project.rb index 19fbc43..f05a01c 100644 --- a/db/migrate/20161020123559_add_validation_for_key_in_project.rb +++ b/db/migrate/20161020123559_add_validation_for_key_in_project.rb @@ -1,4 +1,4 @@ -class AddValidationForKeyInProject < ActiveRecord::Migration +class AddValidationForKeyInProject < ActiveRecord::Migration[4.2] def change add_index :keys, [:key, :project_id], unique: true end diff --git a/db/migrate/20161024155634_create_restricted_ips.rb b/db/migrate/20161024155634_create_restricted_ips.rb index 8d4c38f..767d9ce 100644 --- a/db/migrate/20161024155634_create_restricted_ips.rb +++ b/db/migrate/20161024155634_create_restricted_ips.rb @@ -1,4 +1,4 @@ -class CreateRestrictedIps < ActiveRecord::Migration +class CreateRestrictedIps < ActiveRecord::Migration[4.2] def change create_table :restricted_ips do |t| t.inet :ip diff --git a/db/migrate/20161111083528_add_screenshots_to_projects.rb b/db/migrate/20161111083528_add_screenshots_to_projects.rb index 8932ad3..dae094b 100644 --- a/db/migrate/20161111083528_add_screenshots_to_projects.rb +++ b/db/migrate/20161111083528_add_screenshots_to_projects.rb @@ -1,4 +1,4 @@ -class AddScreenshotsToProjects < ActiveRecord::Migration +class AddScreenshotsToProjects < ActiveRecord::Migration[4.2] def change change_table :projects do |t| t.boolean :screenshots, default: false diff --git a/db/migrate/20191214163720_add_assocition_project_to_cache.rb b/db/migrate/20191214163720_add_assocition_project_to_cache.rb new file mode 100644 index 0000000..c411b07 --- /dev/null +++ b/db/migrate/20191214163720_add_assocition_project_to_cache.rb @@ -0,0 +1,15 @@ +class AddAssocitionProjectToCache < ActiveRecord::Migration[5.2] + def change + drop_table :translation_caches + + create_table :translation_caches do |t| + t.belongs_to :project, index: true, foreign_key: true, null: false + t.text :cache_yaml + t.text :cache_json + + t.timestamps + end + + Scheduled::CacheTranslations.new.perform + end +end diff --git a/db/schema.rb b/db/schema.rb index d9d590c..2474deb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -11,157 +10,150 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161111083528) do +ActiveRecord::Schema.define(version: 2019_12_14_163720) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "highlights", force: :cascade do |t| - t.integer "image_id" - t.integer "key_id" - t.integer "x" - t.integer "y" - t.integer "width" - t.integer "height" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "locale_id" - t.integer "location_id" + create_table "highlights", id: :serial, force: :cascade do |t| + t.integer "image_id" + t.integer "key_id" + t.integer "x" + t.integer "y" + t.integer "width" + t.integer "height" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "locale_id" + t.integer "location_id" + t.index ["image_id"], name: "index_highlights_on_image_id" + t.index ["key_id"], name: "index_highlights_on_key_id" + t.index ["locale_id"], name: "index_highlights_on_locale_id" + t.index ["location_id"], name: "index_highlights_on_location_id" end - add_index "highlights", ["image_id"], name: "index_highlights_on_image_id", using: :btree - add_index "highlights", ["key_id"], name: "index_highlights_on_key_id", using: :btree - add_index "highlights", ["locale_id"], name: "index_highlights_on_locale_id", using: :btree - add_index "highlights", ["location_id"], name: "index_highlights_on_location_id", using: :btree - - create_table "images", force: :cascade do |t| - t.integer "location_id" - t.string "name" - t.string "variant" - t.text "image" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + create_table "images", id: :serial, force: :cascade do |t| + t.integer "location_id" + t.string "name" + t.string "variant" + t.text "image" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["location_id"], name: "index_images_on_location_id" + t.index ["name"], name: "index_images_on_name" end - add_index "images", ["location_id"], name: "index_images_on_location_id", using: :btree - add_index "images", ["name"], name: "index_images_on_name", using: :btree - - create_table "keys", force: :cascade do |t| - t.string "key" - t.text "note" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "data_type", default: "string" - t.integer "project_id" + create_table "keys", id: :serial, force: :cascade do |t| + t.string "key" + t.text "note" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "data_type", default: "string" + t.integer "project_id" + t.index ["key", "id"], name: "index_keys_on_key_and_id" + t.index ["key", "project_id"], name: "index_keys_on_key_and_project_id", unique: true + t.index ["key"], name: "index_keys_on_key" end - add_index "keys", ["key", "id"], name: "index_keys_on_key_and_id", using: :btree - add_index "keys", ["key", "project_id"], name: "index_keys_on_key_and_project_id", unique: true, using: :btree - add_index "keys", ["key"], name: "index_keys_on_key", using: :btree - - create_table "locales", force: :cascade do |t| - t.string "code" + create_table "locales", id: :serial, force: :cascade do |t| + t.string "code" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "project_id" + t.integer "project_id" + t.index ["code"], name: "index_locales_on_code" end - add_index "locales", ["code"], name: "index_locales_on_code", using: :btree - - create_table "locations", force: :cascade do |t| - t.string "path" + create_table "locations", id: :serial, force: :cascade do |t| + t.string "path" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "project_id" + t.integer "project_id" + t.index ["path"], name: "index_locations_on_path" end - add_index "locations", ["path"], name: "index_locations_on_path", using: :btree - - create_table "projects", force: :cascade do |t| - t.string "name" - t.integer "default_locale_id" - t.string "api_token" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "screenshots", default: false + create_table "projects", id: :serial, force: :cascade do |t| + t.string "name" + t.integer "default_locale_id" + t.string "api_token" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "screenshots", default: false + t.index ["api_token"], name: "index_projects_on_api_token" + t.index ["default_locale_id"], name: "index_projects_on_default_locale_id" end - add_index "projects", ["api_token"], name: "index_projects_on_api_token", using: :btree - add_index "projects", ["default_locale_id"], name: "index_projects_on_default_locale_id", using: :btree - - create_table "projects_users", force: :cascade do |t| + create_table "projects_users", id: :serial, force: :cascade do |t| t.integer "project_id" t.integer "user_id" + t.index ["project_id"], name: "index_projects_users_on_project_id" + t.index ["user_id"], name: "index_projects_users_on_user_id" end - add_index "projects_users", ["project_id"], name: "index_projects_users_on_project_id", using: :btree - add_index "projects_users", ["user_id"], name: "index_projects_users_on_user_id", using: :btree - - create_table "releases", force: :cascade do |t| - t.integer "locale_id" - t.string "version" - t.text "yaml" + create_table "releases", id: :serial, force: :cascade do |t| + t.integer "locale_id" + t.string "version" + t.text "yaml" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.index ["locale_id"], name: "index_releases_on_locale_id" end - add_index "releases", ["locale_id"], name: "index_releases_on_locale_id", using: :btree - - create_table "restricted_ips", force: :cascade do |t| - t.inet "ip" - t.integer "user_id" + create_table "restricted_ips", id: :serial, force: :cascade do |t| + t.inet "ip" + t.integer "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_restricted_ips_on_user_id" end - add_index "restricted_ips", ["user_id"], name: "index_restricted_ips_on_user_id", using: :btree - create_table "translation_caches", force: :cascade do |t| - t.string "etag" - t.text "cache" - t.string "kind" + t.bigint "project_id", null: false + t.text "cache_yaml" + t.text "cache_json" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["project_id"], name: "index_translation_caches_on_project_id" end - create_table "translations", force: :cascade do |t| - t.integer "key_id" - t.integer "locale_id" - t.text "text" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "edited", default: false + create_table "translations", id: :serial, force: :cascade do |t| + t.integer "key_id" + t.integer "locale_id" + t.text "text" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "edited", default: false + t.index ["key_id", "locale_id"], name: "index_translations_on_key_id_and_locale_id" + t.index ["key_id"], name: "index_translations_on_key_id" + t.index ["locale_id"], name: "index_translations_on_locale_id" end - add_index "translations", ["key_id", "locale_id"], name: "index_translations_on_key_id_and_locale_id", using: :btree - add_index "translations", ["key_id"], name: "index_translations_on_key_id", using: :btree - add_index "translations", ["locale_id"], name: "index_translations_on_locale_id", using: :btree - - create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" + create_table "users", id: :serial, force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.inet "current_sign_in_ip" - t.inet "last_sign_in_ip" + t.inet "current_sign_in_ip" + t.inet "last_sign_in_ip" t.datetime "created_at" t.datetime "updated_at" - t.string "api_key" - t.string "role" - t.string "available_locales", array: true + t.string "api_key" + t.string "role" + t.string "available_locales", array: true + t.index ["api_key"], name: "index_users_on_api_key" + t.index ["email"], name: "index_users_on_email", unique: true + t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end - add_index "users", ["api_key"], name: "index_users_on_api_key", using: :btree - add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree - add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree - add_foreign_key "highlights", "images" add_foreign_key "highlights", "keys" add_foreign_key "images", "locations" add_foreign_key "releases", "locales" add_foreign_key "restricted_ips", "users" + add_foreign_key "translation_caches", "projects" add_foreign_key "translations", "keys" add_foreign_key "translations", "locales" end diff --git a/spec/controllers/api/v1/translations_controller_spec.rb b/spec/controllers/api/v1/translations_controller_spec.rb index b0981dc..848701a 100644 --- a/spec/controllers/api/v1/translations_controller_spec.rb +++ b/spec/controllers/api/v1/translations_controller_spec.rb @@ -21,6 +21,10 @@ end describe 'GET #index' do + before do + project.cache_translations! + end + action do get :index, format: format end @@ -36,10 +40,6 @@ expect(response.content_type).to eq('application/json') end - it 'response headers does not contain CustomCache' do - expect(response.headers).not_to have_key('CustomCache') - end - context 'with cache' do before do get :index, format: format @@ -52,10 +52,6 @@ it 'response content type' do expect(response.content_type).to eq('application/json') end - - it 'response headers does not contain CustomCache' do - expect(response.headers).to have_key('CustomCache') - end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 4ce0b2d..3bccfa7 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3,6 +3,7 @@ RSpec.describe Project, type: :model do it { is_expected.to have_many :locales } it { is_expected.to have_one :default_locale } + it { is_expected.to have_one :translation_cache } it { is_expected.to have_many :keys } it { is_expected.to have_many :locations } it { is_expected.to have_many :translations } @@ -27,4 +28,32 @@ expect(create(:project).api_token).not_to eq(create(:project).api_token) end end + + describe '#cache_translations!' do + subject { create(:project) } + + before do + create :locale, :with_translations, project: subject + end + + it 'should create new cache' do + expect { + subject.cache_translations! + }.to change(TranslationCache, :count).by(1) + end + + it 'should not create another cache' do + subject.cache_translations! + + expect { + subject.cache_translations! + }.not_to change(TranslationCache, :count) + end + + it 'should not generate new cache if nothing changed' do + subject.cache_translations! + + expect(subject.cache_translations!).to eq(nil) + end + end end diff --git a/spec/models/translation_cache_spec.rb b/spec/models/translation_cache_spec.rb index 05c3be3..8ec7897 100644 --- a/spec/models/translation_cache_spec.rb +++ b/spec/models/translation_cache_spec.rb @@ -1,56 +1,5 @@ require 'rails_helper' RSpec.describe TranslationCache, type: :model do - before do - TranslationCache.delete_all - end - - describe '.find_cache' do - let(:result) { TranslationCache.find_cache(kind: kind, etag: etag) } - let(:etag) { [] } - let(:kind) { nil } - - context 'there is no cache' do - it 'returns nil' do - expect(result).to eq(nil) - end - end - - context 'cache has different etag' do - before { TranslationCache.cache(kind: 'json', etag: ['X'], cache: 'XXX') } - - let(:kind) { 'JSON' } - let(:etag) { ['Y'] } - - it 'returns nil ' do - expect(result).to eq(nil) - end - end - - context 'cache has different kind' do - before { TranslationCache.cache(kind: 'json', etag: ['X'], cache: 'XXX') } - - let(:kind) { 'yaml' } - let(:etag) { ['X'] } - - it 'returns nil' do - expect(result).to eq(nil) - end - end - - context 'cached kind and etag' do - before { TranslationCache.cache(kind: 'json', etag: ['X'], cache: 'YYY') } - - let(:kind) { 'json' } - let(:etag) { ['X'] } - - it 'returns cache instance' do - expect(result).to be_kind_of(TranslationCache) - end - - it 'returns cache data' do - expect(result.cache).to eq('YYY') - end - end - end + it { is_expected.to belong_to :project } end diff --git a/spec/request/api/v1/translations_controller_spec.rb b/spec/request/api/v1/translations_controller_spec.rb index d350b27..f4ec5dc 100644 --- a/spec/request/api/v1/translations_controller_spec.rb +++ b/spec/request/api/v1/translations_controller_spec.rb @@ -9,11 +9,20 @@ module V1 let(:key) { create :key, key: 'foo.bar', project: project } let(:array_key) { create :key, key: 'bar', data_type: 'array', project: project } - let(:headers) do { 'HTTP_AUTHORIZATION' => "Token token=#{project.api_token}" } end + let!(:translations) do + create :translation, locale: cs, key: key, text: 'cs translated text' + create :translation, locale: en, key: key, text: 'en translated text' + create :translation, locale: en, key: array_key, text: "- A \n- B" + end + + before do + project.cache_translations! + end + context 'not authorized' do let(:headers) do { 'HTTP_AUTHORIZATION' => "Token token=UNKNOWN_TOKEN" } @@ -32,12 +41,6 @@ module V1 end describe 'GET /api/v1/translations.json' do - let!(:translations) do - create :translation, locale: cs, key: key, text: 'cs translated text' - create :translation, locale: en, key: key, text: 'en translated text' - create :translation, locale: en, key: array_key, text: "- A \n- B" - end - action do get '/api/v1/translations.json', params: {}, headers: headers end @@ -51,12 +54,6 @@ module V1 end describe 'GET /api/v1/translations.yaml' do - let!(:translations) do - create :translation, locale: cs, key: key, text: 'cs translated text' - create :translation, locale: en, key: key, text: 'en translated text' - create :translation, locale: en, key: array_key, text: "- A \n- B" - end - action do get '/api/v1/translations.yaml', params: {}, headers: headers end