txgh 1.0.0
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/LICENSE +202 -0
- data/README.md +64 -0
- data/lib/ext/zipline/output_stream.rb +62 -0
- data/lib/txgh.rb +53 -0
- data/lib/txgh/app.rb +135 -0
- data/lib/txgh/category_support.rb +31 -0
- data/lib/txgh/config.rb +11 -0
- data/lib/txgh/config/config_pair.rb +36 -0
- data/lib/txgh/config/key_manager.rb +54 -0
- data/lib/txgh/config/provider_instance.rb +20 -0
- data/lib/txgh/config/provider_support.rb +26 -0
- data/lib/txgh/config/providers.rb +9 -0
- data/lib/txgh/config/providers/file_provider.rb +19 -0
- data/lib/txgh/config/providers/git_provider.rb +58 -0
- data/lib/txgh/config/providers/raw_provider.rb +19 -0
- data/lib/txgh/config/tx_config.rb +77 -0
- data/lib/txgh/config/tx_manager.rb +15 -0
- data/lib/txgh/diff_calculator.rb +90 -0
- data/lib/txgh/empty_resource_contents.rb +43 -0
- data/lib/txgh/errors.rb +9 -0
- data/lib/txgh/github_api.rb +83 -0
- data/lib/txgh/github_repo.rb +88 -0
- data/lib/txgh/github_request_auth.rb +28 -0
- data/lib/txgh/handlers.rb +12 -0
- data/lib/txgh/handlers/download_handler.rb +84 -0
- data/lib/txgh/handlers/github.rb +10 -0
- data/lib/txgh/handlers/github/delete_handler.rb +65 -0
- data/lib/txgh/handlers/github/handler.rb +20 -0
- data/lib/txgh/handlers/github/push_handler.rb +108 -0
- data/lib/txgh/handlers/github/request_handler.rb +106 -0
- data/lib/txgh/handlers/response.rb +17 -0
- data/lib/txgh/handlers/stream_response.rb +39 -0
- data/lib/txgh/handlers/tgz_stream_response.rb +41 -0
- data/lib/txgh/handlers/transifex.rb +8 -0
- data/lib/txgh/handlers/transifex/hook_handler.rb +77 -0
- data/lib/txgh/handlers/transifex/request_handler.rb +78 -0
- data/lib/txgh/handlers/triggers.rb +9 -0
- data/lib/txgh/handlers/triggers/handler.rb +66 -0
- data/lib/txgh/handlers/triggers/pull_handler.rb +29 -0
- data/lib/txgh/handlers/triggers/push_handler.rb +21 -0
- data/lib/txgh/handlers/zip_stream_response.rb +21 -0
- data/lib/txgh/merge_calculator.rb +74 -0
- data/lib/txgh/parse_config.rb +24 -0
- data/lib/txgh/resource_committer.rb +39 -0
- data/lib/txgh/resource_contents.rb +118 -0
- data/lib/txgh/resource_downloader.rb +141 -0
- data/lib/txgh/resource_updater.rb +104 -0
- data/lib/txgh/response_helpers.rb +30 -0
- data/lib/txgh/transifex_api.rb +165 -0
- data/lib/txgh/transifex_project.rb +37 -0
- data/lib/txgh/transifex_request_auth.rb +53 -0
- data/lib/txgh/tx_branch_resource.rb +59 -0
- data/lib/txgh/tx_logger.rb +12 -0
- data/lib/txgh/tx_resource.rb +66 -0
- data/lib/txgh/utils.rb +44 -0
- data/lib/txgh/version.rb +3 -0
- data/spec/app_spec.rb +346 -0
- data/spec/category_support_spec.rb +43 -0
- data/spec/config/config_pair_spec.rb +47 -0
- data/spec/config/key_manager_spec.rb +48 -0
- data/spec/config/provider_instance_spec.rb +30 -0
- data/spec/config/provider_support_spec.rb +55 -0
- data/spec/config/tx_config_spec.rb +49 -0
- data/spec/config/tx_manager_spec.rb +57 -0
- data/spec/diff_calculator_spec.rb +90 -0
- data/spec/github_api_spec.rb +148 -0
- data/spec/github_repo_spec.rb +178 -0
- data/spec/github_request_auth_spec.rb +39 -0
- data/spec/handlers/download_handler_spec.rb +81 -0
- data/spec/handlers/github/delete_handler_spec.rb +71 -0
- data/spec/handlers/github/push_handler_spec.rb +76 -0
- data/spec/handlers/tgz_stream_response_spec.rb +59 -0
- data/spec/handlers/transifex/hook_handler_spec.rb +115 -0
- data/spec/handlers/zip_stream_response_spec.rb +58 -0
- data/spec/helpers/github_payload_builder.rb +141 -0
- data/spec/helpers/integration_setup.rb +47 -0
- data/spec/helpers/nil_logger.rb +10 -0
- data/spec/helpers/standard_txgh_setup.rb +92 -0
- data/spec/helpers/test_provider.rb +12 -0
- data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +536 -0
- data/spec/integration/cassettes/pull.yml +47 -0
- data/spec/integration/cassettes/push.yml +544 -0
- data/spec/integration/cassettes/transifex_hook_endpoint.yml +560 -0
- data/spec/integration/config/tx.config +10 -0
- data/spec/integration/hooks_spec.rb +158 -0
- data/spec/integration/payloads/github_postbody.json +161 -0
- data/spec/integration/payloads/github_postbody_l10n.json +136 -0
- data/spec/integration/payloads/github_postbody_release.json +136 -0
- data/spec/integration/triggers_spec.rb +45 -0
- data/spec/merge_calculator_spec.rb +112 -0
- data/spec/parse_config_spec.rb +52 -0
- data/spec/resource_committer_spec.rb +42 -0
- data/spec/resource_contents_spec.rb +212 -0
- data/spec/resource_downloader_spec.rb +205 -0
- data/spec/resource_updater_spec.rb +147 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/transifex_api_spec.rb +345 -0
- data/spec/transifex_project_spec.rb +45 -0
- data/spec/transifex_request_auth_spec.rb +39 -0
- data/spec/tx_branch_resource_spec.rb +99 -0
- data/spec/tx_resource_spec.rb +47 -0
- data/spec/utils_spec.rb +58 -0
- data/txgh.gemspec +29 -0
- metadata +296 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
module Handlers
|
|
3
|
+
module Triggers
|
|
4
|
+
class Handler
|
|
5
|
+
|
|
6
|
+
# includes response helpers in both the class and the singleton class
|
|
7
|
+
include ResponseHelpers
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def handle_request(request, logger)
|
|
11
|
+
handle_safely do
|
|
12
|
+
config = Txgh::Config::KeyManager.config_from_project(
|
|
13
|
+
request.params.fetch('project_slug')
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
handler_for(config, request, logger).execute
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def handle_safely
|
|
23
|
+
yield
|
|
24
|
+
rescue => e
|
|
25
|
+
respond_with_error(500, "Internal server error: #{e.message}", e)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def handler_for(config, request, logger)
|
|
29
|
+
new(
|
|
30
|
+
project: config.transifex_project,
|
|
31
|
+
repo: config.github_repo,
|
|
32
|
+
branch: request.params.fetch('branch'),
|
|
33
|
+
resource_slug: request.params.fetch('resource_slug'),
|
|
34
|
+
logger: logger
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
attr_reader :project, :repo, :branch, :resource_slug, :logger
|
|
40
|
+
|
|
41
|
+
def initialize(options = {})
|
|
42
|
+
@project = options[:project]
|
|
43
|
+
@repo = options[:repo]
|
|
44
|
+
@branch = Utils.absolute_branch(options[:branch])
|
|
45
|
+
@resource_slug = options[:resource_slug]
|
|
46
|
+
@logger = options[:logger]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def branch_resource
|
|
52
|
+
@branch_resource ||= TxBranchResource.new(resource, branch)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def resource
|
|
56
|
+
@resource ||= tx_config.resource(resource_slug)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def tx_config
|
|
60
|
+
@tx_config ||= Txgh::Config::TxManager.tx_config(project, repo, branch)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
module Handlers
|
|
3
|
+
module Triggers
|
|
4
|
+
class PullHandler < Handler
|
|
5
|
+
|
|
6
|
+
def execute
|
|
7
|
+
languages.each do |language|
|
|
8
|
+
committer.commit_resource(
|
|
9
|
+
branch_resource, branch, language['language_code']
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
respond_with(200, true)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def committer
|
|
19
|
+
@committer ||= Txgh::ResourceCommitter.new(project, repo, logger)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def languages
|
|
23
|
+
@languages ||= project.api.get_languages(project.name)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
module Handlers
|
|
3
|
+
module Triggers
|
|
4
|
+
class PushHandler < Handler
|
|
5
|
+
|
|
6
|
+
def execute
|
|
7
|
+
ref = repo.api.get_ref(repo.name, branch)
|
|
8
|
+
updater.update_resource(branch_resource, ref[:object][:sha])
|
|
9
|
+
respond_with(200, true)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def updater
|
|
15
|
+
@updater ||= Txgh::ResourceUpdater.new(project, repo, logger)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'ext/zipline/output_stream'
|
|
2
|
+
|
|
3
|
+
module Txgh
|
|
4
|
+
module Handlers
|
|
5
|
+
class ZipStreamResponse < StreamResponse
|
|
6
|
+
|
|
7
|
+
def write_to(stream)
|
|
8
|
+
Zipline::OutputStream.open(stream) do |zipfile|
|
|
9
|
+
enum.each do |file_name, contents|
|
|
10
|
+
zipfile.put_next_entry(file_name, contents.bytesize)
|
|
11
|
+
zipfile << contents
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def file_extension
|
|
17
|
+
'.zip'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
class MergeCalculator
|
|
3
|
+
class << self
|
|
4
|
+
def merge(head_contents, diff_point_contents, diff_hash)
|
|
5
|
+
new(head_contents, diff_point_contents, diff_hash).merge
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :head_contents, :diff_point_contents, :diff_hash
|
|
10
|
+
|
|
11
|
+
# Merges are based on diffs. Whatever was added/removed/modified between
|
|
12
|
+
# two resources is represented by the diff, while the resources themselves
|
|
13
|
+
# are what gets merged. This class uses the given diff to apply one
|
|
14
|
+
# resource's phrases over the top of another.
|
|
15
|
+
#
|
|
16
|
+
# head_contents: translated contents in HEAD
|
|
17
|
+
# diff_point_contents: translated contents in diff point, eg. master
|
|
18
|
+
# diff_hash: what was added/removed/modified in the source
|
|
19
|
+
def initialize(head_contents, diff_point_contents, diff_hash)
|
|
20
|
+
@head_contents = head_contents
|
|
21
|
+
@diff_point_contents = diff_point_contents
|
|
22
|
+
@diff_hash = diff_hash
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def merge
|
|
26
|
+
phrase_hash = diff_point_hash.dup
|
|
27
|
+
update_added(phrase_hash)
|
|
28
|
+
update_modified(phrase_hash)
|
|
29
|
+
update_removed(phrase_hash)
|
|
30
|
+
|
|
31
|
+
ResourceContents.from_phrase_list(
|
|
32
|
+
head_contents.tx_resource, phrase_hash.values
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def update_added(phrase_hash)
|
|
39
|
+
diff.fetch(:added, {}).each_pair do |key, phrase|
|
|
40
|
+
if val = head_hash[key]
|
|
41
|
+
phrase_hash[key] = val
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def update_modified(phrase_hash)
|
|
47
|
+
diff.fetch(:modified, {}).each_pair do |key, phrase|
|
|
48
|
+
if val = head_hash[key]
|
|
49
|
+
phrase_hash[key] = val
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def update_removed(phrase_hash)
|
|
55
|
+
diff.fetch(:removed, {}).each_pair do |key, _|
|
|
56
|
+
phrase_hash.delete(key)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def diff
|
|
61
|
+
@diff ||= diff_hash.each_with_object({}) do |(status, phrases), ret|
|
|
62
|
+
ret[status] = Utils.index_on('key', phrases)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def head_hash
|
|
67
|
+
head_contents.to_h
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def diff_point_hash
|
|
71
|
+
diff_point_contents.to_h
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'parseconfig'
|
|
2
|
+
require 'tempfile'
|
|
3
|
+
|
|
4
|
+
module Txgh
|
|
5
|
+
# This class wraps the ParseConfig class from the parseconfig gem and
|
|
6
|
+
# provides a way to load config from a string instead of just a file.
|
|
7
|
+
class ParseConfig < ::ParseConfig
|
|
8
|
+
class << self
|
|
9
|
+
def load(contents)
|
|
10
|
+
tmp = Tempfile.new('parseconfig')
|
|
11
|
+
tmp.write(contents)
|
|
12
|
+
tmp.close
|
|
13
|
+
load_file(tmp.path)
|
|
14
|
+
ensure
|
|
15
|
+
tmp.unlink if tmp
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def load_file(path)
|
|
19
|
+
# use the default file loading logic
|
|
20
|
+
new(path)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
class ResourceCommitter
|
|
3
|
+
attr_reader :project, :repo, :logger
|
|
4
|
+
|
|
5
|
+
def initialize(project, repo, logger = nil)
|
|
6
|
+
@project = project
|
|
7
|
+
@repo = repo
|
|
8
|
+
@logger = logger || Logger.new(STDOUT)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def commit_resource(tx_resource, branch, language)
|
|
12
|
+
return if prevent_commit_on?(branch)
|
|
13
|
+
|
|
14
|
+
unless language == tx_resource.source_lang
|
|
15
|
+
file_name, translations = download(tx_resource, branch, language)
|
|
16
|
+
|
|
17
|
+
if translations
|
|
18
|
+
repo.api.commit(repo.name, branch, { file_name => translations })
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def download(tx_resource, branch, language)
|
|
26
|
+
downloader = ResourceDownloader.new(
|
|
27
|
+
project, repo, branch, {
|
|
28
|
+
languages: [language], resources: [tx_resource]
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
downloader.first
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def prevent_commit_on?(branch)
|
|
36
|
+
project.protected_branches.include?(branch)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require 'abroad'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
|
|
4
|
+
module Txgh
|
|
5
|
+
class ResourceContents
|
|
6
|
+
EXTRACTOR_MAP = {
|
|
7
|
+
'YML' => 'yaml/rails',
|
|
8
|
+
'YAML' => 'yaml/rails',
|
|
9
|
+
'KEYVALUEJSON' => 'json/key-value',
|
|
10
|
+
'ANDROID' => 'xml/android'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
SERIALIZER_MAP = {
|
|
14
|
+
'YML' => 'yaml/rails',
|
|
15
|
+
'YAML' => 'yaml/rails',
|
|
16
|
+
'KEYVALUEJSON' => 'json/key-value',
|
|
17
|
+
'ANDROID' => 'xml/android'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
def from_phrase_list(tx_resource, phrases)
|
|
22
|
+
new(tx_resource, phrases: phrases)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def from_string(tx_resource, string)
|
|
26
|
+
new(tx_resource, raw: string)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :tx_resource
|
|
31
|
+
|
|
32
|
+
def initialize(tx_resource, options)
|
|
33
|
+
@tx_resource = tx_resource
|
|
34
|
+
@phrases = options[:phrases]
|
|
35
|
+
@raw = options[:raw]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def phrases
|
|
39
|
+
@phrases ||= extractor.from_string(raw) do |extractor|
|
|
40
|
+
extractor.extract_each.map do |key, value|
|
|
41
|
+
{ 'key' => key, 'string' => value }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def add(key, value)
|
|
47
|
+
phrases << { 'key' => key, 'string' => value }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Some formats like Rails YAML require the language to be written somewhere
|
|
51
|
+
# in the file. If you're using this class to parse and serialize the
|
|
52
|
+
# contents of a translated version of a resource, then you'll probably
|
|
53
|
+
# want to override the resource's source language using the second
|
|
54
|
+
# parameter here.
|
|
55
|
+
def write_to(stream, language = tx_resource.source_lang)
|
|
56
|
+
serializer.from_stream(stream, language) do |serializer|
|
|
57
|
+
phrases.each do |phrase|
|
|
58
|
+
serializer.write_key_value(
|
|
59
|
+
phrase['key'], (phrase['string'] || '').to_s
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# see comment above write_to
|
|
66
|
+
def to_s(language = tx_resource.source_lang)
|
|
67
|
+
stream = StringIO.new
|
|
68
|
+
write_to(stream, language)
|
|
69
|
+
stream.string
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def to_h
|
|
73
|
+
Utils.index_on('key', phrases)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def diff(other_contents)
|
|
77
|
+
diff = diff_hash(other_contents)
|
|
78
|
+
diff_phrases = diff[:added] + diff[:modified]
|
|
79
|
+
self.class.from_phrase_list(tx_resource, diff_phrases)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def diff_hash(other_contents)
|
|
83
|
+
DiffCalculator.compare(phrases, other_contents.phrases)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def merge(other_contents, diff_hash)
|
|
87
|
+
MergeCalculator.merge(other_contents, self, diff_hash)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def empty?
|
|
91
|
+
phrases.empty?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
attr_reader :raw
|
|
97
|
+
|
|
98
|
+
def extractor
|
|
99
|
+
id = EXTRACTOR_MAP.fetch(tx_resource.type) do
|
|
100
|
+
raise TxghInternalError,
|
|
101
|
+
"'#{tx_resource.type}' is not a file type that is supported when "\
|
|
102
|
+
"uploading diffs."
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Abroad.extractor(id)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def serializer
|
|
109
|
+
id = SERIALIZER_MAP.fetch(tx_resource.type) do
|
|
110
|
+
raise TxghInternalError,
|
|
111
|
+
"'#{tx_resource.type}' is not a file type that is supported when "\
|
|
112
|
+
"uploading diffs."
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
Abroad.serializer(id)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
class ResourceDownloader
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
attr_reader :project, :repo, :branch
|
|
6
|
+
|
|
7
|
+
def initialize(project, repo, branch, options = {})
|
|
8
|
+
@project = project
|
|
9
|
+
@repo = repo
|
|
10
|
+
@branch = branch
|
|
11
|
+
|
|
12
|
+
# Provides an override list of languages. If not present, the downloader
|
|
13
|
+
# will make an API call to fetch the list of languages for the project.
|
|
14
|
+
@languages = options[:languages]
|
|
15
|
+
@resources = options[:resources]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def each(&block)
|
|
19
|
+
enum.each(&block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def enum
|
|
25
|
+
if repo.upload_diffs?
|
|
26
|
+
download_merging_diff
|
|
27
|
+
else
|
|
28
|
+
download_without_diff
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def download_merging_diff
|
|
33
|
+
return to_enum(__method__) unless block_given?
|
|
34
|
+
|
|
35
|
+
download_each do |head_resource, language_code, file_name|
|
|
36
|
+
diff_point_resource = tx_config.resource(
|
|
37
|
+
head_resource.original_resource_slug, repo.diff_point
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
source_diff = source_diff_hash(head_resource, diff_point_resource)
|
|
41
|
+
head_content = wrap(transifex_download(head_resource, language_code), head_resource)
|
|
42
|
+
diff_point_content = wrap(transifex_download(diff_point_resource, language_code), diff_point_resource)
|
|
43
|
+
contents = diff_point_content.merge(head_content, source_diff)
|
|
44
|
+
|
|
45
|
+
yield file_name, contents.to_s(language_code)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def source_diff_hash(head_resource, diff_point_resource)
|
|
50
|
+
cache_diff(head_resource, diff_point_resource) do
|
|
51
|
+
br = repo.process_all_branches? ? branch : repo.branch
|
|
52
|
+
head_contents = wrap(git_download(head_resource, br), head_resource)
|
|
53
|
+
diff_point_contents = wrap(git_download(diff_point_resource, repo.diff_point), head_resource)
|
|
54
|
+
head_contents.diff_hash(diff_point_contents)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def cache_diff(head_resource, diff_point_resource)
|
|
59
|
+
key = "#{head_resource.resource_slug}|#{diff_point_resource.resource_slug}"
|
|
60
|
+
if diff = diff_cache[key]
|
|
61
|
+
diff
|
|
62
|
+
else
|
|
63
|
+
diff_cache[key] = yield
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def diff_cache
|
|
68
|
+
@diff_cache ||= {}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def download_without_diff
|
|
72
|
+
return to_enum(__method__) unless block_given?
|
|
73
|
+
|
|
74
|
+
download_each do |resource, language_code, file_name|
|
|
75
|
+
contents = transifex_download(resource, language_code)
|
|
76
|
+
yield file_name, contents
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def download_each
|
|
81
|
+
each_resource do |resource|
|
|
82
|
+
each_language do |language_code|
|
|
83
|
+
file_name = resource.translation_path(resource.lang_map(language_code))
|
|
84
|
+
yield resource, language_code, file_name
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def wrap(string, resource)
|
|
90
|
+
if string
|
|
91
|
+
ResourceContents.from_string(resource, string)
|
|
92
|
+
else
|
|
93
|
+
EmptyResourceContents.new(resource)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def transifex_download(resource, language)
|
|
98
|
+
transifex_api.download(resource, language)
|
|
99
|
+
rescue TransifexNotFoundError
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def git_download(resource, branch)
|
|
104
|
+
repo.api.download(repo.name, resource.source_file, branch)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def transifex_api
|
|
108
|
+
project.api
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def each_resource(&block)
|
|
112
|
+
return to_enum(__method__) unless block_given?
|
|
113
|
+
return @resources.each(&block) if @resources
|
|
114
|
+
|
|
115
|
+
ref = repo.process_all_branches? ? branch : nil
|
|
116
|
+
|
|
117
|
+
tx_config.resources.each do |res|
|
|
118
|
+
yield tx_config.resource(res.resource_slug, ref)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def each_language(&block)
|
|
123
|
+
return to_enum(__method__) unless block_given?
|
|
124
|
+
return @languages.each(&block) if @languages
|
|
125
|
+
|
|
126
|
+
raw_languages.each(&block)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def raw_languages
|
|
130
|
+
@raw_languages ||= transifex_api.get_languages(project.name).map do |lang|
|
|
131
|
+
lang['language_code']
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def tx_config
|
|
136
|
+
@tx_config ||= Txgh::Config::TxManager.tx_config(
|
|
137
|
+
project, repo, repo.process_all_branches? ? branch : repo.branch
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|