lokalise_manager 2.2.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/LICENSE +1 -1
- data/README.md +9 -9
- data/lib/lokalise_manager/error.rb +1 -0
- data/lib/lokalise_manager/global_config.rb +3 -2
- data/lib/lokalise_manager/task_definitions/base.rb +7 -6
- data/lib/lokalise_manager/task_definitions/exporter.rb +6 -3
- data/lib/lokalise_manager/task_definitions/importer.rb +1 -0
- data/lib/lokalise_manager/utils/hash_utils.rb +3 -2
- data/lib/lokalise_manager/version.rb +1 -1
- data/lib/lokalise_manager.rb +14 -8
- data/lokalise_manager.gemspec +4 -4
- metadata +22 -28
- data/spec/lib/lokalise_manager/global_config_spec.rb +0 -114
- data/spec/lib/lokalise_manager/task_definitions/base_spec.rb +0 -113
- data/spec/lib/lokalise_manager/task_definitions/exporter_spec.rb +0 -239
- data/spec/lib/lokalise_manager/task_definitions/importer_spec.rb +0 -199
- data/spec/lib/lokalise_manager_spec.rb +0 -15
- data/spec/lib/utils/hash_utils_spec.rb +0 -13
- data/spec/spec_helper.rb +0 -24
- data/spec/support/file_manager.rb +0 -64
- data/spec/support/spec_addons.rb +0 -16
- data/spec/support/vcr.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62f150fda293c97544a2595165d42dd74c59b8e21bed613943d422321ade498f
|
4
|
+
data.tar.gz: a4b01a90a1fe15f7ee7dd8d657ffd2433a5fdbcfb5785b2090b608530cec23f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c65ddeb0a5c671cc6bfdca8e01dbadb8f3ef7dd43d5c870694a243f8dd8e58893571185a01bb07c6c363b1b301b127f2cf7375f67b7c35675c24e6b7a60406d
|
7
|
+
data.tar.gz: 4a43096baca8264d52c7a11ce91a08b73aaa87a3b1b16056f539746753926c6a23ad77f29e5d942d427633b0623031e488c8c66c6e5f466f0085fc721b57b9dc
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 3.2.0 (26-Aug-22)
|
4
|
+
|
5
|
+
* Fixed an issue when `\n` inside translations was imported as `\\n`. The default value for the `translations_converter` is now `->(raw_data) { YAML.dump(raw_data).gsub(/\\\\n/, '\n') }`.
|
6
|
+
|
7
|
+
## 3.1.0 (17-Aug-22)
|
8
|
+
|
9
|
+
* The default format is now `ruby_yaml` (it used to be `yaml`)
|
10
|
+
|
11
|
+
## 3.0.0 (11-Mar-22)
|
12
|
+
|
13
|
+
* **Breaking change**: Require Ruby 2.7 or above
|
14
|
+
* **Breaking change (potentially)**: Use ruby-lokalise-api v6. In general, this transition should not affect you if you employ `export!` and `import!` methods only. However, please be aware that ruby-lokalise-api has a few breaking changes [listed in its own changelog](https://lokalise.github.io/ruby-lokalise-api/additional_info/changelog)
|
15
|
+
* Use Zeitwerk loader
|
16
|
+
* Prettify and update source code
|
17
|
+
|
3
18
|
## 2.2.0 (23-Feb-22)
|
4
19
|
|
5
20
|
* Use ruby-lokalise-api v5
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -70,7 +70,7 @@ The uploading process is multi-threaded.
|
|
70
70
|
`processes` will contain an array of objects responding to the following methods:
|
71
71
|
|
72
72
|
* `#success` — usually returns `true` (to learn more, check documentation for the `:raise_on_export_fail` option below)
|
73
|
-
* `#process` — returns an object (an instance of the `
|
73
|
+
* `#process` — returns an object (an instance of the `RubyLokaliseApi::Resources::QueuedProcess`) representing a [queued background process](https://lokalise.github.io/ruby-lokalise-api/api/queued-processes) as uploading is done in the background on Lokalise.
|
74
74
|
* `#path` — returns an instance of the `Pathname` class which represent the file being uploaded.
|
75
75
|
|
76
76
|
You can perform periodic checks to read the status of the process. Here's a very simple example:
|
@@ -111,7 +111,7 @@ Please don't forget that Lokalise API has rate limiting and you cannot send more
|
|
111
111
|
|
112
112
|
```ruby
|
113
113
|
{
|
114
|
-
format: '
|
114
|
+
format: 'ruby_yaml',
|
115
115
|
placeholder_format: :icu,
|
116
116
|
yaml_include_root: true,
|
117
117
|
original_filenames: true,
|
@@ -120,7 +120,7 @@ Please don't forget that Lokalise API has rate limiting and you cannot send more
|
|
120
120
|
}
|
121
121
|
```
|
122
122
|
|
123
|
-
Full list of available import options [can be found in the official API documentation](https://
|
123
|
+
Full list of available import options [can be found in the official API documentation](https://developers.lokalise.com/reference/download-files).
|
124
124
|
|
125
125
|
You can provide additional options, and they will be merged with the default ones. For example:
|
126
126
|
|
@@ -135,15 +135,15 @@ In this case the `import_opts` will have `original_filenames` set to `true` and
|
|
135
135
|
```ruby
|
136
136
|
importer = LokaliseManager.importer api_token: '1234abc',
|
137
137
|
project_id: '123.abc',
|
138
|
-
import_opts: {indentation: '4sp}
|
138
|
+
import_opts: {indentation: '4sp'}
|
139
139
|
```
|
140
140
|
|
141
141
|
* `import_safe_mode` (`boolean`) — default to `false`. When this option is enabled, the import task will check whether the directory set with `locales_path` is empty or not. If it is not empty, you will be prompted to continue.
|
142
|
-
* `max_retries_import` (`integer`) — this option is introduced to properly handle Lokalise API rate limiting. If the HTTP status code 429 (too many requests) has been received, this gem will apply an exponential backoff mechanism with a very simple formula: `2 ** retries`. If the maximum number of retries has been reached, a `
|
142
|
+
* `max_retries_import` (`integer`) — this option is introduced to properly handle Lokalise API rate limiting. If the HTTP status code 429 (too many requests) has been received, this gem will apply an exponential backoff mechanism with a very simple formula: `2 ** retries`. If the maximum number of retries has been reached, a `RubyLokaliseApi::Error::TooManyRequests` exception will be raised and the operation will be halted.
|
143
143
|
|
144
144
|
### Export config
|
145
145
|
|
146
|
-
* `export_opts` (`hash`) — options that will be passed to Lokalise API when uploading translations. Full list of available export options [can be found in the official documentation](https://
|
146
|
+
* `export_opts` (`hash`) — options that will be passed to Lokalise API when uploading translations. Full list of available export options [can be found in the official documentation](https://developers.lokalise.com/reference/upload-a-file). By default, the following options are provided:
|
147
147
|
+ `data` (`string`, required) — base64-encoded contents of the translation file.
|
148
148
|
+ `filename` (`string`, required) — translation file name. If the file is stored under a subdirectory (for example, `nested/en.yml` inside the `locales/` directory), the whole path acts as a name. Later when importing files with such names, they will be placed into the proper subdirectories.
|
149
149
|
+ `lang_iso` (`string`, required) — language ISO code which is determined using the root key inside your YAML file. For example, in this case the `lang_iso` is `en_US`:
|
@@ -171,7 +171,7 @@ In this case the `export_opts` will have `detect_icu_plurals` set to `true` and
|
|
171
171
|
c.skip_file_export = ->(file) { f.split[1].to_s.include?('fr') }
|
172
172
|
```
|
173
173
|
|
174
|
-
* `max_retries_export` (`integer`) — this option is introduced to properly handle Lokalise API rate limiting. If the HTTP status code 429 (too many requests) has been received, LokaliseManager will apply an exponential backoff mechanism with a very simple formula: `2 ** retries` (initially `retries` is `0`). If the maximum number of retries has been reached, a `
|
174
|
+
* `max_retries_export` (`integer`) — this option is introduced to properly handle Lokalise API rate limiting. If the HTTP status code 429 (too many requests) has been received, LokaliseManager will apply an exponential backoff mechanism with a very simple formula: `2 ** retries` (initially `retries` is `0`). If the maximum number of retries has been reached, a `RubyLokaliseApi::Error::TooManyRequests` exception will be raised and the export operation will be halted. By default, LokaliseManager will make up to `5` retries which potentially means `1 + 2 + 4 + 8 + 16 + 32 = 63` seconds of waiting time. If the `max_retries_export` is less than `1`, LokaliseManager will not perform any retries and give up immediately after receiving error 429.
|
175
175
|
* `raise_on_export_fail` (`boolean`) — default is `true`. When this option is enabled, LokaliseManager will re-raise any exceptions that happened during the file uploading. In other words, if any uploading thread raised an exception, your exporting process will exit with an exception. Suppose, you are uploading 12 translation files; these files will be split in 2 groups with 6 files each, and each group will be uploaded in parallel (using threads). However, suppose some exception happens when uploading the first group. By default this exception will be re-raised for the whole process and the script will never try to upload the second group. If you would like to continue uploading even if an exception happened, set the `raise_on_export_fail` to `false`. In this case the `export!` method will return an array with scheduled processes and with information about processes that were not successfully scheduled. This information is represented as an object with three methods: `path` (contains an instance of the `Pathname` class which says which file could not be uploaded), `error` (the actual exception), and `success` (returns `false`). So, you can use the following snippet to check your processes:
|
176
176
|
|
177
177
|
```ruby
|
@@ -201,7 +201,7 @@ If your translation files are not in YAML format, you will need to adjust the fo
|
|
201
201
|
|
202
202
|
* `file_ext_regexp` (`regexp`) — regular expression applied to file extensions to determine which files should be imported and exported. Defaults to `/\.ya?ml\z/i` (YAML files).
|
203
203
|
* `translations_loader` (`lambda` or `proc`) — loads translations data and makes sure they are valid before saving them to a translation file. Defaults to `->(raw_data) { YAML.safe_load raw_data }`. In the simplest case you may just return the data back, for example `-> (raw_data) { raw_data }`.
|
204
|
-
* `translations_converter` (`lambda` or `proc`) — converts translations data to a proper format before saving them to a translation file. Defaults to `->(raw_data) { raw_data.
|
204
|
+
* `translations_converter` (`lambda` or `proc`) — converts translations data to a proper format before saving them to a translation file. Defaults to `->(raw_data) { YAML.dump(raw_data).gsub(/\\\\n/, '\n') }`. In the simplest case you may just return the data back, for example `-> (raw_data) { raw_data }`.
|
205
205
|
* `lang_iso_inferer` (`lambda` or `proc`) — infers language ISO code based on the translation file data before uploading it to Lokalise. Defaults to `->(data) { YAML.safe_load(data)&.keys&.first }`.
|
206
206
|
|
207
207
|
### Customizing JSON parser and network adapter
|
@@ -302,4 +302,4 @@ importer.import!
|
|
302
302
|
|
303
303
|
## License
|
304
304
|
|
305
|
-
Copyright (c) [Lokalise team](http://lokalise.com), [Ilya
|
305
|
+
Copyright (c) [Lokalise team](http://lokalise.com), [Ilya Krukowski](http://bodrovis.tech). License type is [MIT](https://github.com/bodrovis/lokalise_manager/blob/master/LICENSE).
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module LokaliseManager
|
4
|
+
# Global configuration options available for LokaliseManager
|
4
5
|
class GlobalConfig
|
5
6
|
class << self
|
6
7
|
attr_accessor :api_token, :project_id
|
@@ -64,7 +65,7 @@ module LokaliseManager
|
|
64
65
|
# Options for import rake task
|
65
66
|
def import_opts
|
66
67
|
@import_opts || {
|
67
|
-
format: '
|
68
|
+
format: 'ruby_yaml',
|
68
69
|
placeholder_format: :icu,
|
69
70
|
yaml_include_root: true,
|
70
71
|
original_filenames: true,
|
@@ -94,7 +95,7 @@ module LokaliseManager
|
|
94
95
|
|
95
96
|
# Converts translations data to the proper format
|
96
97
|
def translations_converter
|
97
|
-
@translations_converter || ->(raw_data) { raw_data.
|
98
|
+
@translations_converter || ->(raw_data) { YAML.dump(raw_data).gsub(/\\\\n/, '\n') }
|
98
99
|
end
|
99
100
|
|
100
101
|
# Infers lang ISO for the given translation file
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'ruby_lokalise_api'
|
4
4
|
require 'pathname'
|
5
5
|
|
6
6
|
module LokaliseManager
|
7
7
|
module TaskDefinitions
|
8
|
+
# Base class for LokaliseManager task definitions that includes common methods and logic
|
8
9
|
class Base
|
9
10
|
using LokaliseManager::Utils::HashUtils
|
10
11
|
|
@@ -33,18 +34,18 @@ module LokaliseManager
|
|
33
34
|
|
34
35
|
# Creates a Lokalise API client
|
35
36
|
#
|
36
|
-
# @return [
|
37
|
+
# @return [RubyLokaliseApi::Client]
|
37
38
|
def api_client
|
38
39
|
client_opts = [config.api_token, config.timeouts]
|
39
40
|
client_method = config.use_oauth2_token ? :oauth2_client : :client
|
40
41
|
|
41
|
-
@api_client = ::
|
42
|
+
@api_client = ::RubyLokaliseApi.send(client_method, *client_opts)
|
42
43
|
end
|
43
44
|
|
44
45
|
# Resets API client
|
45
46
|
def reset_api_client!
|
46
|
-
::
|
47
|
-
::
|
47
|
+
::RubyLokaliseApi.reset_client!
|
48
|
+
::RubyLokaliseApi.reset_oauth2_client!
|
48
49
|
@api_client = nil
|
49
50
|
end
|
50
51
|
|
@@ -94,7 +95,7 @@ module LokaliseManager
|
|
94
95
|
retries = 0
|
95
96
|
begin
|
96
97
|
yield
|
97
|
-
rescue
|
98
|
+
rescue RubyLokaliseApi::Error::TooManyRequests => e
|
98
99
|
raise(e.class, "Gave up after #{retries} retries") if retries >= max_retries
|
99
100
|
|
100
101
|
sleep 2**retries
|
@@ -4,6 +4,7 @@ require 'base64'
|
|
4
4
|
|
5
5
|
module LokaliseManager
|
6
6
|
module TaskDefinitions
|
7
|
+
# Exporter class is used when you want to upload translation files from your project to Lokalise
|
7
8
|
class Exporter < Base
|
8
9
|
# Lokalise allows no more than 6 requests per second
|
9
10
|
MAX_THREADS = 6
|
@@ -38,7 +39,9 @@ module LokaliseManager
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def raise_on_fail(thread)
|
41
|
-
|
42
|
+
return if thread.success
|
43
|
+
|
44
|
+
raise(thread.error.class, "Error while trying to upload #{thread.path}: #{thread.error.message}")
|
42
45
|
end
|
43
46
|
|
44
47
|
# Performs the actual file uploading to Lokalise. If the API rate limit is exceeed,
|
@@ -59,7 +62,7 @@ module LokaliseManager
|
|
59
62
|
# Gets translation files from the specified directory
|
60
63
|
def all_files
|
61
64
|
loc_path = config.locales_path
|
62
|
-
Dir["#{loc_path}/**/*"].
|
65
|
+
Dir["#{loc_path}/**/*"].filter_map do |f|
|
63
66
|
full_path = Pathname.new f
|
64
67
|
|
65
68
|
next unless file_matches_criteria? full_path
|
@@ -67,7 +70,7 @@ module LokaliseManager
|
|
67
70
|
relative_path = full_path.relative_path_from Pathname.new(loc_path)
|
68
71
|
|
69
72
|
[full_path, relative_path]
|
70
|
-
end
|
73
|
+
end
|
71
74
|
end
|
72
75
|
|
73
76
|
# Generates export options
|
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Taken from https://github.com/rails/rails/blob/83217025a171593547d1268651b446d3533e2019/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
|
4
|
-
|
5
3
|
module LokaliseManager
|
6
4
|
module Utils
|
5
|
+
# Common helper methods for hashes
|
7
6
|
module HashUtils
|
8
7
|
refine Hash do
|
8
|
+
# Deeply merges two hashes
|
9
|
+
# Taken from https://github.com/rails/rails/blob/83217025a171593547d1268651b446d3533e2019/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
|
9
10
|
def deep_merge(other_hash, &block)
|
10
11
|
dup.deep_merge!(other_hash, &block)
|
11
12
|
end
|
data/lib/lokalise_manager.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'zeitwerk'
|
3
4
|
require 'yaml'
|
5
|
+
require 'psych'
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
require 'lokalise_manager/version'
|
8
|
-
require 'lokalise_manager/error'
|
9
|
-
require 'lokalise_manager/global_config'
|
10
|
-
require 'lokalise_manager/task_definitions/base'
|
11
|
-
require 'lokalise_manager/task_definitions/importer'
|
12
|
-
require 'lokalise_manager/task_definitions/exporter'
|
7
|
+
loader = Zeitwerk::Loader.for_gem
|
8
|
+
loader.setup
|
13
9
|
|
10
|
+
# LokaliseManager main module that exposes helper methods:
|
11
|
+
#
|
12
|
+
# importer = LokaliseManager.importer api_token: '1234abc', project_id: '123.abc'
|
13
|
+
# exporter = LokaliseManager.exporter api_token: '1234abc', project_id: '123.abc'
|
14
|
+
#
|
15
|
+
# Use the instantiated objects to import or export your translation files:
|
16
|
+
#
|
17
|
+
# importer.import!
|
18
|
+
# exporter.export!
|
19
|
+
#
|
14
20
|
module LokaliseManager
|
15
21
|
class << self
|
16
22
|
# Initializes a new importer client which is used to download
|
data/lokalise_manager.gemspec
CHANGED
@@ -5,26 +5,26 @@ require File.expand_path('lib/lokalise_manager/version', __dir__)
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'lokalise_manager'
|
7
7
|
spec.version = LokaliseManager::VERSION
|
8
|
-
spec.authors = ['Ilya
|
8
|
+
spec.authors = ['Ilya Krukowski']
|
9
9
|
spec.email = ['golosizpru@gmail.com']
|
10
10
|
spec.summary = 'Lokalise integration for Ruby'
|
11
11
|
spec.description = 'This gem contains a collection of some common tasks for Lokalise. Specifically, it allows to import/export translation files from/to Lokalise TMS.'
|
12
12
|
spec.homepage = 'https://github.com/bodrovis/lokalise_manager'
|
13
13
|
spec.license = 'MIT'
|
14
14
|
spec.platform = Gem::Platform::RUBY
|
15
|
-
spec.required_ruby_version = '>= 2.
|
15
|
+
spec.required_ruby_version = '>= 2.7'
|
16
16
|
|
17
17
|
spec.files = Dir['README.md', 'LICENSE',
|
18
18
|
'CHANGELOG.md', 'lib/**/*.rb',
|
19
19
|
'lib/**/*.rake',
|
20
20
|
'lokalise_manager.gemspec', '.github/*.md',
|
21
21
|
'Gemfile', 'Rakefile']
|
22
|
-
spec.test_files = Dir['spec/**/*.rb']
|
23
22
|
spec.extra_rdoc_files = ['README.md']
|
24
23
|
spec.require_paths = ['lib']
|
25
24
|
|
26
|
-
spec.add_dependency 'ruby-lokalise-api', '~>
|
25
|
+
spec.add_dependency 'ruby-lokalise-api', '~> 6.0'
|
27
26
|
spec.add_dependency 'rubyzip', '~> 2.3'
|
27
|
+
spec.add_dependency 'zeitwerk', '~> 2.4'
|
28
28
|
|
29
29
|
spec.add_development_dependency 'codecov', '~> 0.2'
|
30
30
|
spec.add_development_dependency 'dotenv', '~> 2.5'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lokalise_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Ilya
|
7
|
+
- Ilya Krukowski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-lokalise-api
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubyzip
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: zeitwerk
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.4'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: codecov
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -190,16 +204,6 @@ files:
|
|
190
204
|
- lib/lokalise_manager/utils/hash_utils.rb
|
191
205
|
- lib/lokalise_manager/version.rb
|
192
206
|
- lokalise_manager.gemspec
|
193
|
-
- spec/lib/lokalise_manager/global_config_spec.rb
|
194
|
-
- spec/lib/lokalise_manager/task_definitions/base_spec.rb
|
195
|
-
- spec/lib/lokalise_manager/task_definitions/exporter_spec.rb
|
196
|
-
- spec/lib/lokalise_manager/task_definitions/importer_spec.rb
|
197
|
-
- spec/lib/lokalise_manager_spec.rb
|
198
|
-
- spec/lib/utils/hash_utils_spec.rb
|
199
|
-
- spec/spec_helper.rb
|
200
|
-
- spec/support/file_manager.rb
|
201
|
-
- spec/support/spec_addons.rb
|
202
|
-
- spec/support/vcr.rb
|
203
207
|
homepage: https://github.com/bodrovis/lokalise_manager
|
204
208
|
licenses:
|
205
209
|
- MIT
|
@@ -213,25 +217,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
213
217
|
requirements:
|
214
218
|
- - ">="
|
215
219
|
- !ruby/object:Gem::Version
|
216
|
-
version: 2.
|
220
|
+
version: '2.7'
|
217
221
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
218
222
|
requirements:
|
219
223
|
- - ">="
|
220
224
|
- !ruby/object:Gem::Version
|
221
225
|
version: '0'
|
222
226
|
requirements: []
|
223
|
-
rubygems_version: 3.3.
|
227
|
+
rubygems_version: 3.3.21
|
224
228
|
signing_key:
|
225
229
|
specification_version: 4
|
226
230
|
summary: Lokalise integration for Ruby
|
227
|
-
test_files:
|
228
|
-
- spec/lib/lokalise_manager/global_config_spec.rb
|
229
|
-
- spec/lib/lokalise_manager/task_definitions/base_spec.rb
|
230
|
-
- spec/lib/lokalise_manager/task_definitions/exporter_spec.rb
|
231
|
-
- spec/lib/lokalise_manager/task_definitions/importer_spec.rb
|
232
|
-
- spec/lib/lokalise_manager_spec.rb
|
233
|
-
- spec/lib/utils/hash_utils_spec.rb
|
234
|
-
- spec/spec_helper.rb
|
235
|
-
- spec/support/file_manager.rb
|
236
|
-
- spec/support/spec_addons.rb
|
237
|
-
- spec/support/vcr.rb
|
231
|
+
test_files: []
|
@@ -1,114 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe LokaliseManager::GlobalConfig do
|
4
|
-
let(:fake_class) { class_double(described_class) }
|
5
|
-
|
6
|
-
it 'is possible to provide config' do
|
7
|
-
described_class.config do |c|
|
8
|
-
expect(c).to eq(described_class)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'is possible to set project_id' do
|
13
|
-
allow(fake_class).to receive(:project_id=).with('123.abc')
|
14
|
-
fake_class.project_id = '123.abc'
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'is possible to set raise_on_export_fail' do
|
18
|
-
allow(fake_class).to receive(:raise_on_export_fail=).with(false)
|
19
|
-
fake_class.raise_on_export_fail = false
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'is possible to set silent_mode' do
|
23
|
-
allow(fake_class).to receive(:silent_mode=).with(true)
|
24
|
-
fake_class.silent_mode = true
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'is possible to set use_oauth2_token' do
|
28
|
-
allow(fake_class).to receive(:use_oauth2_token=).with(true)
|
29
|
-
fake_class.use_oauth2_token = true
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'is possible to set file_ext_regexp' do
|
33
|
-
allow(fake_class).to receive(:file_ext_regexp=).with(Regexp.new('.*'))
|
34
|
-
fake_class.file_ext_regexp = Regexp.new('.*')
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'is possible to set import_opts' do
|
38
|
-
allow(fake_class).to receive(:import_opts=).with(duck_type(:each))
|
39
|
-
fake_class.import_opts = {
|
40
|
-
format: 'json',
|
41
|
-
indentation: '8sp'
|
42
|
-
}
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'is possible to set export_opts' do
|
46
|
-
allow(fake_class).to receive(:export_opts=).with(duck_type(:each))
|
47
|
-
fake_class.export_opts = {
|
48
|
-
convert_placeholders: true,
|
49
|
-
detect_icu_plurals: true
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'is possible to set branch' do
|
54
|
-
allow(fake_class).to receive(:branch=).with('custom')
|
55
|
-
fake_class.branch = 'custom'
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'is possible to set timeouts' do
|
59
|
-
allow(fake_class).to receive(:timeouts=).with(duck_type(:each))
|
60
|
-
fake_class.timeouts = {
|
61
|
-
open_timeout: 100,
|
62
|
-
timeout: 500
|
63
|
-
}
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'is possible to set import_safe_mode' do
|
67
|
-
allow(fake_class).to receive(:import_safe_mode=).with(true)
|
68
|
-
fake_class.import_safe_mode = true
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'is possible to set max_retries_export' do
|
72
|
-
allow(fake_class).to receive(:max_retries_export=).with(10)
|
73
|
-
fake_class.max_retries_export = 10
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'is possible to set max_retries_import' do
|
77
|
-
allow(fake_class).to receive(:max_retries_import=).with(10)
|
78
|
-
fake_class.max_retries_import = 10
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'is possible to set api_token' do
|
82
|
-
allow(fake_class).to receive(:api_token=).with('abc')
|
83
|
-
fake_class.api_token = 'abc'
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'is possible to override locales_path' do
|
87
|
-
allow(fake_class).to receive(:locales_path=).with('/demo/path')
|
88
|
-
fake_class.locales_path = '/demo/path'
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'is possible to set skip_file_export' do
|
92
|
-
cond = ->(f) { f.nil? }
|
93
|
-
allow(fake_class).to receive(:skip_file_export=).with(cond)
|
94
|
-
fake_class.skip_file_export = cond
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'is possible to set translations_loader' do
|
98
|
-
runner = ->(f) { f.to_json }
|
99
|
-
allow(fake_class).to receive(:translations_loader=).with(runner)
|
100
|
-
fake_class.translations_loader = runner
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'is possible to set translations_converter' do
|
104
|
-
runner = ->(f) { f.to_json }
|
105
|
-
allow(fake_class).to receive(:translations_converter=).with(runner)
|
106
|
-
fake_class.translations_converter = runner
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'is possible to set lang_iso_inferer' do
|
110
|
-
runner = ->(f) { f.to_json }
|
111
|
-
allow(fake_class).to receive(:lang_iso_inferer=).with(runner)
|
112
|
-
fake_class.lang_iso_inferer = runner
|
113
|
-
end
|
114
|
-
end
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe LokaliseManager::TaskDefinitions::Base do
|
4
|
-
let(:described_object) { described_class.new }
|
5
|
-
|
6
|
-
describe '.new' do
|
7
|
-
it 'allows to override config' do
|
8
|
-
obj = described_class.new token: 'fake'
|
9
|
-
expect(obj.config.token).to eq('fake')
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '#config' do
|
14
|
-
it 'allows to update config after initialization' do
|
15
|
-
obj = described_class.new token: 'fake', project_id: '123'
|
16
|
-
|
17
|
-
obj.config.project_id = '345'
|
18
|
-
|
19
|
-
expect(obj.config.project_id).to eq('345')
|
20
|
-
expect(obj.config.token).to eq('fake')
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
specify '.reset_client!' do
|
25
|
-
expect(described_object.api_client).to be_an_instance_of(Lokalise::Client)
|
26
|
-
described_object.reset_api_client!
|
27
|
-
current_client = described_object.instance_variable_get :@api_client
|
28
|
-
expect(current_client).to be_nil
|
29
|
-
end
|
30
|
-
|
31
|
-
specify '.project_id_with_branch!' do
|
32
|
-
result = described_object.send :project_id_with_branch
|
33
|
-
expect(result).to be_an_instance_of(String)
|
34
|
-
expect(result).not_to include(':master')
|
35
|
-
|
36
|
-
described_object.config.branch = 'develop'
|
37
|
-
result = described_object.send :project_id_with_branch
|
38
|
-
expect(result).to be_an_instance_of(String)
|
39
|
-
expect(result).to include(':develop')
|
40
|
-
end
|
41
|
-
|
42
|
-
describe '.subdir_and_filename_for' do
|
43
|
-
it 'works properly for longer paths' do
|
44
|
-
path = 'my_path/is/here/file.yml'
|
45
|
-
result = described_object.send(:subdir_and_filename_for, path)
|
46
|
-
expect(result.length).to eq(2)
|
47
|
-
expect(result[0]).to be_an_instance_of(Pathname)
|
48
|
-
expect(result[0].to_s).to eq('my_path/is/here')
|
49
|
-
expect(result[1].to_s).to eq('file.yml')
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'works properly for shorter paths' do
|
53
|
-
path = 'file.yml'
|
54
|
-
result = described_object.send(:subdir_and_filename_for, path)
|
55
|
-
expect(result.length).to eq(2)
|
56
|
-
expect(result[1]).to be_an_instance_of(Pathname)
|
57
|
-
expect(result[0].to_s).to eq('.')
|
58
|
-
expect(result[1].to_s).to eq('file.yml')
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe '.check_options_errors!' do
|
63
|
-
it 'raises an error when the API key is not set' do
|
64
|
-
allow(LokaliseManager::GlobalConfig).to receive(:api_token).and_return(nil)
|
65
|
-
|
66
|
-
expect { described_object.send(:check_options_errors!) }.to raise_error(LokaliseManager::Error, /API token is not set/i)
|
67
|
-
|
68
|
-
expect(LokaliseManager::GlobalConfig).to have_received(:api_token)
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'returns an error when the project_id is not set' do
|
72
|
-
allow_project_id described_object, nil do
|
73
|
-
expect { described_object.send(:check_options_errors!) }.to raise_error(LokaliseManager::Error, /ID is not set/i)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
describe '.proper_ext?' do
|
79
|
-
it 'works properly with path represented as a string' do
|
80
|
-
path = 'my_path/here/file.yml'
|
81
|
-
expect(described_object.send(:proper_ext?, path)).to be true
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'works properly with path represented as a pathname' do
|
85
|
-
path = Pathname.new 'my_path/here/file.json'
|
86
|
-
expect(described_object.send(:proper_ext?, path)).to be false
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
describe '.api_client' do
|
91
|
-
it 'is possible to set timeouts' do
|
92
|
-
allow(described_object.config).to receive(:timeouts).and_return({
|
93
|
-
open_timeout: 100,
|
94
|
-
timeout: 500
|
95
|
-
})
|
96
|
-
|
97
|
-
client = described_object.api_client
|
98
|
-
expect(client).to be_an_instance_of(Lokalise::Client)
|
99
|
-
expect(client).not_to be_an_instance_of(Lokalise::OAuth2Client)
|
100
|
-
expect(client.open_timeout).to eq(100)
|
101
|
-
expect(client.timeout).to eq(500)
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'uses .oauth_client when the use_oauth2_token is true' do
|
105
|
-
allow(described_object.config).to receive(:use_oauth2_token).and_return(true)
|
106
|
-
|
107
|
-
client = described_object.api_client
|
108
|
-
|
109
|
-
expect(client).to be_an_instance_of(Lokalise::OAuth2Client)
|
110
|
-
expect(client).not_to be_an_instance_of(Lokalise::Client)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
@@ -1,239 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'base64'
|
4
|
-
|
5
|
-
describe LokaliseManager::TaskDefinitions::Exporter do
|
6
|
-
let(:filename) { 'en.yml' }
|
7
|
-
let(:path) { "#{Dir.getwd}/locales/nested/#{filename}" }
|
8
|
-
let(:relative_name) { "nested/#{filename}" }
|
9
|
-
let(:project_id) { ENV['LOKALISE_PROJECT_ID'] }
|
10
|
-
let(:described_object) do
|
11
|
-
described_class.new project_id: project_id,
|
12
|
-
api_token: ENV['LOKALISE_API_TOKEN'],
|
13
|
-
max_retries_export: 2
|
14
|
-
end
|
15
|
-
|
16
|
-
context 'with many translation files' do
|
17
|
-
before :all do
|
18
|
-
add_translation_files! with_ru: true, additional: 5
|
19
|
-
end
|
20
|
-
|
21
|
-
after :all do
|
22
|
-
rm_translation_files
|
23
|
-
end
|
24
|
-
|
25
|
-
describe '.export!' do
|
26
|
-
it 'sends a proper API request and handles rate limiting' do
|
27
|
-
process = nil
|
28
|
-
|
29
|
-
VCR.use_cassette('upload_files_multiple') do
|
30
|
-
expect { process = described_object.export!.first.process }.to output(/complete!/).to_stdout
|
31
|
-
end
|
32
|
-
|
33
|
-
expect(process.project_id).to eq(project_id)
|
34
|
-
expect(process.status).to eq('queued')
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'handles too many requests' do
|
38
|
-
allow(described_object.config).to receive(:max_retries_export).and_return(1)
|
39
|
-
allow(described_object).to receive(:sleep).and_return(0)
|
40
|
-
|
41
|
-
fake_client = instance_double('Lokalise::Client')
|
42
|
-
allow(fake_client).to receive(:token).with(any_args).and_return('fake_token')
|
43
|
-
allow(fake_client).to receive(:upload_file).with(any_args).and_raise(Lokalise::Error::TooManyRequests)
|
44
|
-
allow(described_object).to receive(:api_client).and_return(fake_client)
|
45
|
-
|
46
|
-
expect { described_object.export! }.to raise_error(Lokalise::Error::TooManyRequests, /Gave up after 1 retries/i)
|
47
|
-
|
48
|
-
expect(described_object).to have_received(:sleep).exactly(6).times
|
49
|
-
expect(described_object).to have_received(:api_client).at_least(12).times
|
50
|
-
expect(fake_client).to have_received(:upload_file).exactly(12).times
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'handles too many requests but does not re-raise anything when raise_on_export_fail is false' do
|
54
|
-
allow(described_object.config).to receive(:max_retries_export).and_return(1)
|
55
|
-
allow(described_object.config).to receive(:raise_on_export_fail).and_return(false)
|
56
|
-
allow(described_object).to receive(:sleep).and_return(0)
|
57
|
-
|
58
|
-
fake_client = instance_double('Lokalise::Client')
|
59
|
-
allow(fake_client).to receive(:token).with(any_args).and_return('fake_token')
|
60
|
-
allow(fake_client).to receive(:upload_file).with(any_args).and_raise(Lokalise::Error::TooManyRequests)
|
61
|
-
allow(described_object).to receive(:api_client).and_return(fake_client)
|
62
|
-
processes = []
|
63
|
-
expect { processes = described_object.export! }.not_to raise_error
|
64
|
-
|
65
|
-
expect(processes[0].success).to be false
|
66
|
-
expect(processes[1].error.class).to eq(Lokalise::Error::TooManyRequests)
|
67
|
-
expect(processes.count).to eq(7)
|
68
|
-
|
69
|
-
expect(described_object).to have_received(:sleep).exactly(7).times
|
70
|
-
expect(described_object).to have_received(:api_client).at_least(14).times
|
71
|
-
expect(fake_client).to have_received(:upload_file).exactly(14).times
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
context 'with one translation file' do
|
77
|
-
before :all do
|
78
|
-
add_translation_files!
|
79
|
-
end
|
80
|
-
|
81
|
-
after :all do
|
82
|
-
rm_translation_files
|
83
|
-
end
|
84
|
-
|
85
|
-
describe '.export!' do
|
86
|
-
it 'sends a proper API request but does not output anything when silent_mode is enabled' do
|
87
|
-
allow(described_object.config).to receive(:silent_mode).and_return(true)
|
88
|
-
|
89
|
-
process = nil
|
90
|
-
|
91
|
-
VCR.use_cassette('upload_files') do
|
92
|
-
expect { process = described_object.export!.first.process }.not_to output(/complete!/).to_stdout
|
93
|
-
end
|
94
|
-
|
95
|
-
expect(process.status).to eq('queued')
|
96
|
-
expect(described_object.config).to have_received(:silent_mode).at_most(1).times
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'sends a proper API request' do
|
100
|
-
process = VCR.use_cassette('upload_files') do
|
101
|
-
described_object.export!
|
102
|
-
end.first.process
|
103
|
-
|
104
|
-
expect(process.project_id).to eq(project_id)
|
105
|
-
expect(process.status).to eq('queued')
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'sends a proper API request when a different branch is provided' do
|
109
|
-
allow(described_object.config).to receive(:branch).and_return('develop')
|
110
|
-
|
111
|
-
process_data = VCR.use_cassette('upload_files_branch') do
|
112
|
-
described_object.export!
|
113
|
-
end.first
|
114
|
-
|
115
|
-
expect(described_object.config).to have_received(:branch).at_most(2).times
|
116
|
-
expect(process_data.success).to be true
|
117
|
-
expect(process_data.path.to_s).to include('en.yml')
|
118
|
-
|
119
|
-
process = process_data.process
|
120
|
-
expect(process).to be_an_instance_of(Lokalise::Resources::QueuedProcess)
|
121
|
-
expect(process.project_id).to eq(project_id)
|
122
|
-
expect(process.status).to eq('queued')
|
123
|
-
end
|
124
|
-
|
125
|
-
it 'halts when the API key is not set' do
|
126
|
-
allow(described_object.config).to receive(:api_token).and_return(nil)
|
127
|
-
|
128
|
-
expect { described_object.export! }.to raise_error(LokaliseManager::Error, /API token is not set/i)
|
129
|
-
expect(described_object.config).to have_received(:api_token)
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'halts when the project_id is not set' do
|
133
|
-
allow_project_id described_object, nil do
|
134
|
-
expect { described_object.export! }.to raise_error(LokaliseManager::Error, /ID is not set/i)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
describe '#all_files' do
|
140
|
-
it 'yield proper arguments' do
|
141
|
-
expect(described_object.send(:all_files).flatten).to include(
|
142
|
-
Pathname.new(path),
|
143
|
-
Pathname.new(relative_name)
|
144
|
-
)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
describe '.opts' do
|
149
|
-
let(:base64content) { Base64.strict_encode64(File.read(path).strip) }
|
150
|
-
|
151
|
-
it 'generates proper options' do
|
152
|
-
resulting_opts = described_object.send(:opts, path, relative_name)
|
153
|
-
|
154
|
-
expect(resulting_opts[:data]).to eq(base64content)
|
155
|
-
expect(resulting_opts[:filename]).to eq(relative_name)
|
156
|
-
expect(resulting_opts[:lang_iso]).to eq('en')
|
157
|
-
end
|
158
|
-
|
159
|
-
it 'allows to redefine options' do
|
160
|
-
allow(described_object.config).to receive(:export_opts).and_return({
|
161
|
-
detect_icu_plurals: true,
|
162
|
-
convert_placeholders: true
|
163
|
-
})
|
164
|
-
|
165
|
-
resulting_opts = described_object.send(:opts, path, relative_name)
|
166
|
-
|
167
|
-
expect(described_object.config).to have_received(:export_opts)
|
168
|
-
expect(resulting_opts[:data]).to eq(base64content)
|
169
|
-
expect(resulting_opts[:filename]).to eq(relative_name)
|
170
|
-
expect(resulting_opts[:lang_iso]).to eq('en')
|
171
|
-
expect(resulting_opts[:detect_icu_plurals]).to be true
|
172
|
-
expect(resulting_opts[:convert_placeholders]).to be true
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
context 'with two translation files' do
|
178
|
-
let(:filename_ru) { 'ru.yml' }
|
179
|
-
let(:path_ru) { "#{Dir.getwd}/locales/#{filename_ru}" }
|
180
|
-
|
181
|
-
before :all do
|
182
|
-
add_translation_files! with_ru: true
|
183
|
-
end
|
184
|
-
|
185
|
-
after :all do
|
186
|
-
rm_translation_files
|
187
|
-
end
|
188
|
-
|
189
|
-
describe '.export!' do
|
190
|
-
it 're-raises export errors' do
|
191
|
-
allow_project_id described_object, '542886116159f798720dc4.94769464'
|
192
|
-
|
193
|
-
VCR.use_cassette('upload_files_error') do
|
194
|
-
expect { described_object.export! }.to raise_error(Lokalise::Error::BadRequest, /Unknown `lang_iso`/)
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
describe '.opts' do
|
200
|
-
let(:base64content_ru) { Base64.strict_encode64(File.read(path_ru).strip) }
|
201
|
-
|
202
|
-
it 'generates proper options' do
|
203
|
-
resulting_opts = described_object.send(:opts, path_ru, filename_ru)
|
204
|
-
|
205
|
-
expect(resulting_opts[:data]).to eq(base64content_ru)
|
206
|
-
expect(resulting_opts[:filename]).to eq(filename_ru)
|
207
|
-
expect(resulting_opts[:lang_iso]).to eq('ru_RU')
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
describe '#all_files' do
|
212
|
-
it 'returns all files' do
|
213
|
-
files = described_object.send(:all_files).flatten
|
214
|
-
expect(files).to include(
|
215
|
-
Pathname.new(path),
|
216
|
-
Pathname.new(relative_name)
|
217
|
-
)
|
218
|
-
expect(files).to include(
|
219
|
-
Pathname.new(path_ru),
|
220
|
-
Pathname.new(filename_ru)
|
221
|
-
)
|
222
|
-
end
|
223
|
-
|
224
|
-
it 'does not return files that have to be skipped' do
|
225
|
-
allow(described_object.config).to receive(:skip_file_export).twice.and_return(
|
226
|
-
->(f) { f.split[1].to_s.include?('ru') }
|
227
|
-
)
|
228
|
-
files = described_object.send(:all_files).sort
|
229
|
-
expect(files[0]).to include(
|
230
|
-
Pathname.new(path),
|
231
|
-
Pathname.new(relative_name)
|
232
|
-
)
|
233
|
-
expect(files.count).to eq(1)
|
234
|
-
|
235
|
-
expect(described_object.config).to have_received(:skip_file_export).twice
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
@@ -1,199 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe LokaliseManager::TaskDefinitions::Importer do
|
4
|
-
let(:described_object) do
|
5
|
-
described_class.new project_id: project_id,
|
6
|
-
api_token: ENV['LOKALISE_API_TOKEN'],
|
7
|
-
max_retries_import: 2
|
8
|
-
end
|
9
|
-
let(:loc_path) { described_object.config.locales_path }
|
10
|
-
let(:project_id) { ENV['LOKALISE_PROJECT_ID'] }
|
11
|
-
let(:local_trans) { "#{Dir.getwd}/spec/fixtures/trans.zip" }
|
12
|
-
|
13
|
-
describe '#open_and_process_zip' do
|
14
|
-
it 're-raises errors during file processing' do
|
15
|
-
entry = double
|
16
|
-
allow(entry).to receive(:name).and_return('fail.yml')
|
17
|
-
allow(described_object).to receive(:data_from).with(entry).and_raise(EncodingError)
|
18
|
-
expect { described_object.send(:process!, entry) }.
|
19
|
-
to raise_error(EncodingError, /Error when trying to process fail\.yml/)
|
20
|
-
|
21
|
-
expect(described_object).to have_received(:data_from)
|
22
|
-
end
|
23
|
-
|
24
|
-
it 're-raises errors during file opening' do
|
25
|
-
expect { described_object.send(:open_and_process_zip, 'http://fake.url/wrong/path.zip') }.
|
26
|
-
to raise_error(SocketError, /Failed to open TCP connection/)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe '#download_files' do
|
31
|
-
it 'returns a proper download URL' do
|
32
|
-
response = VCR.use_cassette('download_files') do
|
33
|
-
described_object.send :download_files
|
34
|
-
end
|
35
|
-
|
36
|
-
expect(response['project_id']).to eq('672198945b7d72fc048021.15940510')
|
37
|
-
expect(response['bundle_url']).to include('s3-eu-west-1.amazonaws.com')
|
38
|
-
end
|
39
|
-
|
40
|
-
it 're-raises errors during file download' do
|
41
|
-
allow_project_id described_object, 'invalid'
|
42
|
-
|
43
|
-
VCR.use_cassette('download_files_error') do
|
44
|
-
expect { described_object.send :download_files }.
|
45
|
-
to raise_error(Lokalise::Error::BadRequest, /Invalid `project_id` parameter/)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
describe '.import!' do
|
51
|
-
context 'with errors' do
|
52
|
-
it 'handles too many requests' do
|
53
|
-
allow(described_object).to receive(:sleep).and_return(0)
|
54
|
-
|
55
|
-
fake_client = instance_double('Lokalise::Client')
|
56
|
-
allow(fake_client).to receive(:download_files).and_raise(Lokalise::Error::TooManyRequests)
|
57
|
-
allow(described_object).to receive(:api_client).and_return(fake_client)
|
58
|
-
|
59
|
-
expect { described_object.import! }.to raise_error(Lokalise::Error::TooManyRequests, /Gave up after 2 retries/i)
|
60
|
-
|
61
|
-
expect(described_object).to have_received(:sleep).exactly(2).times
|
62
|
-
expect(described_object).to have_received(:api_client).exactly(3).times
|
63
|
-
expect(fake_client).to have_received(:download_files).exactly(3).times
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'halts when the API key is not set' do
|
67
|
-
allow(described_object.config).to receive(:api_token).and_return(nil)
|
68
|
-
expect { described_object.import! }.to raise_error(LokaliseManager::Error, /API token is not set/i)
|
69
|
-
expect(described_object.config).to have_received(:api_token)
|
70
|
-
expect(count_translations).to eq(0)
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'halts when the project_id is not set' do
|
74
|
-
allow_project_id described_object, nil do
|
75
|
-
expect { described_object.import! }.to raise_error(LokaliseManager::Error, /ID is not set/i)
|
76
|
-
expect(count_translations).to eq(0)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
context 'when directory is empty' do
|
82
|
-
before do
|
83
|
-
mkdir_locales
|
84
|
-
rm_translation_files
|
85
|
-
end
|
86
|
-
|
87
|
-
after :all do
|
88
|
-
rm_translation_files
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'runs import successfully for local files' do
|
92
|
-
allow(described_object).to receive(:download_files).and_return(
|
93
|
-
{
|
94
|
-
'project_id' => '123.abc',
|
95
|
-
'bundle_url' => local_trans
|
96
|
-
}
|
97
|
-
)
|
98
|
-
|
99
|
-
expect(described_object.import!).to be true
|
100
|
-
|
101
|
-
expect(count_translations).to eq(4)
|
102
|
-
expect(described_object).to have_received(:download_files)
|
103
|
-
expect_file_exist loc_path, 'en/nested/main_en.yml'
|
104
|
-
expect_file_exist loc_path, 'en/nested/deep/secondary_en.yml'
|
105
|
-
expect_file_exist loc_path, 'ru/main_ru.yml'
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'runs import successfully' do
|
109
|
-
result = nil
|
110
|
-
|
111
|
-
VCR.use_cassette('import') do
|
112
|
-
expect { result = described_object.import! }.to output(/complete!/).to_stdout
|
113
|
-
end
|
114
|
-
|
115
|
-
expect(result).to be true
|
116
|
-
|
117
|
-
expect(count_translations).to eq(24)
|
118
|
-
expect_file_exist loc_path, 'en_1.yml'
|
119
|
-
expect_file_exist loc_path, 'ru_2.yml'
|
120
|
-
end
|
121
|
-
|
122
|
-
it 'runs import successfully but does not provide any output when silent_mode is enabled' do
|
123
|
-
allow(described_object.config).to receive(:silent_mode).and_return(true)
|
124
|
-
result = nil
|
125
|
-
|
126
|
-
VCR.use_cassette('import') do
|
127
|
-
expect { result = described_object.import! }.not_to output(/complete!/).to_stdout
|
128
|
-
end
|
129
|
-
|
130
|
-
expect(result).to be true
|
131
|
-
expect_file_exist loc_path, 'en_1.yml'
|
132
|
-
expect_file_exist loc_path, 'ru_2.yml'
|
133
|
-
expect(described_object.config).to have_received(:silent_mode).at_most(1).times
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
context 'when directory is not empty and safe mode enabled' do
|
138
|
-
let(:safe_mode_obj) do
|
139
|
-
described_class.new project_id: project_id,
|
140
|
-
api_token: ENV['LOKALISE_API_TOKEN'],
|
141
|
-
import_safe_mode: true
|
142
|
-
end
|
143
|
-
|
144
|
-
before :all do
|
145
|
-
mkdir_locales
|
146
|
-
end
|
147
|
-
|
148
|
-
before do
|
149
|
-
rm_translation_files
|
150
|
-
add_translation_files!
|
151
|
-
end
|
152
|
-
|
153
|
-
after :all do
|
154
|
-
rm_translation_files
|
155
|
-
end
|
156
|
-
|
157
|
-
it 'import proceeds when the user agrees' do
|
158
|
-
allow(safe_mode_obj).to receive(:download_files).and_return(
|
159
|
-
{
|
160
|
-
'project_id' => '123.abc',
|
161
|
-
'bundle_url' => local_trans
|
162
|
-
}
|
163
|
-
)
|
164
|
-
|
165
|
-
allow($stdin).to receive(:gets).and_return('Y')
|
166
|
-
expect { safe_mode_obj.import! }.to output(/is not empty/).to_stdout
|
167
|
-
|
168
|
-
expect(count_translations).to eq(5)
|
169
|
-
expect($stdin).to have_received(:gets)
|
170
|
-
expect(safe_mode_obj).to have_received(:download_files)
|
171
|
-
expect_file_exist loc_path, 'en/nested/main_en.yml'
|
172
|
-
expect_file_exist loc_path, 'en/nested/deep/secondary_en.yml'
|
173
|
-
expect_file_exist loc_path, 'ru/main_ru.yml'
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'import halts when a user chooses not to proceed' do
|
177
|
-
allow(safe_mode_obj).to receive(:download_files).at_most(0).times
|
178
|
-
allow($stdin).to receive(:gets).and_return('N')
|
179
|
-
expect { safe_mode_obj.import! }.to output(/cancelled/).to_stdout
|
180
|
-
|
181
|
-
expect(safe_mode_obj).not_to have_received(:download_files)
|
182
|
-
expect($stdin).to have_received(:gets)
|
183
|
-
expect(count_translations).to eq(1)
|
184
|
-
end
|
185
|
-
|
186
|
-
it 'import halts when a user chooses not to proceed and debug info is not printed out when silent_mode is enabled' do
|
187
|
-
allow(safe_mode_obj.config).to receive(:silent_mode).and_return(true)
|
188
|
-
allow(safe_mode_obj).to receive(:download_files).at_most(0).times
|
189
|
-
allow($stdin).to receive(:gets).and_return('N')
|
190
|
-
expect { safe_mode_obj.import! }.not_to output(/cancelled/).to_stdout
|
191
|
-
|
192
|
-
expect(safe_mode_obj).not_to have_received(:download_files)
|
193
|
-
expect(safe_mode_obj.config).to have_received(:silent_mode)
|
194
|
-
expect($stdin).to have_received(:gets)
|
195
|
-
expect(count_translations).to eq(1)
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe LokaliseManager do
|
4
|
-
it 'returns a proper version' do
|
5
|
-
expect(described_class::VERSION).to be_a(String)
|
6
|
-
end
|
7
|
-
|
8
|
-
specify '.importer' do
|
9
|
-
expect(described_class.importer).to be_a(LokaliseManager::TaskDefinitions::Importer)
|
10
|
-
end
|
11
|
-
|
12
|
-
specify '.exporter' do
|
13
|
-
expect(described_class.exporter).to be_a(LokaliseManager::TaskDefinitions::Exporter)
|
14
|
-
end
|
15
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe LokaliseManager::Utils::HashUtils do
|
4
|
-
using described_class
|
5
|
-
let(:h1) { {a: 100, b: 200, c: {c1: 100}} }
|
6
|
-
let(:h2) { {b: 250, c: {c1: 200}} }
|
7
|
-
|
8
|
-
specify '#deep_merge' do
|
9
|
-
result = h1.deep_merge(h2) { |_key, this_val, other_val| this_val + other_val }
|
10
|
-
expect(result[:b]).to eq(450)
|
11
|
-
expect(result[:c][:c1]).to eq(300)
|
12
|
-
end
|
13
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dotenv/load'
|
4
|
-
require 'simplecov'
|
5
|
-
|
6
|
-
SimpleCov.start do
|
7
|
-
add_filter 'spec/'
|
8
|
-
add_filter '.github/'
|
9
|
-
end
|
10
|
-
|
11
|
-
if ENV['CI'] == 'true'
|
12
|
-
require 'codecov'
|
13
|
-
SimpleCov.formatter = SimpleCov::Formatter::Codecov
|
14
|
-
end
|
15
|
-
|
16
|
-
require 'lokalise_manager'
|
17
|
-
|
18
|
-
# Support files
|
19
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
|
20
|
-
|
21
|
-
RSpec.configure do |config|
|
22
|
-
config.include FileManager
|
23
|
-
config.include SpecAddons
|
24
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'fileutils'
|
4
|
-
|
5
|
-
module FileManager
|
6
|
-
def mkdir_locales
|
7
|
-
FileUtils.mkdir_p(LokaliseManager::GlobalConfig.locales_path) unless File.directory?(LokaliseManager::GlobalConfig.locales_path)
|
8
|
-
end
|
9
|
-
|
10
|
-
def rm_translation_files
|
11
|
-
FileUtils.rm_rf locales_dir
|
12
|
-
end
|
13
|
-
|
14
|
-
def locales_dir
|
15
|
-
Dir["#{LokaliseManager::GlobalConfig.locales_path}/**/*"]
|
16
|
-
end
|
17
|
-
|
18
|
-
def count_translations
|
19
|
-
locales_dir.count { |file| File.file?(file) }
|
20
|
-
end
|
21
|
-
|
22
|
-
def add_translation_files!(with_ru: false, additional: nil)
|
23
|
-
FileUtils.mkdir_p "#{Dir.getwd}/locales/nested"
|
24
|
-
open_and_write('locales/nested/en.yml') { |f| f.write en_data }
|
25
|
-
|
26
|
-
return unless with_ru
|
27
|
-
|
28
|
-
open_and_write('locales/ru.yml') { |f| f.write ru_data }
|
29
|
-
|
30
|
-
return unless additional
|
31
|
-
|
32
|
-
additional.times do |i|
|
33
|
-
data = {'en' => {"key_#{i}" => "value #{i}"}}
|
34
|
-
|
35
|
-
open_and_write("locales/en_#{i}.yml") { |f| f.write data.to_yaml }
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def open_and_write(rel_path, &block)
|
40
|
-
return unless block
|
41
|
-
|
42
|
-
File.open("#{Dir.getwd}/#{rel_path}", 'w+:UTF-8', &block)
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def en_data
|
48
|
-
<<~DATA
|
49
|
-
en:
|
50
|
-
my_key: "My value"
|
51
|
-
nested:
|
52
|
-
key: "Value 2"
|
53
|
-
DATA
|
54
|
-
end
|
55
|
-
|
56
|
-
def ru_data
|
57
|
-
<<~DATA
|
58
|
-
ru_RU:
|
59
|
-
my_key: "Моё значение"
|
60
|
-
nested:
|
61
|
-
key: "Значение 2"
|
62
|
-
DATA
|
63
|
-
end
|
64
|
-
end
|
data/spec/support/spec_addons.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SpecAddons
|
4
|
-
def allow_project_id(obj, value)
|
5
|
-
allow(obj.config).to receive(:project_id).and_return(value)
|
6
|
-
return unless block_given?
|
7
|
-
|
8
|
-
yield
|
9
|
-
expect(obj.config).to have_received(:project_id)
|
10
|
-
end
|
11
|
-
|
12
|
-
def expect_file_exist(path, file)
|
13
|
-
file_path = File.join path, file
|
14
|
-
expect(File.file?(file_path)).to be true
|
15
|
-
end
|
16
|
-
end
|
data/spec/support/vcr.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'vcr'
|
4
|
-
|
5
|
-
VCR.configure do |c|
|
6
|
-
c.ignore_hosts 'codeclimate.com'
|
7
|
-
c.hook_into :faraday
|
8
|
-
c.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
|
9
|
-
c.filter_sensitive_data('<LOKALISE_TOKEN>') do |_i|
|
10
|
-
ENV.fetch('LOKALISE_API_TOKEN')
|
11
|
-
end
|
12
|
-
c.configure_rspec_metadata!
|
13
|
-
end
|