lit 0.3.3 → 0.4.0.pre.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|