ruby-lokalise-api 4.1.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61d306f7be49970786734ed94a006a6fd37d54e6919c19eb61390f78ad5acff8
4
- data.tar.gz: ea3a5594f544a876386ffa1d50c1a7ff2a842966ca00811df33f54d9e304106e
3
+ metadata.gz: f15e541afdd5e50a0e9317640b872bb04144dcaa717b2390887546b694e308fd
4
+ data.tar.gz: f34bb8a44f1e8552a0bc83b3f4c15d604500d56ecea503260d6423b047b36dcc
5
5
  SHA512:
6
- metadata.gz: 356f32428e0732321e27bc47f2c63022bea36990fb69886fa2daff9d2016bee3f9b80795c326ef4c090f280e58de51d65e8b5e9c6711b3de2be6bb37ae2b2c01
7
- data.tar.gz: 4d5eb8f4fac05abf181fe793d94b2ec5ca759c004d865bbbd085252e4ac49d5abded9056fe0535bb428e1a1308c858a760d4fedd3a05afa7aebb2c8ec1e8ff3f
6
+ metadata.gz: dbbc75876ec0e0c9f5a611d54520585759d77cb4f13a81126d7f9a45c89852331801054ec973f94a6c0b4e58c0a8ea8c6cb88167e5b4a4b3ac9521531b911a87
7
+ data.tar.gz: 2687c66c1fb1ef0012cac50329b87a7c48118cff98a890e06b7764ccce828a850750b6896d5ace0a6810e8cebb86658bbd333794e6bc94cde725c957b1c968f6
@@ -4,7 +4,7 @@
4
4
  2. [Create a topic branch.][branch]
5
5
  3. Implement your feature or bug fix.
6
6
  4. Don't forget to add specs and make sure they pass by running `rspec .`.
7
- 5. Make sure your code complies with the style guide by running `rubocop`. `rubocop -a` can automatically fix most issues for you.
7
+ 5. Make sure your code complies with the style guide by running `rubocop`. `rubocop -a` can automatically fix most issues for you. Run `rubocop -A` to make it more aggressive.
8
8
  6. If necessary, add documentation for your feature or bug fix.
9
9
  7. Commit and push your changes.
10
10
  8. [Submit a pull request.][pr]
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  Official opinionated Ruby interface for the [Lokalise API](https://app.lokalise.com/api2docs/curl/) that represents returned data as Ruby objects.
9
9
 
10
- Looking for a Rails integration? Try the new [lokalise_rails gem](https://github.com/bodrovis/lokalise_rails).
10
+ Looking for a Rails integration? Try the [lokalise_rails gem](https://github.com/bodrovis/lokalise_rails). Also you can use a [lokalise_manager gem](https://github.com/bodrovis/lokalise_manager) which allows to exchange translation files between Lokalise and *any* Ruby script.
11
11
 
12
12
  ## Quickstart
13
13
 
@@ -39,6 +39,12 @@ process = @client.upload_file project_id,
39
39
  process.status
40
40
  ```
41
41
 
42
+ Alternatively instantiate your client with an [OAuth2 token](http://docs.lokalise.com/en/articles/5574713-oauth-2):
43
+
44
+ ```ruby
45
+ @client = Lokalise.oauth_client 'YOUR_OAUTH_TOKEN_HERE'
46
+ ```
47
+
42
48
  ## Usage
43
49
 
44
50
  Detailed documentation can be found at [lokalise.github.io/ruby-lokalise-api](https://lokalise.github.io/ruby-lokalise-api/).
@@ -47,4 +53,4 @@ Detailed documentation can be found at [lokalise.github.io/ruby-lokalise-api](ht
47
53
 
48
54
  This gem is licensed under the [BSD 3 Clause license](https://github.com/lokalise/ruby-lokalise-api/blob/master/LICENSE). Prior to version 4 the license type was MIT.
49
55
 
50
- Copyright (c) [Lokalise team](http://lokalise.co), [Ilya Bodrov](http://bodrovis.tech)
56
+ Copyright (c) [Lokalise team](http://lokalise.co) and [Ilya Bodrov](http://bodrovis.tech)
@@ -23,13 +23,15 @@ require 'ruby-lokalise-api/rest/webhooks'
23
23
 
24
24
  module Lokalise
25
25
  class Client
26
- attr_reader :token
27
- attr_accessor :timeout, :open_timeout
26
+ attr_reader :token, :token_header
27
+ attr_accessor :timeout, :open_timeout, :enable_compression
28
28
 
29
29
  def initialize(token, params = {})
30
30
  @token = token
31
31
  @timeout = params.fetch(:timeout, nil)
32
32
  @open_timeout = params.fetch(:open_timeout, nil)
33
+ @enable_compression = params.fetch(:enable_compression, false)
34
+ @token_header = 'x-api-token'
33
35
  end
34
36
 
35
37
  # rubocop:disable Metrics/ParameterLists
@@ -19,11 +19,12 @@ module Lokalise
19
19
  def initialize(response, params = {})
20
20
  produce_collection_for response
21
21
  populate_pagination_data_for response
22
+ content = response['content']
22
23
  # Project, team id, user id, and branch may not be present in some cases
23
- @project_id = response['content']['project_id']
24
- @team_id = response['content']['team_id']
25
- @user_id = response['content']['user_id']
26
- @branch = response['content']['branch']
24
+ @project_id = content['project_id']
25
+ @team_id = content['team_id']
26
+ @user_id = content['user_id']
27
+ @branch = content['branch']
27
28
  @request_params = params
28
29
  @client = response['client']
29
30
  @path = response['path']
@@ -5,19 +5,25 @@ module Lokalise
5
5
  BASE_URL = 'https://api.lokalise.com/api2/'
6
6
 
7
7
  def connection(client)
8
- options = {
8
+ Faraday.new(options(client), request_params_for(client)) do |faraday|
9
+ faraday.use(:gzip) if client.enable_compression
10
+ faraday.adapter Faraday.default_adapter
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def options(client)
17
+ {
9
18
  headers: {
10
19
  accept: 'application/json',
11
20
  user_agent: "ruby-lokalise-api gem/#{Lokalise::VERSION}",
12
- 'x-api-token': client.token
21
+ client.token_header => client.token
13
22
  },
14
23
  url: BASE_URL
15
24
  }
16
- Faraday.new(options, request_params_for(client)) { |faraday| faraday.adapter Faraday.default_adapter }
17
25
  end
18
26
 
19
- private
20
-
21
27
  # Allows to customize request params per-client
22
28
  def request_params_for(client)
23
29
  {request: {timeout: client.timeout, open_timeout: client.open_timeout}}
@@ -210,7 +210,8 @@
210
210
  "is_fuzzy",
211
211
  "is_reviewed",
212
212
  "words",
213
- "custom_translation_statuses"
213
+ "custom_translation_statuses",
214
+ "task_id"
214
215
  ],
215
216
  "translation_provider": [
216
217
  "provider_id",
@@ -39,7 +39,8 @@ module Lokalise
39
39
  class << self
40
40
  # Create a new error from an HTTP response
41
41
  def from_response(body)
42
- new body['error']['message'].to_s
42
+ msg = body.key?('error') ? body['error']['message'] : body['message']
43
+ new msg.to_s
43
44
  end
44
45
  end
45
46
 
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lokalise
4
+ class OAuthClient < Client
5
+ def initialize(token, params = {})
6
+ super(token, params)
7
+ @token_header = 'Authorization'
8
+ @token = "Bearer #{@token}"
9
+ end
10
+ end
11
+ end
@@ -58,7 +58,8 @@ module Lokalise
58
58
  def respond_with(response, client)
59
59
  body = custom_load response.body
60
60
  uri = Addressable::URI.parse response.env.url
61
- respond_with_error(response.status, body) if body.respond_to?(:has_key?) && body.key?('error')
61
+ status = response.status
62
+ respond_with_error status, body if status.between?(400, 599) || (body.respond_to?(:has_key?) && body.key?('error'))
62
63
  extract_headers_from(response).
63
64
  merge('content' => body,
64
65
  'client' => client,
@@ -74,7 +75,7 @@ module Lokalise
74
75
  end
75
76
 
76
77
  def respond_with_error(code, body)
77
- raise(Lokalise::Error, body['error']) unless Lokalise::Error::ERRORS.key? code
78
+ raise(Lokalise::Error, body['error'] || body) unless Lokalise::Error::ERRORS.key? code
78
79
 
79
80
  raise Lokalise::Error::ERRORS[code].from_response(body)
80
81
  end
@@ -107,10 +107,11 @@ module Lokalise
107
107
  end
108
108
 
109
109
  def produce_resource(model_class, response)
110
+ content = response['content']
110
111
  data_key_singular = data_key_for model_class: model_class
111
- if response['content'].key? data_key_singular
112
- data = response['content'].delete data_key_singular
113
- response['content'].merge! data
112
+ if content.key? data_key_singular
113
+ data = content.delete data_key_singular
114
+ content.merge! data
114
115
  end
115
116
 
116
117
  new response
@@ -156,9 +157,10 @@ module Lokalise
156
157
  # Sometimes there is an `id_key` but it has a value of `null`
157
158
  # (for example when we do not place the actual order but only check its price).
158
159
  # Therefore we must explicitly check if the key is present
159
- return response['content'][id_key] if response['content'].key?(id_key)
160
+ content = response['content']
161
+ return content[id_key] if content.key?(id_key)
160
162
 
161
- response['content'][data_key][id_key]
163
+ content[data_key][id_key]
162
164
  end
163
165
 
164
166
  # Store all resources attributes under the corresponding instance variables.
@@ -6,7 +6,8 @@ module Lokalise
6
6
  supports :update, :destroy, [:reload_data, '', :find]
7
7
 
8
8
  def merge(params = {})
9
- self.class.merge @client, self.class.endpoint(project_id, branch_id, :merge), params
9
+ klass = self.class
10
+ klass.merge @client, klass.endpoint(project_id, branch_id, :merge), params
10
11
  end
11
12
 
12
13
  class << self
@@ -22,7 +22,8 @@ module Lokalise
22
22
  c_r Lokalise::Resources::File, :download, [project_id, 'download'], params
23
23
  end
24
24
 
25
- # Imports translation file to the given project. File data must base64-encoded
25
+ # Imports translation file to the given project. File data must base64-encoded.
26
+ # To encode your data in Base64, use `Base64.strict_encode64()` method.
26
27
  #
27
28
  # @see https://app.lokalise.com/api2docs/curl/#transition-upload-a-file-post
28
29
  # @return [Hash]
@@ -47,9 +47,7 @@ module Lokalise
47
47
  #
48
48
  # @return [Array<String>]
49
49
  def attributes_for(klass)
50
- @attributes ||= begin
51
- YAML.load_file(File.expand_path('../data/attributes.json', __dir__)).freeze
52
- end
50
+ @attributes ||= YAML.load_file(File.expand_path('../data/attributes.json', __dir__)).freeze
53
51
 
54
52
  name = unify klass.name.snakecase
55
53
  @attributes[name]
@@ -22,6 +22,6 @@ class String
22
22
  end
23
23
 
24
24
  def remove_trailing_slash
25
- gsub %r{/\z}, ''
25
+ delete_suffix '/'
26
26
  end
27
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lokalise
4
- VERSION = '4.1.0'
4
+ VERSION = '4.4.0'
5
5
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'faraday'
4
+ require 'faraday_middleware'
4
5
  require 'yaml'
5
6
  require 'addressable'
6
7
 
@@ -62,6 +63,7 @@ require 'ruby-lokalise-api/collections/custom_translation_status'
62
63
  require 'ruby-lokalise-api/collections/webhook'
63
64
 
64
65
  require 'ruby-lokalise-api/client'
66
+ require 'ruby-lokalise-api/oauth_client'
65
67
 
66
68
  module Lokalise
67
69
  class << self
@@ -78,5 +80,19 @@ module Lokalise
78
80
  def reset_client!
79
81
  @client = nil
80
82
  end
83
+
84
+ # Initializes a new OAuthClient object
85
+ #
86
+ # @return [Lokalise::OAuthClient]
87
+ # @param token [String]
88
+ # @param params [Hash]
89
+ def oauth_client(token, params = {})
90
+ @oauth_client ||= Lokalise::OAuthClient.new token, params
91
+ end
92
+
93
+ # Reset the currently set OAuth client
94
+ def reset_oauth_client!
95
+ @oauth_client = nil
96
+ end
81
97
  end
82
98
  end
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency 'addressable', '~> 2.5'
26
26
  spec.add_dependency 'faraday', '~> 1.0'
27
+ spec.add_dependency 'faraday_middleware', '~> 1.0'
27
28
  spec.add_dependency 'json', '>= 1.8.0'
28
29
 
29
30
  spec.add_development_dependency 'codecov', '~> 0.1'
@@ -3,6 +3,9 @@
3
3
  RSpec.describe Lokalise::Connection do
4
4
  include described_class
5
5
 
6
+ let(:project_id) { '803826145ba90b42d5d860.46800099' }
7
+ let(:key_id) { 44_596_059 }
8
+
6
9
  before { Lokalise.reset_client! }
7
10
 
8
11
  after do
@@ -10,11 +13,18 @@ RSpec.describe Lokalise::Connection do
10
13
  Faraday.default_adapter = :net_http
11
14
  end
12
15
 
13
- it 'timeouts should not be set by default but the token must be present' do
16
+ it 'Authorization header must be present for OAuth client' do
17
+ conn = connection test_oauth_client
18
+ expect(conn.headers['Authorization']).to eq("Bearer #{test_client.token}")
19
+ expect(conn.headers['X-api-token']).to be_nil
20
+ end
21
+
22
+ it 'timeouts and compression should not be set by default but the token must be present' do
14
23
  conn = connection test_client
15
24
  expect(conn.options.timeout).to be_nil
16
25
  expect(conn.options.open_timeout).to be_nil
17
26
  expect(conn.headers['X-api-token']).to eq(test_client.token)
27
+ expect(conn.builder.handlers).not_to include(FaradayMiddleware::Gzip)
18
28
  end
19
29
 
20
30
  it 'allows to customize timeouts' do
@@ -42,4 +52,38 @@ RSpec.describe Lokalise::Connection do
42
52
  expect(another_conn.builder.adapter).to eq(Faraday::Adapter::Excon)
43
53
  expect(conn.builder.adapter).to eq(Faraday::Adapter::NetHttp)
44
54
  end
55
+
56
+ it 'allows to customize compression' do
57
+ custom_client = Lokalise.client(ENV['LOKALISE_API_TOKEN'], enable_compression: true)
58
+ conn = connection custom_client
59
+ expect(conn.headers['X-api-token']).to eq(custom_client.token)
60
+ expect(conn.builder.handlers).to include(FaradayMiddleware::Gzip)
61
+ end
62
+
63
+ it 'is possible to enable gzip compression' do
64
+ gzip_client = Lokalise.client(ENV['LOKALISE_API_TOKEN'], enable_compression: true)
65
+ keys = VCR.use_cassette('all_keys_gzip') do
66
+ gzip_client.keys project_id
67
+ end.collection
68
+
69
+ expect(keys.first.key_id).to eq(key_id)
70
+ end
71
+
72
+ it 'is possible to disable gzip compression' do
73
+ no_gzip_client = Lokalise.client(ENV['LOKALISE_API_TOKEN'], enable_compression: false)
74
+ keys = VCR.use_cassette('all_keys_no_gzip') do
75
+ no_gzip_client.keys project_id
76
+ end.collection
77
+
78
+ expect(keys.first.key_id).to eq(key_id)
79
+ end
80
+
81
+ it 'gzip compression is off by default' do
82
+ default_gzip_client = Lokalise.client(ENV['LOKALISE_API_TOKEN'])
83
+ keys = VCR.use_cassette('all_keys_default_gzip') do
84
+ default_gzip_client.keys project_id
85
+ end.collection
86
+
87
+ expect(keys.first.key_id).to eq(key_id)
88
+ end
45
89
  end
@@ -15,6 +15,14 @@ RSpec.describe Lokalise::Error do
15
15
  end.to raise_error(described_class)
16
16
  end
17
17
 
18
+ it 'handles an exception when the response does not contain an error key' do
19
+ expect do
20
+ VCR.use_cassette('error_no_error_key') do
21
+ get 'projects', Lokalise.client('invalid')
22
+ end
23
+ end.to raise_error(Lokalise::Error::BadRequest)
24
+ end
25
+
18
26
  it 'raises BadRequest when API token is invalid' do
19
27
  expect do
20
28
  VCR.use_cassette('error_invalid_token') do
@@ -59,26 +59,27 @@ RSpec.describe Lokalise::Client do
59
59
 
60
60
  specify '#translation' do
61
61
  translation = VCR.use_cassette('translation') do
62
- test_client.translation project_id, translation_id
62
+ test_client.translation project_id, 304_581_218
63
63
  end
64
64
 
65
- expect(translation.translation_id).to eq(translation_id)
66
- expect(translation.key_id).to eq(15_571_975)
67
- expect(translation.language_iso).to eq('en')
68
- expect(translation.modified_at).to eq('2019-03-26 16:41:31 (Etc/UTC)')
69
- expect(translation.modified_at_timestamp).to eq(1_553_618_491)
65
+ expect(translation.translation_id).to eq(304_581_218)
66
+ expect(translation.key_id).to eq(44_596_059)
67
+ expect(translation.language_iso).to eq('ru')
68
+ expect(translation.modified_at).to eq('2020-05-15 10:44:42 (Etc/UTC)')
69
+ expect(translation.modified_at_timestamp).to eq(1_589_539_482)
70
70
  expect(translation.modified_by).to eq(20_181)
71
71
  expect(translation.modified_by_email).to eq('bodrovis@protonmail.com')
72
- expect(translation.translation).to eq('RSpec is a testing suite')
72
+ expect(translation.translation).to eq('Сообщение')
73
73
  expect(translation.is_fuzzy).to eq(false)
74
74
  expect(translation.is_reviewed).to eq(false)
75
- expect(translation.words).to eq(5)
75
+ expect(translation.words).to eq(1)
76
76
  expect(translation.custom_translation_statuses).to eq([])
77
+ expect(translation.task_id).to be_nil
77
78
  end
78
79
 
79
80
  specify '#reload_data' do
80
81
  translation = VCR.use_cassette('translation') do
81
- test_client.translation project_id, translation_id
82
+ test_client.translation project_id, 304_581_218
82
83
  end
83
84
 
84
85
  reloaded_translation = VCR.use_cassette('translation') do
@@ -6,6 +6,7 @@ RSpec.describe Lokalise do
6
6
  expect(test_client.token).to eq(ENV['LOKALISE_API_TOKEN'])
7
7
  expect(test_client.timeout).to be_nil
8
8
  expect(test_client.open_timeout).to be_nil
9
+ expect(test_client.enable_compression).to be false
9
10
  end
10
11
 
11
12
  specify '.reset_client!' do
@@ -15,6 +16,21 @@ RSpec.describe Lokalise do
15
16
  expect(current_client).to be_nil
16
17
  end
17
18
 
19
+ specify '.oauth_client' do
20
+ expect(test_oauth_client).to be_an_instance_of(Lokalise::OAuthClient)
21
+ expect(test_oauth_client.token).to eq("Bearer #{ENV['LOKALISE_API_TOKEN']}")
22
+ expect(test_oauth_client.timeout).to be_nil
23
+ expect(test_oauth_client.open_timeout).to be_nil
24
+ expect(test_oauth_client.enable_compression).to be false
25
+ end
26
+
27
+ specify '.reset_oauth_client!' do
28
+ expect(test_oauth_client).to be_an_instance_of(Lokalise::OAuthClient)
29
+ described_class.reset_oauth_client!
30
+ current_oauth_client = described_class.instance_variable_get '@oauth_client'
31
+ expect(current_oauth_client).to be_nil
32
+ end
33
+
18
34
  context 'with client params' do
19
35
  before { described_class.reset_client! }
20
36
 
@@ -29,5 +45,10 @@ RSpec.describe Lokalise do
29
45
  custom_client = described_class.client(ENV['LOKALISE_API_TOKEN'], open_timeout: 100)
30
46
  expect(custom_client.open_timeout).to eq(100)
31
47
  end
48
+
49
+ it 'is possible to customize compression' do
50
+ custom_client = described_class.client(ENV['LOKALISE_API_TOKEN'], enable_compression: true)
51
+ expect(custom_client.enable_compression).to be true
52
+ end
32
53
  end
33
54
  end
@@ -4,4 +4,8 @@ module TestClient
4
4
  def test_client(token = nil, params = {})
5
5
  Lokalise.client(token || ENV['LOKALISE_API_TOKEN'], params)
6
6
  end
7
+
8
+ def test_oauth_client(token = nil, params = {})
9
+ Lokalise.oauth_client(token || ENV['LOKALISE_API_TOKEN'], params)
10
+ end
7
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lokalise-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Bodrov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-01 00:00:00.000000000 Z
11
+ date: 2021-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday_middleware
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: json
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -238,6 +252,7 @@ files:
238
252
  - lib/ruby-lokalise-api/data/attributes.json
239
253
  - lib/ruby-lokalise-api/error.rb
240
254
  - lib/ruby-lokalise-api/json_handler.rb
255
+ - lib/ruby-lokalise-api/oauth_client.rb
241
256
  - lib/ruby-lokalise-api/request.rb
242
257
  - lib/ruby-lokalise-api/resources/base.rb
243
258
  - lib/ruby-lokalise-api/resources/branch.rb
@@ -334,7 +349,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
334
349
  - !ruby/object:Gem::Version
335
350
  version: '0'
336
351
  requirements: []
337
- rubygems_version: 3.2.11
352
+ rubygems_version: 3.2.26
338
353
  signing_key:
339
354
  specification_version: 4
340
355
  summary: Ruby interface to the Lokalise API
@@ -356,11 +371,11 @@ test_files:
356
371
  - spec/lib/ruby-lokalise-api/rest/screenshots_spec.rb
357
372
  - spec/lib/ruby-lokalise-api/rest/snapshots_spec.rb
358
373
  - spec/lib/ruby-lokalise-api/rest/tasks_spec.rb
359
- - spec/lib/ruby-lokalise-api/rest/teams_spec.rb
360
- - spec/lib/ruby-lokalise-api/rest/team_users_spec.rb
361
374
  - spec/lib/ruby-lokalise-api/rest/team_user_groups_spec.rb
362
- - spec/lib/ruby-lokalise-api/rest/translations_spec.rb
375
+ - spec/lib/ruby-lokalise-api/rest/team_users_spec.rb
376
+ - spec/lib/ruby-lokalise-api/rest/teams_spec.rb
363
377
  - spec/lib/ruby-lokalise-api/rest/translation_providers_spec.rb
378
+ - spec/lib/ruby-lokalise-api/rest/translations_spec.rb
364
379
  - spec/lib/ruby-lokalise-api/rest/webhooks_spec.rb
365
380
  - spec/lib/ruby-lokalise-api/utils/snakecase_spec.rb
366
381
  - spec/lib/ruby-lokalise-api_spec.rb