lit 0.3.3 → 0.4.0.pre.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -6
- data/app/assets/javascripts/lit/lit_frontend.js +20 -12
- data/app/assets/stylesheets/lit/application.css +3 -0
- data/app/controllers/lit/api/v1/base_controller.rb +1 -1
- data/app/controllers/lit/api/v1/locales_controller.rb +1 -1
- data/app/controllers/lit/api/v1/localization_keys_controller.rb +12 -4
- data/app/controllers/lit/api/v1/localizations_controller.rb +25 -9
- data/app/controllers/lit/application_controller.rb +11 -8
- data/app/controllers/lit/concerns/request_info_store.rb +1 -0
- data/app/controllers/lit/incomming_localizations_controller.rb +22 -15
- data/app/controllers/lit/locales_controller.rb +1 -1
- data/app/controllers/lit/localization_keys_controller.rb +44 -18
- data/app/controllers/lit/localizations_controller.rb +16 -11
- data/app/controllers/lit/sources_controller.rb +9 -13
- data/app/helpers/lit/frontend_helper.rb +28 -16
- data/app/helpers/lit/localizations_helper.rb +2 -1
- data/app/jobs/lit/synchronize_source_job.rb +1 -1
- data/app/models/lit/incomming_localization.rb +71 -41
- data/app/models/lit/locale.rb +11 -13
- data/app/models/lit/localization.rb +26 -24
- data/app/models/lit/localization_key.rb +46 -55
- data/app/models/lit/source.rb +17 -74
- data/app/queries/localization_key_search_query.rb +80 -0
- data/app/services/remote_interactor_service.rb +45 -0
- data/app/services/synchronize_source_service.rb +63 -0
- data/app/views/kaminari/lit/_gap.html.erb +1 -1
- data/app/views/layouts/lit/_navigation.html.erb +1 -1
- data/app/views/lit/dashboard/index.html.erb +2 -2
- data/app/views/lit/incomming_localizations/index.html.erb +10 -6
- data/app/views/lit/localization_keys/_localization_row.html.erb +1 -1
- data/app/views/lit/localization_keys/_localizations_list.html.erb +84 -0
- data/app/views/lit/localization_keys/_sidebar.html.erb +66 -0
- data/app/views/lit/localization_keys/change_completed.js.erb +2 -0
- data/app/views/lit/localization_keys/index.html.erb +3 -121
- data/app/views/lit/localization_keys/not_translated.html.erb +9 -0
- data/app/views/lit/localization_keys/restore_deleted.js.erb +1 -0
- data/app/views/lit/localization_keys/visited_again.html.erb +9 -0
- data/app/views/lit/localizations/_previous_versions_rows.html.erb +2 -2
- data/app/views/lit/localizations/change_completed.js.erb +2 -0
- data/app/views/lit/localizations/update.js.erb +2 -0
- data/app/views/lit/sources/_form.html.erb +1 -1
- data/app/views/lit/sources/index.html.erb +1 -1
- data/config/routes.rb +5 -0
- data/db/migrate/20181017123839_lit_add_is_deleted_to_localization_keys.rb +8 -0
- data/db/migrate/20181018075955_lit_add_localization_key_is_deleted_to_localization_keys.rb +8 -0
- data/db/migrate/20181030111522_lit_add_is_visited_again_to_localization_keys.rb +8 -0
- data/lib/generators/lit/install/templates/initializer.rb +5 -1
- data/lib/lit.rb +1 -0
- data/lib/lit/adapters/redis_storage.rb +1 -1
- data/lib/lit/cache.rb +32 -54
- data/lib/lit/export.rb +80 -0
- data/lib/lit/i18n_backend.rb +12 -9
- data/lib/lit/import.rb +179 -0
- data/lib/lit/loader.rb +2 -0
- data/lib/lit/version.rb +1 -1
- data/lib/tasks/lit_tasks.rake +78 -13
- metadata +21 -6
@@ -3,11 +3,14 @@ module Lit
|
|
3
3
|
attr_accessor :interpolated_key
|
4
4
|
|
5
5
|
## SCOPES
|
6
|
-
scope :completed,
|
7
|
-
scope :not_completed,
|
8
|
-
scope :starred,
|
9
|
-
scope :ordered,
|
10
|
-
scope :
|
6
|
+
scope :completed, -> { where(is_completed: true) }
|
7
|
+
scope :not_completed, -> { where(is_completed: false) }
|
8
|
+
scope :starred, -> { where(is_starred: true) }
|
9
|
+
scope :ordered, -> { order(localization_key: :asc) }
|
10
|
+
scope :active, -> { where(is_deleted: false) }
|
11
|
+
scope :not_active, -> { where(is_deleted: true) }
|
12
|
+
scope :visited_again, -> { where(is_visited_again: true) }
|
13
|
+
scope :after, lambda { |dt|
|
11
14
|
joins(:localizations)
|
12
15
|
.where('lit_localization_keys.updated_at >= ?', dt)
|
13
16
|
.where('lit_localizations.is_changed = true')
|
@@ -21,11 +24,14 @@ module Lit
|
|
21
24
|
presence: true,
|
22
25
|
uniqueness: { if: :localization_key_changed? }
|
23
26
|
|
27
|
+
## ACCESSORS
|
24
28
|
unless defined?(::ActionController::StrongParameters)
|
25
|
-
## ACCESSIBLE
|
26
29
|
attr_accessible :localization_key
|
27
30
|
end
|
28
31
|
|
32
|
+
## BEFORE AND AFTER
|
33
|
+
after_commit :check_completed, on: :update
|
34
|
+
|
29
35
|
def to_s
|
30
36
|
localization_key
|
31
37
|
end
|
@@ -38,70 +44,55 @@ module Lit
|
|
38
44
|
new_created = true
|
39
45
|
end
|
40
46
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
def mark_completed
|
47
|
-
self.is_completed = localizations.changed.count(:id) == localizations.count
|
48
|
-
end
|
49
|
-
|
50
|
-
def mark_completed!
|
51
|
-
save if mark_completed
|
52
|
-
end
|
53
|
-
|
54
|
-
def mark_all_completed!
|
55
|
-
localizations.update_all(['is_changed=?', true])
|
56
|
-
mark_completed!
|
47
|
+
return unless new_created
|
48
|
+
Lit::LocalizationKey.update_all ['is_completed=?', false],
|
49
|
+
['id=? and is_completed=?', id, false]
|
57
50
|
end
|
58
51
|
|
59
52
|
def self.order_options
|
60
|
-
['localization_key asc', 'localization_key desc', 'created_at asc',
|
53
|
+
['localization_key asc', 'localization_key desc', 'created_at asc',
|
54
|
+
'created_at desc', 'updated_at asc', 'updated_at desc']
|
61
55
|
end
|
62
56
|
|
63
|
-
# it can be overridden in parent application,
|
57
|
+
# it can be overridden in parent application,
|
58
|
+
# for example: {:order => "created_at desc"}
|
64
59
|
def self.default_search_options
|
65
60
|
{}
|
66
61
|
end
|
67
62
|
|
68
63
|
def self.search(options = {})
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
77
|
-
localization_key_col = FakeLocalizationKey.arel_table[:localization_key]
|
78
|
-
default_value_col = FakeLocalization.arel_table[:default_value]
|
79
|
-
translated_value_col = FakeLocalization.arel_table[:translated_value]
|
80
|
-
if options[:key_prefix].present?
|
81
|
-
q = "#{options[:key_prefix]}%"
|
82
|
-
s = s.where(localization_key_col.matches(q))
|
83
|
-
end
|
84
|
-
if options[:key].present?
|
85
|
-
q = "%#{options[:key]}%"
|
86
|
-
q_underscore = "%#{options[:key].parameterize.underscore}%"
|
87
|
-
cond = localization_key_col.matches(q).or(
|
88
|
-
default_value_col.matches(q).or(
|
89
|
-
translated_value_col.matches(q)
|
90
|
-
)
|
91
|
-
).or(localization_key_col.matches(q_underscore))
|
92
|
-
s = s.joins([:localizations]).where(cond)
|
64
|
+
LocalizationKeySearchQuery.new(self, options).perform
|
65
|
+
end
|
66
|
+
|
67
|
+
def change_all_completed
|
68
|
+
self.class.transaction do
|
69
|
+
toggle(:is_completed).save!
|
70
|
+
localizations.update_all is_changed: is_completed
|
93
71
|
end
|
94
|
-
|
95
|
-
|
72
|
+
end
|
73
|
+
|
74
|
+
def soft_destroy
|
75
|
+
ActiveRecord::Base.transaction do
|
76
|
+
update is_deleted: true
|
77
|
+
change_all_completed
|
78
|
+
I18n.backend.available_locales.each do |l|
|
79
|
+
Lit.init.cache.delete_key "#{l}.#{localization_key}"
|
80
|
+
end
|
96
81
|
end
|
97
|
-
s
|
98
82
|
end
|
99
83
|
|
100
|
-
|
101
|
-
|
84
|
+
def restore
|
85
|
+
ActiveRecord::Base.transaction do
|
86
|
+
update is_deleted: false, is_completed: false, is_visited_again: false
|
87
|
+
localizations.update_all is_changed: false
|
88
|
+
end
|
102
89
|
end
|
103
|
-
|
104
|
-
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def check_completed
|
94
|
+
self.is_completed = localizations.changed.count == localizations.count
|
95
|
+
save! if is_completed_changed?
|
105
96
|
end
|
106
97
|
end
|
107
98
|
end
|
data/app/models/lit/source.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
module Lit
|
3
3
|
class Source < ActiveRecord::Base
|
4
|
-
LOCALES_PATH = '/api/v1/locales.json'
|
5
|
-
LOCALIZATION_KEYS_PATH = '/api/v1/localization_keys.json'
|
6
|
-
LOCALIZATIONS_PATH = '/api/v1/localizations.json'
|
7
|
-
LAST_CHANGE_PATH = '/api/v1/last_change.json'
|
4
|
+
LOCALES_PATH = '/api/v1/locales.json'.freeze
|
5
|
+
LOCALIZATION_KEYS_PATH = '/api/v1/localization_keys.json'.freeze
|
6
|
+
LOCALIZATIONS_PATH = '/api/v1/localizations.json'.freeze
|
7
|
+
LAST_CHANGE_PATH = '/api/v1/last_change.json'.freeze
|
8
8
|
|
9
9
|
## ASSOCIATIONS
|
10
10
|
has_many :incomming_localizations
|
@@ -13,8 +13,9 @@ module Lit
|
|
13
13
|
validates :api_key, :identifier, :url,
|
14
14
|
presence: true
|
15
15
|
validates :url,
|
16
|
-
format: { with:
|
16
|
+
format: { with: %r{\Ahttps?://.*/.*[^/]\Z}i }
|
17
17
|
|
18
|
+
## ACCESSORS
|
18
19
|
unless defined?(::ActionController::StrongParameters)
|
19
20
|
attr_accessible :api_key, :identifier, :url
|
20
21
|
end
|
@@ -23,89 +24,31 @@ module Lit
|
|
23
24
|
before_create :set_last_updated_at_upon_creation
|
24
25
|
after_validation :check_if_url_is_valid
|
25
26
|
|
26
|
-
def
|
27
|
-
result =
|
27
|
+
def last_change
|
28
|
+
result = RemoteInteractorService.new(self).send_request(LAST_CHANGE_PATH)
|
28
29
|
result['last_change'] unless result.nil?
|
29
30
|
end
|
30
31
|
|
31
|
-
def synchronize
|
32
|
-
after = last_updated_at.nil? ? nil : last_updated_at.to_s(:db)
|
33
|
-
result = get_from_remote(LOCALIZATIONS_PATH, after: after)
|
34
|
-
unless result.nil?
|
35
|
-
if result.is_a?(Array)
|
36
|
-
result.each do |r|
|
37
|
-
il = IncommingLocalization.new
|
38
|
-
if ::Rails::VERSION::MAJOR < 4
|
39
|
-
il = IncommingLocalization.where(incomming_id: r['id']).first_or_initialize
|
40
|
-
else
|
41
|
-
il = IncommingLocalization.find_or_initialize_by(incomming_id: r['id'])
|
42
|
-
end
|
43
|
-
il.source = self
|
44
|
-
il.locale_str = r['locale_str']
|
45
|
-
il.locale = Locale.where(locale: il.locale_str).first
|
46
|
-
il.localization_key_str = r['localization_key_str']
|
47
|
-
il.localization_key = LocalizationKey.where(localization_key: il.localization_key_str).first
|
48
|
-
unless il.is_duplicate?(r['value'])
|
49
|
-
il.save!
|
50
|
-
IncommingLocalization.where(id: il.id).
|
51
|
-
update_all(translated_value: r['value'])
|
52
|
-
end
|
53
|
-
end
|
54
|
-
last_change = get_last_change
|
55
|
-
last_change = DateTime.parse(last_change) unless last_change.nil?
|
56
|
-
touch_last_updated_at(last_change)
|
57
|
-
update_column(:sync_complete, true)
|
58
|
-
save
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
32
|
def touch_last_updated_at!
|
64
|
-
|
33
|
+
assign_last_updated_at
|
65
34
|
save
|
66
35
|
end
|
67
36
|
|
68
|
-
|
69
|
-
|
70
|
-
def touch_last_updated_at(time = nil)
|
37
|
+
def assign_last_updated_at(time = nil)
|
71
38
|
self.last_updated_at = time || Time.now
|
72
39
|
end
|
73
40
|
|
74
|
-
|
75
|
-
if errors.empty? && (self.new_record? || self.url_changed?)
|
76
|
-
errors.add(:url, 'is not accessible') if get_last_change.nil?
|
77
|
-
end
|
78
|
-
end
|
41
|
+
private
|
79
42
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
query_values.each do |k, v|
|
85
|
-
params = URI.decode_www_form(uri.query || '') << [k, v]
|
86
|
-
uri.query = URI.encode_www_form(params)
|
87
|
-
end
|
88
|
-
req = Net::HTTP::Get.new(uri.request_uri)
|
89
|
-
req.add_field('Authorization', %(Token token="#{api_key}"))
|
90
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
91
|
-
http.use_ssl = (uri.port == 443)
|
92
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
93
|
-
res = http.start do |http_started|
|
94
|
-
http_started.request(req)
|
95
|
-
end
|
96
|
-
if res.is_a?(Net::HTTPSuccess)
|
97
|
-
result = JSON.parse(res.body)
|
98
|
-
end
|
99
|
-
rescue => e
|
100
|
-
::Rails.logger.error { "Lit remote error: #{e}" } if defined?(Rails)
|
101
|
-
end
|
102
|
-
result
|
43
|
+
def check_if_url_is_valid
|
44
|
+
return if errors.present? || !(new_record? || url_changed?) ||
|
45
|
+
last_change.present?
|
46
|
+
errors.add :url, 'is not accessible'
|
103
47
|
end
|
104
48
|
|
105
49
|
def set_last_updated_at_upon_creation
|
106
|
-
if last_updated_at.blank?
|
107
|
-
|
108
|
-
end
|
50
|
+
return if last_updated_at.blank? && !Lit.set_last_updated_at_upon_creation
|
51
|
+
assign_last_updated_at
|
109
52
|
end
|
110
53
|
end
|
111
54
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class LocalizationKeySearchQuery
|
2
|
+
def initialize(scope, params = {})
|
3
|
+
@scope = scope
|
4
|
+
@params = params.to_h
|
5
|
+
.reverse_merge(@scope.default_search_options)
|
6
|
+
.with_indifferent_access
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform
|
10
|
+
order_data
|
11
|
+
search_key_prefix
|
12
|
+
search_key
|
13
|
+
search_completed
|
14
|
+
@scope
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def order_data
|
20
|
+
if @params[:order] && order_options.include?(@params[:order])
|
21
|
+
column, order = @params[:order].split(' ')
|
22
|
+
@scope = @scope.order(
|
23
|
+
localization_key_arel[column.to_sym].send(order.to_sym)
|
24
|
+
)
|
25
|
+
else
|
26
|
+
@scope = @scope.ordered
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def search_key_prefix
|
31
|
+
return if @params[:key_prefix].blank?
|
32
|
+
q = Arel::Nodes.build_quoted("#{@params[:key_prefix]}%")
|
33
|
+
@scope = @scope.where(localization_key_col.matches(q))
|
34
|
+
end
|
35
|
+
|
36
|
+
def search_key
|
37
|
+
return if @params[:key].blank?
|
38
|
+
q = Arel::Nodes.build_quoted("%#{@params[:key]}%")
|
39
|
+
q_underscore = Arel::Nodes.build_quoted("%#{@params[:key].parameterize.underscore}%")
|
40
|
+
cond = search_key_conditions(q, q_underscore)
|
41
|
+
@scope = @scope.joins([:localizations]).where(cond)
|
42
|
+
end
|
43
|
+
|
44
|
+
def search_key_conditions(query, q_underscore)
|
45
|
+
localization_key_col.matches(query).or(
|
46
|
+
default_value_col.matches(query).or(
|
47
|
+
translated_value_col.matches(query)
|
48
|
+
)
|
49
|
+
).or(localization_key_col.matches(q_underscore))
|
50
|
+
end
|
51
|
+
|
52
|
+
def search_completed
|
53
|
+
return if @params[:include_completed].to_i != 1
|
54
|
+
@scope = @scope.not_completed
|
55
|
+
end
|
56
|
+
|
57
|
+
def localization_key_col
|
58
|
+
localization_key_arel[:localization_key]
|
59
|
+
end
|
60
|
+
|
61
|
+
def default_value_col
|
62
|
+
localization_arel[:default_value]
|
63
|
+
end
|
64
|
+
|
65
|
+
def translated_value_col
|
66
|
+
localization_arel[:translated_value]
|
67
|
+
end
|
68
|
+
|
69
|
+
def localization_key_arel
|
70
|
+
Arel::Table.new 'lit_localization_keys'
|
71
|
+
end
|
72
|
+
|
73
|
+
def localization_arel
|
74
|
+
Arel::Table.new 'lit_localizations'
|
75
|
+
end
|
76
|
+
|
77
|
+
def order_options
|
78
|
+
Lit::LocalizationKey.order_options
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class RemoteInteractorService
|
2
|
+
def initialize(source)
|
3
|
+
@source = source
|
4
|
+
end
|
5
|
+
|
6
|
+
def send_request(path, query_values = {})
|
7
|
+
uri = initialize_uri(path, query_values)
|
8
|
+
req = initialize_request(uri)
|
9
|
+
connection = initialize_connection(uri)
|
10
|
+
perform_request(connection, req)
|
11
|
+
rescue => e
|
12
|
+
return unless defined?(Rails)
|
13
|
+
::Rails.logger.error { "Lit remote error: #{e}" }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def initialize_uri(path, query_values)
|
19
|
+
uri = URI(@source.url + path)
|
20
|
+
query_values.each do |k, v|
|
21
|
+
params = URI.decode_www_form(uri.query || '') << [k, v]
|
22
|
+
uri.query = URI.encode_www_form(params)
|
23
|
+
end
|
24
|
+
uri
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_request(uri)
|
28
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
29
|
+
req.add_field('Authorization', %(Token token="#{@source.api_key}"))
|
30
|
+
req
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_connection(uri)
|
34
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
35
|
+
http.use_ssl = (uri.port == 443)
|
36
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
37
|
+
http
|
38
|
+
end
|
39
|
+
|
40
|
+
def perform_request(connection, request)
|
41
|
+
res = connection.start { |http| http.request(request) }
|
42
|
+
return res unless res.is_a?(Net::HTTPSuccess)
|
43
|
+
JSON.parse(res.body)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class SynchronizeSourceService
|
2
|
+
def initialize(source)
|
3
|
+
@source = source
|
4
|
+
end
|
5
|
+
|
6
|
+
def execute
|
7
|
+
synchronize_localizations
|
8
|
+
update_timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def synchronize_localizations
|
14
|
+
after_date = @source.last_updated_at&.to_s(:db)
|
15
|
+
result = interactor.send_request Lit::Source::LOCALIZATIONS_PATH,
|
16
|
+
after: after_date
|
17
|
+
return unless result&.is_a?(Array)
|
18
|
+
result.each { |loc| synchronize_localization loc }
|
19
|
+
end
|
20
|
+
|
21
|
+
def synchronize_localization(loc)
|
22
|
+
inc_loc = find_incomming_localization(loc)
|
23
|
+
inc_loc.source = @source
|
24
|
+
inc_loc.locale_str = loc['locale_str']
|
25
|
+
inc_loc.locale = Lit::Locale.find_by(locale: loc['locale_str'])
|
26
|
+
inc_loc.localization_key_str = loc['localization_key_str']
|
27
|
+
inc_loc.localization_key_is_deleted = localization_key_deleted?(loc)
|
28
|
+
inc_loc.localization_key = find_localization_key(inc_loc)
|
29
|
+
inc_loc.translated_value = loc['value']
|
30
|
+
return if inc_loc.duplicated?(loc['value'])
|
31
|
+
inc_loc.save!
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_incomming_localization(localization)
|
35
|
+
Lit::IncommingLocalization.find_or_initialize_by(
|
36
|
+
incomming_id: localization['id']
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_localization_key(inc_loc)
|
41
|
+
Lit::LocalizationKey.find_by(
|
42
|
+
localization_key: inc_loc.localization_key_str
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def localization_key_deleted?(loc)
|
47
|
+
loc['localization_key_is_deleted'] || false
|
48
|
+
end
|
49
|
+
|
50
|
+
def update_timestamps
|
51
|
+
@source.assign_last_updated_at(fetch_last_change)
|
52
|
+
@source.sync_complete = true
|
53
|
+
@source.save!
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch_last_change
|
57
|
+
interactor.send_request(Lit::Source::LAST_CHANGE_PATH)['last_change']
|
58
|
+
end
|
59
|
+
|
60
|
+
def interactor
|
61
|
+
RemoteInteractorService.new(@source)
|
62
|
+
end
|
63
|
+
end
|