Skip to content

Commit 4b15672

Browse files
authored
Merge pull request #164 from prograils/fix_duplication_detection_while_synchronizing
When synchronizing with remote record duplication was not properly checked
2 parents 58cb518 + 6b64cf6 commit 4b15672

9 files changed

+124
-132
lines changed

app/controllers/lit/api/v1/localizations_controller.rb

+9-17
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,26 @@ module Lit
22
class Api::V1::LocalizationsController < Api::V1::BaseController
33
def index
44
@localizations = fetch_localizations
5-
render json: @localizations.as_json(
6-
root: false,
7-
only: %i[id localization_key_id locale_id],
8-
methods: %i[
9-
value localization_key_str locale_str
10-
localization_key_is_deleted
11-
]
12-
)
5+
render json:
6+
@localizations.as_json(
7+
root: false,
8+
only: %i[id localization_key_id locale_id],
9+
methods: %i[value localization_key_str locale_str localization_key_is_deleted],
10+
)
1311
end
1412

1513
def last_change
1614
@localization = Localization.order(updated_at: :desc).first
17-
render json: @localization.as_json(
18-
root: false, only: [], methods: [:last_change]
19-
)
15+
render json: @localization.as_json(root: false, only: [], methods: [:last_change])
2016
end
2117

2218
private
2319

2420
def fetch_localizations
25-
scope = Localization.includes(
26-
:locale,
27-
:localization_key
28-
)
21+
scope = Localization.includes(:locale, :localization_key)
2922

3023
if params[:after].present?
31-
after_date = Time.parse("#{params[:after]} #{Time.zone.name}")
32-
.in_time_zone
24+
after_date = Time.parse("#{params[:after]} #{Time.zone.name}").in_time_zone
3325
scope.after(after_date).to_a
3426
else
3527
scope.all

app/models/lit/incomming_localization.rb

+13-27
Original file line numberDiff line numberDiff line change
@@ -36,39 +36,27 @@ def accept
3636
def duplicated?(val)
3737
set_localization
3838
return false if localization_has_changed?
39-
translated_value =
40-
localization.read_attribute_before_type_cast('translated_value')
41-
if localization.is_changed? && !translated_value.nil?
42-
translated_value == val
43-
else
44-
localization.read_attribute_before_type_cast('default_value') == val
45-
end
39+
40+
localization.translation == val
4641
end
4742

4843
private
4944

5045
def set_localization
5146
return if locale.blank? || localization_key.blank?
52-
self.localization = localization_key.localizations
53-
.find_by(locale_id: locale_id)
47+
self.localization = localization_key.localizations.find_by(locale_id: locale_id)
5448
end
5549

5650
def localization_has_changed?
57-
localization.blank? ||
58-
localization.is_deleted != localization_key_is_deleted
51+
localization.blank? || localization.is_deleted != localization_key_is_deleted
5952
end
6053

6154
def update_existing_localization_data
62-
localization.update!(
63-
translated_value: translated_value,
64-
is_changed: true
65-
)
55+
localization.update!(translated_value: translated_value, is_changed: true)
6656
end
6757

6858
def update_existing_localization_key_data
69-
localization_key.update!(
70-
is_deleted: localization_key_is_deleted
71-
)
59+
localization_key.update!(is_deleted: localization_key_is_deleted)
7260
end
7361

7462
def assign_new_localization_data
@@ -83,25 +71,23 @@ def assign_new_locale
8371

8472
def assign_new_localization_key
8573
self.localization_key =
86-
Lit::LocalizationKey.where(
87-
localization_key: localization_key_str,
88-
is_deleted: localization_key_is_deleted
89-
).first_or_create!
74+
Lit::LocalizationKey.where(localization_key: localization_key_str, is_deleted: localization_key_is_deleted)
75+
.first_or_create!
9076
end
9177

9278
def assign_new_localization
9379
self.localization =
94-
Lit::Localization.where(localization_key_id: localization_key.id)
95-
.where(locale_id: locale.id)
96-
.first_or_initialize
80+
Lit::Localization
81+
.where(localization_key_id: localization_key.id)
82+
.where(locale_id: locale.id)
83+
.first_or_initialize
9784
localization.translated_value = translated_value
9885
localization.is_changed = true
9986
localization.save!
10087
end
10188

10289
def update_cache
103-
Lit.init.cache.update_cache localization.full_key,
104-
localization.translation
90+
Lit.init.cache.update_cache localization.full_key, localization.translation
10591
end
10692
end
10793
end

app/models/lit/localization.rb

+6-14
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,14 @@ class Localization < Lit::Base
66
## SCOPES
77
scope :changed, -> { where is_changed: true }
88
scope :not_changed, -> { where is_changed: false }
9+
910
# @HACK: dirty, find a way to round date to full second
10-
scope :after, lambda { |dt|
11-
where('updated_at >= ?', dt + 1.second)
12-
.where(is_changed: true)
13-
}
14-
scope :active, lambda {
15-
joins(:localization_key)
16-
.where(Lit::LocalizationKey.table_name => { is_deleted: false })
17-
}
11+
scope :after, lambda { |dt| where('updated_at >= ?', dt + 1.second).where(is_changed: true) }
12+
scope :active, lambda { joins(:localization_key).where(Lit::LocalizationKey.table_name => { is_deleted: false }) }
1813

1914
## ASSOCIATIONS
20-
belongs_to :locale
21-
belongs_to :localization_key, touch: true
15+
belongs_to :locale, required: true
16+
belongs_to :localization_key, touch: true, required: true
2217
has_many :localization_versions, dependent: :destroy
2318
has_many :versions, class_name: '::Lit::LocalizationVersion'
2419

@@ -31,9 +26,7 @@ class Localization < Lit::Base
3126
## ACCESSORS
3227
attr_accessor :full_key_str
3328

34-
unless defined?(::ActionController::StrongParameters)
35-
attr_accessible :translated_value, :locale_id
36-
end
29+
attr_accessible :translated_value, :locale_id unless defined?(::ActionController::StrongParameters)
3730

3831
## BEFORE & AFTER
3932
with_options if: :translated_value_changed? do |o|
@@ -94,6 +87,5 @@ def create_version
9487
translated_value = translated_value_was || default_value
9588
localization_versions.new(translated_value: translated_value)
9689
end
97-
9890
end
9991
end

app/services/remote_interactor_service.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def initialize_uri(path, query_values)
2727

2828
def initialize_request(uri)
2929
req = Net::HTTP::Get.new(uri.request_uri)
30-
req.add_field('Authorization', %(Token token="#{@source.api_key}"))
30+
req.add_field('Authorization', "Token token=\"#{@source.api_key}\"")
3131
req
3232
end
3333

@@ -41,6 +41,7 @@ def initialize_connection(uri)
4141
def perform_request(connection, request)
4242
res = connection.start { |http| http.request(request) }
4343
return res unless res.is_a?(Net::HTTPSuccess)
44+
4445
JSON.parse(res.body)
4546
end
4647
end

app/services/synchronize_source_service.rb

+4-8
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ def execute
1212

1313
def synchronize_localizations
1414
after_date = @source.last_updated_at&.to_s(:db)
15-
result = interactor.send_request Lit::Source::LOCALIZATIONS_PATH,
16-
after: after_date
15+
result = interactor.send_request Lit::Source::LOCALIZATIONS_PATH, after: after_date
1716
return unless result&.is_a?(Array)
1817
result.each { |loc| synchronize_localization loc }
1918
end
@@ -28,19 +27,16 @@ def synchronize_localization(loc)
2827
inc_loc.localization_key = find_localization_key(inc_loc)
2928
inc_loc.translated_value = loc['value']
3029
return if inc_loc.duplicated?(loc['value'])
30+
3131
inc_loc.save!
3232
end
3333

3434
def find_incomming_localization(localization)
35-
Lit::IncommingLocalization.find_or_initialize_by(
36-
incomming_id: localization['id']
37-
)
35+
Lit::IncommingLocalization.find_or_initialize_by(incomming_id: localization['id'])
3836
end
3937

4038
def find_localization_key(inc_loc)
41-
Lit::LocalizationKey.find_by(
42-
localization_key: inc_loc.localization_key_str
43-
)
39+
Lit::LocalizationKey.find_by(localization_key: inc_loc.localization_key_str)
4440
end
4541

4642
def localization_key_deleted?(loc)

lit.gemspec

+10-12
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
33
require 'lit/version'
44

55
Gem::Specification.new do |s|
6-
s.name = 'lit'
7-
s.version = Lit::VERSION
8-
s.authors = ['Maciej Litwiniuk', 'Piotr Boniecki', 'Michał Buszkiewicz',
9-
'Szymon Soppa']
10-
s.email = ['[email protected]']
11-
s.license = 'MIT'
12-
s.homepage = 'https://github.com/prograils/lit'
13-
s.summary = 'Database powered i18n backend with web gui'
14-
s.description = "Translate your apps with pleasure (sort of...) and for free.
6+
s.name = 'lit'
7+
s.version = Lit::VERSION
8+
s.authors = ['Maciej Litwiniuk', 'Piotr Boniecki', 'Michał Buszkiewicz', 'Szymon Soppa']
9+
s.email = ['[email protected]']
10+
s.license = 'MIT'
11+
s.homepage = 'https://github.com/prograils/lit'
12+
s.summary = 'Database powered i18n backend with web gui'
13+
s.description =
14+
"Translate your apps with pleasure (sort of...) and for free.
1515
It's simple i18n web interface, build on top of twitter
1616
bootstrap, that one may find helpful in translating app by
1717
non-technicals. "
1818

19-
s.files = Dir['{app,config,db,lib}/**/*'] + ['MIT-LICENSE', 'Rakefile',
20-
'README.md']
19+
s.files = Dir['{app,config,db,lib}/**/*'] + %w[MIT-LICENSE Rakefile README.md]
2120
s.add_dependency 'rails', '>= 4.2.0'
2221
s.add_dependency 'jquery-rails'
2322
s.add_dependency 'coffee-rails'
@@ -33,4 +32,3 @@ Gem::Specification.new do |s|
3332
s.add_development_dependency 'vcr', '~> 4.0.0'
3433
s.add_development_dependency 'webmock', '~> 3.4.2'
3534
end
36-

test/jobs/lit/synchronize_source_job_test.rb

-36
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require 'test_helper'
2+
3+
# Applicable only in an ActiveJob-enabled environment (Rails 4.2+ or 4.0/4.1
4+
# with additional gem)
5+
if defined?(ActiveJob)
6+
module Lit
7+
class SynchronizeSourceServiceTest < ActiveSupport::TestCase
8+
fixtures :all
9+
10+
def setup
11+
after = 3.hours.ago
12+
after_str = after.strftime('%F %T')
13+
after_param = Rack::Utils.escape(after_str)
14+
@source = Source.first
15+
@source.update_column(:last_updated_at, after)
16+
localizations_addr = "http://testhost.com/lit/api/v1/localizations.json?after=#{after_param}"
17+
last_change_addr = 'http://testhost.com/lit/api/v1/last_change.json'
18+
stub_request(:get, localizations_addr).to_return(
19+
body:
20+
Localization
21+
.all
22+
.as_json(
23+
root: false,
24+
only: %i[id localization_key_id locale_id],
25+
methods: %i[value localization_key_str locale_str localization_key_is_deleted],
26+
)
27+
.to_json,
28+
)
29+
stub_request(:get, last_change_addr).to_return(body: { last_change: after_str }.to_json)
30+
end
31+
32+
test 'synchronization works for different values' do
33+
assert_equal 0, @source.incomming_localizations.count
34+
Localization.all.each do |lc|
35+
lc.translated_value << 'test'
36+
lc.is_changed = true
37+
lc.save
38+
end
39+
SynchronizeSourceService.new(@source).execute
40+
assert_equal 5, @source.incomming_localizations.count
41+
end
42+
43+
test 'synchronization ignores same values' do
44+
assert_equal 0, @source.incomming_localizations.count
45+
SynchronizeSourceService.new(@source).execute
46+
assert_equal 0, @source.incomming_localizations.count
47+
end
48+
end
49+
end
50+
end

0 commit comments

Comments
 (0)