rosette-core 1.0.1
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 +7 -0
- data/Gemfile +26 -0
- data/History.txt +3 -0
- data/README.md +94 -0
- data/Rakefile +18 -0
- data/lib/rosette/core.rb +110 -0
- data/lib/rosette/core/branch_utils.rb +152 -0
- data/lib/rosette/core/commands.rb +139 -0
- data/lib/rosette/core/commands/errors.rb +17 -0
- data/lib/rosette/core/commands/git/commit_command.rb +65 -0
- data/lib/rosette/core/commands/git/diff_base_command.rb +301 -0
- data/lib/rosette/core/commands/git/diff_command.rb +188 -0
- data/lib/rosette/core/commands/git/diff_entry.rb +44 -0
- data/lib/rosette/core/commands/git/fetch_command.rb +27 -0
- data/lib/rosette/core/commands/git/repo_snapshot_command.rb +40 -0
- data/lib/rosette/core/commands/git/show_command.rb +70 -0
- data/lib/rosette/core/commands/git/snapshot_command.rb +50 -0
- data/lib/rosette/core/commands/git/status_command.rb +128 -0
- data/lib/rosette/core/commands/git/with_non_merge_ref.rb +48 -0
- data/lib/rosette/core/commands/git/with_ref.rb +92 -0
- data/lib/rosette/core/commands/git/with_refs.rb +92 -0
- data/lib/rosette/core/commands/git/with_repo_name.rb +50 -0
- data/lib/rosette/core/commands/git/with_snapshots.rb +45 -0
- data/lib/rosette/core/commands/queuing/enqueue_commit_command.rb +37 -0
- data/lib/rosette/core/commands/queuing/requeue_commit_command.rb +46 -0
- data/lib/rosette/core/commands/translations/export_command.rb +257 -0
- data/lib/rosette/core/commands/translations/translation_lookup_command.rb +66 -0
- data/lib/rosette/core/commands/translations/with_locale.rb +47 -0
- data/lib/rosette/core/configurator.rb +160 -0
- data/lib/rosette/core/error_reporters/buffered_error_reporter.rb +96 -0
- data/lib/rosette/core/error_reporters/error_reporter.rb +31 -0
- data/lib/rosette/core/error_reporters/nil_error_reporter.rb +25 -0
- data/lib/rosette/core/error_reporters/printing_error_reporter.rb +58 -0
- data/lib/rosette/core/error_reporters/raising_error_reporter.rb +27 -0
- data/lib/rosette/core/errors.rb +93 -0
- data/lib/rosette/core/extractor/commit_log.rb +33 -0
- data/lib/rosette/core/extractor/commit_log_status.rb +57 -0
- data/lib/rosette/core/extractor/commit_processor.rb +109 -0
- data/lib/rosette/core/extractor/extractor.rb +72 -0
- data/lib/rosette/core/extractor/extractor_config.rb +74 -0
- data/lib/rosette/core/extractor/locale.rb +118 -0
- data/lib/rosette/core/extractor/phrase.rb +76 -0
- data/lib/rosette/core/extractor/phrase/phrase_index_policy.rb +108 -0
- data/lib/rosette/core/extractor/phrase/phrase_to_hash.rb +33 -0
- data/lib/rosette/core/extractor/repo_config.rb +339 -0
- data/lib/rosette/core/extractor/serializer_config.rb +55 -0
- data/lib/rosette/core/extractor/static_extractor.rb +44 -0
- data/lib/rosette/core/extractor/translation.rb +44 -0
- data/lib/rosette/core/extractor/translation/translation_to_hash.rb +28 -0
- data/lib/rosette/core/git/diff_finder.rb +131 -0
- data/lib/rosette/core/git/ref.rb +116 -0
- data/lib/rosette/core/git/repo.rb +378 -0
- data/lib/rosette/core/path_matcher_factory.rb +330 -0
- data/lib/rosette/core/resolvers/extractor_id.rb +37 -0
- data/lib/rosette/core/resolvers/integration_id.rb +37 -0
- data/lib/rosette/core/resolvers/preprocessor_id.rb +38 -0
- data/lib/rosette/core/resolvers/resolver.rb +115 -0
- data/lib/rosette/core/resolvers/serializer_id.rb +37 -0
- data/lib/rosette/core/snapshots/cached_head_snapshot_factory.rb +51 -0
- data/lib/rosette/core/snapshots/cached_snapshot_factory.rb +67 -0
- data/lib/rosette/core/snapshots/head_snapshot_factory.rb +58 -0
- data/lib/rosette/core/snapshots/repo_config_path_filter.rb +83 -0
- data/lib/rosette/core/snapshots/snapshot_factory.rb +184 -0
- data/lib/rosette/core/string_utils.rb +23 -0
- data/lib/rosette/core/translation_status.rb +81 -0
- data/lib/rosette/core/validators.rb +18 -0
- data/lib/rosette/core/validators/commit_validator.rb +62 -0
- data/lib/rosette/core/validators/commits_validator.rb +32 -0
- data/lib/rosette/core/validators/encoding_validator.rb +32 -0
- data/lib/rosette/core/validators/locale_validator.rb +37 -0
- data/lib/rosette/core/validators/repo_validator.rb +33 -0
- data/lib/rosette/core/validators/serializer_validator.rb +37 -0
- data/lib/rosette/core/validators/validator.rb +31 -0
- data/lib/rosette/core/version.rb +8 -0
- data/lib/rosette/data_stores.rb +11 -0
- data/lib/rosette/data_stores/errors.rb +26 -0
- data/lib/rosette/data_stores/phrase_status.rb +59 -0
- data/lib/rosette/integrations.rb +12 -0
- data/lib/rosette/integrations/errors.rb +15 -0
- data/lib/rosette/integrations/integratable.rb +58 -0
- data/lib/rosette/integrations/integration.rb +23 -0
- data/lib/rosette/preprocessors.rb +11 -0
- data/lib/rosette/preprocessors/errors.rb +14 -0
- data/lib/rosette/preprocessors/preprocessor.rb +48 -0
- data/lib/rosette/queuing.rb +14 -0
- data/lib/rosette/queuing/commits.rb +19 -0
- data/lib/rosette/queuing/commits/commit_conductor.rb +90 -0
- data/lib/rosette/queuing/commits/commit_job.rb +93 -0
- data/lib/rosette/queuing/commits/commits_queue_configurator.rb +60 -0
- data/lib/rosette/queuing/commits/extract_stage.rb +46 -0
- data/lib/rosette/queuing/commits/fetch_stage.rb +51 -0
- data/lib/rosette/queuing/commits/finalize_stage.rb +76 -0
- data/lib/rosette/queuing/commits/phrase_storage_granularity.rb +20 -0
- data/lib/rosette/queuing/commits/push_stage.rb +91 -0
- data/lib/rosette/queuing/commits/stage.rb +96 -0
- data/lib/rosette/queuing/job.rb +74 -0
- data/lib/rosette/queuing/queue.rb +28 -0
- data/lib/rosette/queuing/queue_configurator.rb +76 -0
- data/lib/rosette/queuing/worker.rb +30 -0
- data/lib/rosette/serializers.rb +10 -0
- data/lib/rosette/serializers/serializer.rb +98 -0
- data/lib/rosette/tms.rb +9 -0
- data/lib/rosette/tms/repository.rb +95 -0
- data/rosette-core.gemspec +24 -0
- data/spec/core/branch_utils_spec.rb +110 -0
- data/spec/core/commands/git/commit_command_spec.rb +60 -0
- data/spec/core/commands/git/diff_command_spec.rb +263 -0
- data/spec/core/commands/git/fetch_command_spec.rb +61 -0
- data/spec/core/commands/git/repo_snapshot_command_spec.rb +72 -0
- data/spec/core/commands/git/show_command_spec.rb +128 -0
- data/spec/core/commands/git/snapshot_command_spec.rb +86 -0
- data/spec/core/commands/git/status_command_spec.rb +154 -0
- data/spec/core/commands/queuing/enqueue_commit_command_spec.rb +34 -0
- data/spec/core/commands/queuing/requeue_commit_command_spec.rb +46 -0
- data/spec/core/commands/translations/export_command_spec.rb +113 -0
- data/spec/core/commands/translations/translation_lookup_command_spec.rb +58 -0
- data/spec/core/configurator_spec.rb +47 -0
- data/spec/core/error_reporters/buffered_error_reporter_spec.rb +61 -0
- data/spec/core/error_reporters/nil_error_reporter_spec.rb +16 -0
- data/spec/core/error_reporters/printing_error_reporter_spec.rb +60 -0
- data/spec/core/extractor/commit_log_status_spec.rb +216 -0
- data/spec/core/extractor/commit_processor_spec.rb +68 -0
- data/spec/core/extractor/extractor_config_spec.rb +47 -0
- data/spec/core/extractor/extractor_spec.rb +26 -0
- data/spec/core/extractor/locale_spec.rb +92 -0
- data/spec/core/extractor/phrase/phrase_index_policy_spec.rb +116 -0
- data/spec/core/extractor/phrase/phrase_to_hash_spec.rb +18 -0
- data/spec/core/extractor/repo_config_spec.rb +147 -0
- data/spec/core/extractor/translation/translation_to_hash_spec.rb +25 -0
- data/spec/core/git/diff_finder_spec.rb +74 -0
- data/spec/core/git/ref_spec.rb +118 -0
- data/spec/core/git/repo_spec.rb +216 -0
- data/spec/core/path_matcher_factory_spec.rb +139 -0
- data/spec/core/resolvers/extractor_id_spec.rb +47 -0
- data/spec/core/resolvers/integration_id_spec.rb +47 -0
- data/spec/core/resolvers/preprocessor_id_spec.rb +47 -0
- data/spec/core/resolvers/serializer_id_spec.rb +47 -0
- data/spec/core/snapshots/snapshot_factory_spec.rb +145 -0
- data/spec/core/string_utils_spec.rb +19 -0
- data/spec/core/translation_status_spec.rb +91 -0
- data/spec/core/validators/commit_validator_spec.rb +40 -0
- data/spec/core/validators/encoding_validator_spec.rb +30 -0
- data/spec/core/validators/locale_validator_spec.rb +31 -0
- data/spec/core/validators/repo_validator_spec.rb +30 -0
- data/spec/core/validators/serializer_validator_spec.rb +31 -0
- data/spec/integrations/integratable_spec.rb +58 -0
- data/spec/queuing/commits/commit_conductor_spec.rb +71 -0
- data/spec/queuing/commits/commit_job_spec.rb +87 -0
- data/spec/queuing/commits/extract_stage_spec.rb +68 -0
- data/spec/queuing/commits/fetch_stage_spec.rb +101 -0
- data/spec/queuing/commits/finalize_stage_spec.rb +88 -0
- data/spec/queuing/commits/push_stage_spec.rb +145 -0
- data/spec/queuing/commits/stage_spec.rb +80 -0
- data/spec/queuing/job_spec.rb +33 -0
- data/spec/queuing/queue_configurator_spec.rb +44 -0
- data/spec/spec_helper.rb +90 -0
- data/spec/test_helpers/fake_commit_stage.rb +17 -0
- metadata +257 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Rosette
|
6
|
+
module Core
|
7
|
+
module Commands
|
8
|
+
|
9
|
+
# Mixin that handles configuration and validation of a set of git refs or
|
10
|
+
# commit ids. Meant to be mixed into the classes in {Rosette::Core::Commands}.
|
11
|
+
# Required to be used in combination with {Rosette::Core::Commands::WithRepoName}.
|
12
|
+
#
|
13
|
+
# @see Rosette::Core::Commands::WithRepoName
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# class MyCommand < Rosette::Core::Commands::Command
|
17
|
+
# include WithRepoName
|
18
|
+
# include WithRefs
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# cmd = MyCommand.new
|
22
|
+
# .set_repo_name('my_repo')
|
23
|
+
# .set_refs(['master'])
|
24
|
+
#
|
25
|
+
# cmd.commit_strs # => ["master"]
|
26
|
+
# cmd.commit_ids # => ["67f0e9a60dfe39430b346086f965e6c94a8ddd24"]
|
27
|
+
# cmd.valid? # => true
|
28
|
+
#
|
29
|
+
# cmd.set_refs(['non_existent_ref'])
|
30
|
+
# cmd.valid? # => false
|
31
|
+
# cmd.messages # => { commit_str: ["Unable to find commit 'non_existent_ref'"] }
|
32
|
+
#
|
33
|
+
# @!attribute [r] commit_str
|
34
|
+
# @return [String] the raw value as set by either {#set_ref} or {#set_commit_id}.
|
35
|
+
module WithRefs
|
36
|
+
attr_reader :commit_strs
|
37
|
+
|
38
|
+
# Set the git refs (i.e. a branch names). Calling this method after {#set_commit_ids}
|
39
|
+
# will overwrite the commit id values. In other words, it's generally a good idea to
|
40
|
+
# only call one of {#set_commit_ids} or {#set_refs} but not both.
|
41
|
+
#
|
42
|
+
# @param [Array<String>] ref_strs The git refs.
|
43
|
+
# @return [self]
|
44
|
+
def set_refs(ref_strs)
|
45
|
+
@commit_strs = ref_strs
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set the git commit ids. Calling this method after {#set_refs} will overwrite the ref values.
|
50
|
+
# In other words, it's generally a good idea to only call one of {#set_commit_ids} or {#set_refs}
|
51
|
+
# but not both.
|
52
|
+
#
|
53
|
+
# @param [Array<String>] commit_ids The commit ids.
|
54
|
+
# @return [self]
|
55
|
+
def set_commit_ids(commit_ids)
|
56
|
+
@commit_strs = commit_ids
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Resolves the git ref or commit id at +index+ and returns the corresponding commit id.
|
61
|
+
# If {#set_refs} was used to set git refs (i.e. branch names), this method looks up
|
62
|
+
# and returns the corresponding commit id. If {#set_commit_ids} was used to set
|
63
|
+
# commit ids, then that commit id is validated and returned.
|
64
|
+
#
|
65
|
+
# @param [Fixnum] index The index of the ref or commit id to return.
|
66
|
+
# @return [String] The commit id set via either {#set_refs} or {#set_commit_ids}.
|
67
|
+
# @raise [Java::OrgEclipseJgitErrors::MissingObjectException, Java::JavaLang::IllegalArgumentException]
|
68
|
+
# If either the commit id doesn't exist or the ref can't be found.
|
69
|
+
def commit_ids
|
70
|
+
@commit_ids ||= @commit_strs.map do |commit_str|
|
71
|
+
REV_COMMIT_MUTEX.synchronize do
|
72
|
+
get_repo(repo_name)
|
73
|
+
.repo.get_rev_commit(commit_str)
|
74
|
+
.getId.name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
REV_COMMIT_MUTEX = Mutex.new
|
82
|
+
|
83
|
+
def self.included(base)
|
84
|
+
if base.respond_to?(:validate)
|
85
|
+
base.validate :commit_strs, type: :commits
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# Mixin that handles configuration and validation of a repository name.
|
8
|
+
# Meant to be mixed into the classes in {Rosette::Core::Commands}.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# class MyCommand < Rosette::Core::Commands::Command
|
12
|
+
# include WithRepoName
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# cmd = MyCommand.new
|
16
|
+
# .set_repo_name('my_repo')
|
17
|
+
#
|
18
|
+
# cmd.repo_name # => "my_repo"
|
19
|
+
# cmd.valid? # => true
|
20
|
+
#
|
21
|
+
# cmd.set_repo_name('non_existent_repo')
|
22
|
+
# cmd.valid? # => false
|
23
|
+
# cmd.messages # => { repo_name: ["Unable to find repo 'non_existent_repo'."] }
|
24
|
+
#
|
25
|
+
# @!attribute [r] repo_name
|
26
|
+
# @return [String] the repository name.
|
27
|
+
module WithRepoName
|
28
|
+
attr_reader :repo_name
|
29
|
+
|
30
|
+
# Set the repository name.
|
31
|
+
#
|
32
|
+
# @param [String] repo_name The repository name.
|
33
|
+
# @return [self]
|
34
|
+
def set_repo_name(repo_name)
|
35
|
+
@repo_name = repo_name
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def self.included(base)
|
42
|
+
if base.respond_to?(:validate)
|
43
|
+
base.validate :repo_name, type: :repo
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# Mixin capable of taking snapshots of git repositories. Meant to be mixed
|
8
|
+
# into the classes in {Rosette::Core::Commands}.
|
9
|
+
#
|
10
|
+
# @see Rosette::Core::SnapshotFactory
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# class MyCommand < Rosette::Core::Commands::Command
|
14
|
+
# include WithSnapshots
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# cmd = MyCommand.new
|
18
|
+
# snap = cmd.take_snapshot(repo_config, commit_id, paths)
|
19
|
+
# snap # => { 'path/to/file.rb' => '67f0e9a60dfe39430b346086f965e6c94a8ddd24', ... }
|
20
|
+
module WithSnapshots
|
21
|
+
# Takes and returns a snapshot hash for the given repo and commit id. Limits
|
22
|
+
# the paths returned via the +paths+ argument. If no paths are passed,
|
23
|
+
# {#take_snapshot} returns a snapshot containing all the paths in the repository.
|
24
|
+
#
|
25
|
+
# @param [Rosette::Core::RepoConfig] repo_config The repository configuration
|
26
|
+
# to take the snapshot from.
|
27
|
+
# @param [String] commit_id The git commit id to take the snapshot of.
|
28
|
+
# @param [Array] paths The list of paths to consider when taking the snapshot.
|
29
|
+
# Only those paths included in this list will appear in the snapshot hash.
|
30
|
+
# @return [Hash] the snapshot hash (path to commit id pairs).
|
31
|
+
def take_snapshot(repo_config, commit_id, paths = [])
|
32
|
+
__snapshot_factory__.take_snapshot(repo_config, commit_id, paths)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def __snapshot_factory__
|
38
|
+
@@__snapshot_factory__ ||=
|
39
|
+
CachedSnapshotFactory.new(configuration.cache)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# Enqueues a commit for processing on Rosette's configured queue.
|
8
|
+
#
|
9
|
+
# @see Rosette::Queuing
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# EnqueueCommitCommand.new(configuration)
|
13
|
+
# .set_repo_name('my_repo')
|
14
|
+
# .set_ref('master')
|
15
|
+
# .execute
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# EnqueueCommitCommand.new(configuration)
|
19
|
+
# .set_repo_name('my_repo')
|
20
|
+
# .set_commit_id('67f0e9a60dfe39430b346086f965e6c94a8ddd24')
|
21
|
+
# .execute
|
22
|
+
class EnqueueCommitCommand < GitCommand
|
23
|
+
include WithRepoName
|
24
|
+
include WithRef
|
25
|
+
|
26
|
+
def execute
|
27
|
+
conductor = Rosette::Queuing::Commits::CommitConductor.new(
|
28
|
+
configuration, repo_name, Rosette.logger
|
29
|
+
)
|
30
|
+
|
31
|
+
conductor.enqueue(commit_id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# Sets the commit's status to NOT_FOUND, then enqueues it for processing
|
8
|
+
# on Rosette's configured queue. In other words, this command will
|
9
|
+
# re-process the commit, which should be an idempotent operation.
|
10
|
+
#
|
11
|
+
# @see Rosette::Queuing
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# RequeueCommitCommand.new(configuration)
|
15
|
+
# .set_repo_name('my_repo')
|
16
|
+
# .set_ref('master')
|
17
|
+
# .execute
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# RequeueCommitCommand.new(configuration)
|
21
|
+
# .set_repo_name('my_repo')
|
22
|
+
# .set_commit_id('67f0e9a60dfe39430b346086f965e6c94a8ddd24')
|
23
|
+
# .execute
|
24
|
+
class RequeueCommitCommand < GitCommand
|
25
|
+
include WithRepoName
|
26
|
+
include WithRef
|
27
|
+
|
28
|
+
def execute
|
29
|
+
commit_log = datastore.lookup_commit_log(repo_name, commit_id)
|
30
|
+
|
31
|
+
datastore.add_or_update_commit_log(
|
32
|
+
commit_log.repo_name, commit_log.commit_id,
|
33
|
+
commit_log.commit_datetime, PhraseStatus::NOT_FOUND,
|
34
|
+
commit_log.phrase_count, commit_log.branch_name
|
35
|
+
)
|
36
|
+
|
37
|
+
EnqueueCommitCommand.new(configuration)
|
38
|
+
.set_repo_name(repo_name)
|
39
|
+
.set_commit_id(commit_id)
|
40
|
+
.execute
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
require 'base64'
|
5
|
+
require 'digest/md5'
|
6
|
+
|
7
|
+
module Rosette
|
8
|
+
module Core
|
9
|
+
module Commands
|
10
|
+
|
11
|
+
# Finds, encodes, and serializes the translations identified by a
|
12
|
+
# snapshot of the given git ref or commit id. In other words, this
|
13
|
+
# command exports the translations for a git branch or commit. This
|
14
|
+
# command also applies any configured pre-processors to the
|
15
|
+
# translations before serializing them. As a better visualization,
|
16
|
+
# here's the pipeline translations go through when exported:
|
17
|
+
#
|
18
|
+
# preprocessed -> serialized/encoded -> base 64 encoded (if
|
19
|
+
# requested) -> returned
|
20
|
+
#
|
21
|
+
# @!attribute [r] locale
|
22
|
+
# @return [String] the locale to export translations for.
|
23
|
+
# @!attribute [r] serializer
|
24
|
+
# @return [String] the serializer to use when exporting the
|
25
|
+
# translations. Must be recognizable as a serializer id, eg.
|
26
|
+
# 'yaml/rails' or 'json/key-value'.
|
27
|
+
# @!attribute [r] base_64_encode
|
28
|
+
# @return [Boolean] whether or not the serialized translations
|
29
|
+
# should be returned encoded in base 64.
|
30
|
+
# @!attribute [r] encoding
|
31
|
+
# @return [String, Encoding] the encoding translations are
|
32
|
+
# expected to be in. This attribute refers to string encoding
|
33
|
+
# and is distinct from base 64 encoding.
|
34
|
+
# @!attribute [r] include_snapshot
|
35
|
+
# @return [Boolean] whether or not the snapshot used to identify
|
36
|
+
# translations is returned alongside the serialized phrases.
|
37
|
+
# @!attribute [r] include_checksum
|
38
|
+
# @return [Boolean] whether or not the checksum of translations
|
39
|
+
# is returned alongside the serialized phrases.
|
40
|
+
# @!attribute [r] paths
|
41
|
+
# @return [Array<String>] the list of paths to export translations
|
42
|
+
# for. Any translations that belong to phrases that did not come
|
43
|
+
# from a path in this list will not be included in the export.
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# cmd = ExportCommand.new(configuration)
|
47
|
+
# .set_repo_name('my_repo')
|
48
|
+
# .set_ref('master')
|
49
|
+
# .set_locale('pt-BR')
|
50
|
+
# .set_serializer('json/key-value')
|
51
|
+
# .set_base_64_encode(true)
|
52
|
+
# .set_encoding(Encoding::UTF_8)
|
53
|
+
# .set_include_snapshot(false)
|
54
|
+
#
|
55
|
+
# cmd.execute
|
56
|
+
# # =>
|
57
|
+
# # {
|
58
|
+
# # payload: "<base 64 encoded string>",
|
59
|
+
# # encoding: "UTF_8"
|
60
|
+
# # translation_count: 105,
|
61
|
+
# # base_64_encoded: true
|
62
|
+
# # locale: "pt-BR"
|
63
|
+
# # }
|
64
|
+
class ExportCommand < GitCommand
|
65
|
+
attr_reader :locale, :serializer, :base_64_encode
|
66
|
+
attr_reader :encoding, :include_snapshot, :include_checksum
|
67
|
+
attr_reader :paths
|
68
|
+
|
69
|
+
include WithRepoName
|
70
|
+
include WithRef
|
71
|
+
include WithLocale
|
72
|
+
|
73
|
+
include WithSnapshots
|
74
|
+
|
75
|
+
validate :serializer, type: :serializer
|
76
|
+
validate :encoding, type: :encoding
|
77
|
+
|
78
|
+
def initialize(*args)
|
79
|
+
super
|
80
|
+
@paths = []
|
81
|
+
@encoding = Encoding::UTF_8
|
82
|
+
@base_64_encode = false
|
83
|
+
@include_snapshot = false
|
84
|
+
@include_checksum = false
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sets the serializer used to export translations. Must be recognizable
|
88
|
+
# as a serializer id, eg. 'yaml/rails' or 'json/key-value'.
|
89
|
+
#
|
90
|
+
# @param [String] serializer The serializer to use.
|
91
|
+
# @return [self]
|
92
|
+
def set_serializer(serializer)
|
93
|
+
@serializer = serializer
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
# Sets whether or not the serialized translations should be returned
|
98
|
+
# encoded in base 64.
|
99
|
+
#
|
100
|
+
# @param [Boolean] should_encode To encode or not encode, that is
|
101
|
+
# the question.
|
102
|
+
# @return [self]
|
103
|
+
def set_base_64_encode(should_encode)
|
104
|
+
@base_64_encode = should_encode
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
# Sets the encoding translations are expected to be in. Not to be
|
109
|
+
# confused with base 64 encoding.
|
110
|
+
#
|
111
|
+
# @param [String, Encoding] encoding The encoding to use. Can be
|
112
|
+
# either a +String+ or a Ruby +Encoding+, eg. +Encoding::UTF_8+.
|
113
|
+
# @return [self]
|
114
|
+
def set_encoding(encoding)
|
115
|
+
@encoding = encoding
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
# Sets whether or not to include the snapshot in the return value.
|
120
|
+
#
|
121
|
+
# @param [Boolean] should_include_snapshot whether or not to
|
122
|
+
# return the snapshot.
|
123
|
+
# @return [self]
|
124
|
+
def set_include_snapshot(should_include_snapshot)
|
125
|
+
@include_snapshot = should_include_snapshot
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
# Sets whether or not to include a checksum of the phrases in the
|
130
|
+
# return value.
|
131
|
+
#
|
132
|
+
# @param [Boolean] should_include_checksum whether or not to include
|
133
|
+
# the checksum.
|
134
|
+
# @return [self]
|
135
|
+
def set_include_checksum(should_include_checksum)
|
136
|
+
@include_checksum = should_include_checksum
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
# A list of files or paths to filter translations by. Only translations
|
141
|
+
# matching these paths will be included in the export payload.
|
142
|
+
def set_paths(paths)
|
143
|
+
@paths = Array(paths)
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
# Perform the export.
|
148
|
+
#
|
149
|
+
# @return [Hash] containing the following attributes:
|
150
|
+
# * +payload+: The serialized +String+ blob of all the translations.
|
151
|
+
# * +encoding+: The encoding of the strings in +payload+.
|
152
|
+
# * +translation_count+: The number of translations in +payload+.
|
153
|
+
# * +base_64_encoded+: A boolean indicating if +payload+ is base
|
154
|
+
# 64 encoded.
|
155
|
+
# * +locale+: The locale the translations in +payload+ are written in.
|
156
|
+
# * +snapshot+: The snapshot used to identify the translations in
|
157
|
+
def execute
|
158
|
+
stream = StringIO.new
|
159
|
+
snapshot = take_snapshot(repo_config, commit_id, paths)
|
160
|
+
translation_count = 0
|
161
|
+
checksum_list = []
|
162
|
+
|
163
|
+
serializer_instance = serializer_config.klass.new(
|
164
|
+
stream, locale_obj, encoding
|
165
|
+
)
|
166
|
+
|
167
|
+
write_translations_for(snapshot, serializer_instance) do |trans|
|
168
|
+
translation_count += 1
|
169
|
+
|
170
|
+
if include_checksum
|
171
|
+
checksum_list << "#{trans.phrase.index_value}#{trans.translation}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
params = {
|
176
|
+
payload: encode(stream.string),
|
177
|
+
encoding: serializer_instance.encoding.to_s,
|
178
|
+
translation_count: translation_count,
|
179
|
+
base_64_encoded: base_64_encode,
|
180
|
+
locale: locale,
|
181
|
+
paths: paths
|
182
|
+
}
|
183
|
+
|
184
|
+
if include_snapshot
|
185
|
+
params.merge!(snapshot: snapshot)
|
186
|
+
end
|
187
|
+
|
188
|
+
if include_checksum
|
189
|
+
params.merge!(checksum: checksum_for(checksum_list))
|
190
|
+
end
|
191
|
+
|
192
|
+
params
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
def write_translations_for(snapshot, serializer_instance)
|
198
|
+
each_translation(snapshot) do |trans|
|
199
|
+
next unless include_trans?(trans)
|
200
|
+
trans = apply_preprocessors(trans, serializer_config)
|
201
|
+
yield trans if block_given?
|
202
|
+
|
203
|
+
serializer_instance.write_key_value(
|
204
|
+
trans.phrase.index_value, trans.translation
|
205
|
+
)
|
206
|
+
end
|
207
|
+
|
208
|
+
serializer_instance.flush
|
209
|
+
end
|
210
|
+
|
211
|
+
def include_trans?(trans)
|
212
|
+
paths.size == 0 || paths.include?(trans.phrase.file)
|
213
|
+
end
|
214
|
+
|
215
|
+
def checksum_for(list)
|
216
|
+
Digest::MD5.hexdigest(list.sort.join)
|
217
|
+
end
|
218
|
+
|
219
|
+
def locale_obj
|
220
|
+
@locale_obj ||= repo_config.get_locale(locale)
|
221
|
+
end
|
222
|
+
|
223
|
+
def apply_preprocessors(translation, serializer_config)
|
224
|
+
serializer_config.preprocessors.inject(translation) do |trans, preprocessor|
|
225
|
+
preprocessor.process(trans)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def encode(string)
|
230
|
+
if base_64_encode
|
231
|
+
Base64.encode64(string)
|
232
|
+
else
|
233
|
+
string
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def serializer_config
|
238
|
+
@serializer_config ||= repo_config.get_serializer_config(serializer)
|
239
|
+
end
|
240
|
+
|
241
|
+
def repo_config
|
242
|
+
@repo_config ||= get_repo(repo_name)
|
243
|
+
end
|
244
|
+
|
245
|
+
def each_translation(snapshot)
|
246
|
+
datastore.phrases_by_commits(repo_name, snapshot) do |phrase|
|
247
|
+
# only yield if a translation exists
|
248
|
+
if text = repo_config.tms.lookup_translation(locale_obj, phrase)
|
249
|
+
yield Translation.new(phrase, locale, text)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|