deepl-rb 3.6.0 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +27 -0
  3. data/.github/workflows/add_issues_to_kanban.yml +16 -0
  4. data/.gitlab-ci.yml +172 -0
  5. data/.rubocop.yml +51 -0
  6. data/CHANGELOG.md +22 -3
  7. data/CODE_OF_CONDUCT.md +132 -0
  8. data/CONTRIBUTING.md +37 -0
  9. data/Gemfile +24 -0
  10. data/Rakefile +56 -0
  11. data/SECURITY.md +58 -0
  12. data/VERSION +1 -0
  13. data/deepl-rb.gemspec +140 -0
  14. data/lib/deepl/api.rb +22 -0
  15. data/lib/deepl/configuration.rb +59 -0
  16. data/lib/deepl/constants/base_constant.rb +18 -0
  17. data/lib/deepl/constants/formality.rb +16 -0
  18. data/lib/deepl/constants/model_type.rb +14 -0
  19. data/lib/deepl/constants/split_sentences.rb +14 -0
  20. data/lib/deepl/constants/tag_handling.rb +13 -0
  21. data/lib/deepl/constants/tone.rb +20 -0
  22. data/lib/deepl/constants/writing_style.rb +20 -0
  23. data/lib/deepl/document_api.rb +121 -0
  24. data/lib/deepl/exceptions/authorization_failed.rb +14 -0
  25. data/lib/deepl/exceptions/bad_request.rb +16 -0
  26. data/lib/deepl/exceptions/document_translation_error.rb +15 -0
  27. data/lib/deepl/exceptions/error.rb +14 -0
  28. data/lib/deepl/exceptions/limit_exceeded.rb +18 -0
  29. data/lib/deepl/exceptions/not_found.rb +16 -0
  30. data/lib/deepl/exceptions/not_supported.rb +14 -0
  31. data/lib/deepl/exceptions/quota_exceeded.rb +14 -0
  32. data/lib/deepl/exceptions/request_entity_too_large.rb +15 -0
  33. data/lib/deepl/exceptions/request_error.rb +21 -0
  34. data/lib/deepl/exceptions/server_error.rb +18 -0
  35. data/lib/deepl/glossary_api.rb +38 -0
  36. data/lib/deepl/requests/base.rb +196 -0
  37. data/lib/deepl/requests/document/download.rb +44 -0
  38. data/lib/deepl/requests/document/get_status.rb +44 -0
  39. data/lib/deepl/requests/document/upload.rb +74 -0
  40. data/lib/deepl/requests/glossary/create.rb +59 -0
  41. data/lib/deepl/requests/glossary/destroy.rb +37 -0
  42. data/lib/deepl/requests/glossary/entries.rb +37 -0
  43. data/lib/deepl/requests/glossary/find.rb +38 -0
  44. data/lib/deepl/requests/glossary/language_pairs.rb +38 -0
  45. data/lib/deepl/requests/glossary/list.rb +37 -0
  46. data/lib/deepl/requests/languages.rb +37 -0
  47. data/lib/deepl/requests/rephrase.rb +55 -0
  48. data/lib/deepl/requests/style_rule/list.rb +59 -0
  49. data/lib/deepl/requests/translate.rb +93 -0
  50. data/lib/deepl/requests/usage.rb +33 -0
  51. data/lib/deepl/resources/base.rb +17 -0
  52. data/lib/deepl/resources/document_handle.rb +57 -0
  53. data/lib/deepl/resources/document_translation_status.rb +54 -0
  54. data/lib/deepl/resources/glossary.rb +28 -0
  55. data/lib/deepl/resources/language.rb +30 -0
  56. data/lib/deepl/resources/language_pair.rb +23 -0
  57. data/lib/deepl/resources/style_rule.rb +85 -0
  58. data/lib/deepl/resources/text.rb +24 -0
  59. data/lib/deepl/resources/usage.rb +27 -0
  60. data/lib/deepl/style_rule_api.rb +17 -0
  61. data/lib/deepl/utils/backoff_timer.rb +46 -0
  62. data/lib/deepl/utils/exception_builder.rb +34 -0
  63. data/lib/deepl.rb +158 -0
  64. data/lib/http_client_options.rb +22 -0
  65. data/lib/version.rb +8 -0
  66. data/spec/api/api_spec.rb +20 -0
  67. data/spec/api/configuration_spec.rb +122 -0
  68. data/spec/api/deepl_spec.rb +460 -0
  69. data/spec/constants/constants_spec.rb +158 -0
  70. data/spec/fixtures/vcr_cassettes/deepl_document.yml +95 -0
  71. data/spec/fixtures/vcr_cassettes/deepl_document_download.yml +1214 -0
  72. data/spec/fixtures/vcr_cassettes/deepl_glossaries.yml +1163 -0
  73. data/spec/fixtures/vcr_cassettes/deepl_languages.yml +54 -0
  74. data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +87 -0
  75. data/spec/fixtures/vcr_cassettes/deepl_translate.yml +358 -0
  76. data/spec/fixtures/vcr_cassettes/deepl_usage.yml +129 -0
  77. data/spec/fixtures/vcr_cassettes/glossaries.yml +1702 -0
  78. data/spec/fixtures/vcr_cassettes/languages.yml +229 -0
  79. data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +401 -0
  80. data/spec/fixtures/vcr_cassettes/style_rules.yml +92 -0
  81. data/spec/fixtures/vcr_cassettes/translate_texts.yml +10630 -0
  82. data/spec/fixtures/vcr_cassettes/usage.yml +171 -0
  83. data/spec/integration_tests/document_api_spec.rb +178 -0
  84. data/spec/integration_tests/integration_test_utils.rb +170 -0
  85. data/spec/integration_tests/style_rule_api_spec.rb +79 -0
  86. data/spec/requests/extra_body_parameters_types_spec.rb +82 -0
  87. data/spec/requests/glossary/create_spec.rb +65 -0
  88. data/spec/requests/glossary/destroy_spec.rb +66 -0
  89. data/spec/requests/glossary/entries_spec.rb +62 -0
  90. data/spec/requests/glossary/find_spec.rb +68 -0
  91. data/spec/requests/glossary/language_pairs_spec.rb +40 -0
  92. data/spec/requests/glossary/list_spec.rb +54 -0
  93. data/spec/requests/languages_spec.rb +68 -0
  94. data/spec/requests/rephrase_spec.rb +172 -0
  95. data/spec/requests/translate_spec.rb +493 -0
  96. data/spec/requests/usage_spec.rb +43 -0
  97. data/spec/resources/glossary_spec.rb +38 -0
  98. data/spec/resources/language_pair_spec.rb +23 -0
  99. data/spec/resources/language_spec.rb +45 -0
  100. data/spec/resources/text_spec.rb +23 -0
  101. data/spec/resources/usage_spec.rb +35 -0
  102. data/spec/spec_helper.rb +92 -0
  103. metadata +102 -2
@@ -0,0 +1,55 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Requests
8
+ class Rephrase < Base
9
+ attr_reader :text, :target_lang, :writing_style, :tone
10
+
11
+ def initialize(api, text, target_lang = nil, writing_style = nil, tone = nil, options = {}) # rubocop:disable Metrics/ParameterLists
12
+ super(api, options)
13
+ @text = text
14
+ @target_lang = target_lang
15
+ @writing_style = writing_style
16
+ @tone = tone
17
+ end
18
+
19
+ def request # rubocop:disable Metrics/AbcSize
20
+ text_arrayified = text.is_a?(Array) ? text : [text]
21
+ payload = { text: text_arrayified }
22
+ payload[:target_lang] = target_lang unless target_lang.nil?
23
+ payload[:writing_style] = writing_style unless writing_style.nil?
24
+ payload[:tone] = tone unless tone.nil?
25
+ build_texts(*execute_request_with_retries(post_request(payload)))
26
+ end
27
+
28
+ def details
29
+ "HTTP Headers: #{headers}\nPayload #{{ text: text, target_lang: target_lang,
30
+ writing_style: writing_style, tone: tone }}"
31
+ end
32
+
33
+ def to_s
34
+ "POST #{uri.request_uri}"
35
+ end
36
+
37
+ private
38
+
39
+ def build_texts(request, response)
40
+ data = JSON.parse(response.body)
41
+
42
+ texts = data['improvements'].map do |improvement|
43
+ Resources::Text.new(improvement['text'], improvement['detected_source_language'], nil,
44
+ request, response)
45
+ end
46
+
47
+ texts.size == 1 ? texts.first : texts
48
+ end
49
+
50
+ def path
51
+ 'write/rephrase'
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,59 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Requests
8
+ module StyleRule
9
+ class List < Base
10
+ def initialize(api, options = {})
11
+ super
12
+ end
13
+
14
+ def request
15
+ build_style_rule_list(*execute_request_with_retries(get_request))
16
+ end
17
+
18
+ def to_s
19
+ "GET #{uri.request_uri}"
20
+ end
21
+
22
+ private
23
+
24
+ def get_request # rubocop:disable Naming/AccessorMethodName
25
+ http_headers = add_json_content_type(headers)
26
+ Net::HTTP::Get.new(uri.request_uri, http_headers)
27
+ end
28
+
29
+ def build_style_rule_list(request, response)
30
+ data = JSON.parse(response.body)
31
+ data['style_rules'].map do |style_rule|
32
+ Resources::StyleRule.new(style_rule, request, response)
33
+ end
34
+ end
35
+
36
+ def uri
37
+ @uri ||= begin
38
+ base_uri = URI("#{host}/v3/#{path}")
39
+ query_string = build_query_string
40
+ base_uri.query = query_string unless query_string.empty?
41
+ base_uri
42
+ end
43
+ end
44
+
45
+ def build_query_string
46
+ params = {}
47
+ params['page'] = option(:page).to_s if option?(:page)
48
+ params['page_size'] = option(:page_size).to_s if option?(:page_size)
49
+ params['detailed'] = option(:detailed).to_s.downcase if option?(:detailed)
50
+ URI.encode_www_form(params)
51
+ end
52
+
53
+ def path
54
+ 'style_rules'
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,93 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Requests
8
+ class Translate < Base
9
+ STRING_TO_BOOLEAN_MAP = { '1' => true, '0' => false }.freeze
10
+ BOOLEAN_TO_STRING_MAP = { true => '1', false => '0' }.freeze
11
+ STRING_TO_BOOLEAN_CONVERSION = ->(value) { STRING_TO_BOOLEAN_MAP[value] }
12
+ BOOLEAN_TO_STRING_CONVERSION = ->(value) { BOOLEAN_TO_STRING_MAP[value] }
13
+ STRING_TO_ARRAY_CONVERSION = lambda { |value|
14
+ if value.nil?
15
+ nil
16
+ else
17
+ (value.is_a?(Array) ? value : value.split(','))
18
+ end
19
+ }.freeze
20
+ OPTIONS_CONVERSIONS = {
21
+ split_sentences: BOOLEAN_TO_STRING_CONVERSION,
22
+ preserve_formatting: STRING_TO_BOOLEAN_CONVERSION,
23
+ outline_detection: STRING_TO_BOOLEAN_CONVERSION,
24
+ splitting_tags: STRING_TO_ARRAY_CONVERSION,
25
+ non_splitting_tags: STRING_TO_ARRAY_CONVERSION,
26
+ ignore_tags: STRING_TO_ARRAY_CONVERSION,
27
+ custom_instructions: STRING_TO_ARRAY_CONVERSION
28
+ }.freeze
29
+
30
+ attr_reader :text, :source_lang, :target_lang, :ignore_tags, :splitting_tags,
31
+ :non_splitting_tags, :model_type, :custom_instructions, :tag_handling_version
32
+
33
+ def initialize(api, text, source_lang, target_lang, options = {})
34
+ super(api, options)
35
+ @text = text
36
+ @source_lang = source_lang
37
+ @target_lang = target_lang
38
+
39
+ tweak_parameters!
40
+ end
41
+
42
+ def request # rubocop:disable Metrics/MethodLength
43
+ text_arrayified = text.is_a?(Array) ? text : [text]
44
+ payload = { text: text_arrayified, source_lang: source_lang, target_lang: target_lang }
45
+
46
+ if option?(:style_rule)
47
+ style_rule = option(:style_rule)
48
+ payload[:style_id] = if style_rule.is_a?(DeepL::Resources::StyleRule)
49
+ style_rule.style_id
50
+ else
51
+ style_rule.to_s
52
+ end
53
+ end
54
+
55
+ build_texts(*execute_request_with_retries(post_request(payload)))
56
+ end
57
+
58
+ def details
59
+ "HTTP Headers: #{headers}\nPayload #{{ text: text, source_lang: source_lang,
60
+ target_lang: target_lang }}"
61
+ end
62
+
63
+ def to_s
64
+ "POST #{uri.request_uri}"
65
+ end
66
+
67
+ private
68
+
69
+ def tweak_parameters!
70
+ OPTIONS_CONVERSIONS.each do |param, converter|
71
+ next unless option?(param) && !converter[option(param)].nil?
72
+
73
+ set_option(param, converter[option(param)])
74
+ end
75
+ end
76
+
77
+ def build_texts(request, response)
78
+ data = JSON.parse(response.body)
79
+
80
+ texts = data['translations'].map do |translation|
81
+ Resources::Text.new(translation['text'], translation['detected_source_language'],
82
+ translation['model_type_used'], request, response)
83
+ end
84
+
85
+ texts.size == 1 ? texts.first : texts
86
+ end
87
+
88
+ def path
89
+ 'translate'
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Requests
8
+ class Usage < Base
9
+ def initialize(api, options = {})
10
+ super
11
+ end
12
+
13
+ def request
14
+ build_usage(*execute_request_with_retries(get_request))
15
+ end
16
+
17
+ def to_s
18
+ "GET #{uri.request_uri}"
19
+ end
20
+
21
+ private
22
+
23
+ def build_usage(request, response)
24
+ data = JSON.parse(response.body)
25
+ Resources::Usage.new(data['character_count'], data['character_limit'], request, response)
26
+ end
27
+
28
+ def path
29
+ 'usage'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class Base
9
+ attr_reader :request, :response
10
+
11
+ def initialize(request, response)
12
+ @request = request
13
+ @response = response
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ # Copyright 2024 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class DocumentHandle < Base
9
+ attr_reader :document_id, :document_key
10
+
11
+ def initialize(document_id, document_key, *args)
12
+ super(*args)
13
+
14
+ @document_id = document_id
15
+ @document_key = document_key
16
+ end
17
+
18
+ def to_s
19
+ "DocumentHandle: ID: #{document_id} - Key: #{document_key}"
20
+ end
21
+
22
+ ##
23
+ # For this DocumentHandle, waits until the document translation is finished and returns the
24
+ # final status of the document.
25
+ #
26
+ # @return [DeepL::Resources::DocumentTranslationStatus] Final status of the document
27
+ # translation.
28
+
29
+ def wait_until_document_translation_finished
30
+ doc_status = nil
31
+ max_tries = max_doc_status_queries
32
+ num_tries = 0
33
+ loop do
34
+ num_tries += 1
35
+ sleep(calculate_waiting_time(doc_status)) unless doc_status.nil?
36
+ doc_status = DeepL.document.get_status(self)
37
+ break if doc_status.finished? || num_tries > max_tries
38
+ end
39
+ doc_status
40
+ end
41
+
42
+ private
43
+
44
+ def calculate_waiting_time(_resp)
45
+ # ignore _resp.seconds_remaining for now, while it is unreliable
46
+ 5
47
+ end
48
+
49
+ def max_doc_status_queries
50
+ configured_value = DeepL.configuration.max_doc_status_queries
51
+ return configured_value unless configured_value.nil?
52
+
53
+ 10
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,54 @@
1
+ # Copyright 2024 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class DocumentTranslationStatus < Base
9
+ attr_reader :document_id, :status, :seconds_remaining, :billed_characters, :error_message
10
+
11
+ def initialize(response, *args)
12
+ super(*args)
13
+
14
+ @document_id = response['document_id']
15
+ @status = response['status']
16
+ @seconds_remaining = response['seconds_remaining']
17
+ @billed_characters = response['billed_characters']
18
+ @error_message = response['error_message']
19
+ end
20
+
21
+ def to_s
22
+ "DocumentTranslationStatus: ID: #{document_id} - Status: #{status} " \
23
+ "- Error message: #{error_message}"
24
+ end
25
+
26
+ ##
27
+ # Checks if the translation finished successfully
28
+ #
29
+ # @return [true] if so
30
+
31
+ def successful?
32
+ status == 'done'
33
+ end
34
+
35
+ ##
36
+ # Checks if there was an error during translation
37
+ #
38
+ # @return [true] if so
39
+
40
+ def error?
41
+ status == 'error'
42
+ end
43
+
44
+ ##
45
+ # Checks if the translation process terminated. Note that this could be due to an error as
46
+ # well, but means no further waiting is necessary.
47
+ #
48
+ # @return [true] if so
49
+ def finished?
50
+ successful? || error?
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ # Copyright 2022 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class Glossary < Base
9
+ attr_reader :id, :name, :ready, :source_lang, :target_lang, :creation_time, :entry_count
10
+
11
+ def initialize(glossary, *args)
12
+ super(*args)
13
+
14
+ @id = glossary['glossary_id']
15
+ @name = glossary['name']
16
+ @ready = glossary['ready']
17
+ @source_lang = glossary['source_lang']
18
+ @target_lang = glossary['target_lang']
19
+ @creation_time = glossary['creation_time']
20
+ @entry_count = glossary['entry_count']
21
+ end
22
+
23
+ def to_s
24
+ "#{id} - #{name}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # Copyright 2021 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class Language < Base
9
+ attr_reader :code, :name
10
+
11
+ def initialize(code, name, supports_formality, *args)
12
+ super(*args)
13
+
14
+ @code = code
15
+ @name = name
16
+ @supports_formality = supports_formality
17
+ end
18
+
19
+ def to_s
20
+ "#{code} - #{name}"
21
+ end
22
+
23
+ def supports_formality?
24
+ return @supports_formality unless @supports_formality.nil?
25
+
26
+ raise Exceptions::NotSupported, 'Support formality is only available on target languages'
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright 2022 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class LanguagePair < Base
9
+ attr_reader :source_lang, :target_lang
10
+
11
+ def initialize(source_lang, target_lang, *args)
12
+ super(*args)
13
+
14
+ @source_lang = source_lang
15
+ @target_lang = target_lang
16
+ end
17
+
18
+ def to_s
19
+ "#{source_lang} - #{target_lang}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,85 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ require 'time'
7
+
8
+ module DeepL
9
+ module Resources
10
+ class ConfiguredRules
11
+ attr_reader :dates_and_times, :formatting, :numbers, :punctuation,
12
+ :spelling_and_grammar, :style_and_tone, :vocabulary
13
+
14
+ def initialize(configured_rules_data)
15
+ @dates_and_times = extract_rule(configured_rules_data, 'dates_and_times')
16
+ @formatting = extract_rule(configured_rules_data, 'formatting')
17
+ @numbers = extract_rule(configured_rules_data, 'numbers')
18
+ @punctuation = extract_rule(configured_rules_data, 'punctuation')
19
+ @spelling_and_grammar = extract_rule(configured_rules_data, 'spelling_and_grammar')
20
+ @style_and_tone = extract_rule(configured_rules_data, 'style_and_tone')
21
+ @vocabulary = extract_rule(configured_rules_data, 'vocabulary')
22
+ end
23
+
24
+ private
25
+
26
+ def extract_rule(data, key)
27
+ data[key] || {}
28
+ end
29
+ end
30
+
31
+ class CustomInstruction
32
+ attr_reader :label, :prompt, :source_language
33
+
34
+ def initialize(custom_instruction_data)
35
+ @label = custom_instruction_data['label']
36
+ @prompt = custom_instruction_data['prompt']
37
+ @source_language = custom_instruction_data['source_language']
38
+ end
39
+ end
40
+
41
+ class StyleRule < Base
42
+ attr_reader :style_id, :name, :creation_time, :updated_time, :language, :version,
43
+ :configured_rules, :custom_instructions
44
+
45
+ def initialize(style_rule, *args)
46
+ super(*args)
47
+ extract_basic_fields(style_rule)
48
+ extract_configured_rules(style_rule)
49
+ extract_custom_instructions(style_rule)
50
+ end
51
+
52
+ private
53
+
54
+ def extract_basic_fields(style_rule)
55
+ @style_id = style_rule['style_id']
56
+ @name = style_rule['name']
57
+ @creation_time = parse_time(style_rule['creation_time'])
58
+ @updated_time = parse_time(style_rule['updated_time'])
59
+ @language = style_rule['language']
60
+ @version = style_rule['version']
61
+ end
62
+
63
+ def extract_configured_rules(style_rule)
64
+ configured_rules_data = style_rule['configured_rules']
65
+ @configured_rules = configured_rules_data ? ConfiguredRules.new(configured_rules_data) : nil
66
+ end
67
+
68
+ def extract_custom_instructions(style_rule)
69
+ @custom_instructions = style_rule['custom_instructions']&.map do |ci|
70
+ CustomInstruction.new(ci)
71
+ end
72
+ end
73
+
74
+ def to_s
75
+ "#{style_id} - #{name}"
76
+ end
77
+
78
+ def parse_time(time_string)
79
+ return nil unless time_string
80
+
81
+ Time.parse(time_string)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class Text < Base
9
+ attr_reader :text, :detected_source_language, :model_type_used
10
+
11
+ def initialize(text, detected_source_language, model_type_used, *args)
12
+ super(*args)
13
+
14
+ @text = text
15
+ @detected_source_language = detected_source_language
16
+ @model_type_used = model_type_used
17
+ end
18
+
19
+ def to_s
20
+ text
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class Usage < Base
9
+ attr_reader :character_count, :character_limit
10
+
11
+ def initialize(character_count, character_limit, *args)
12
+ super(*args)
13
+
14
+ @character_count = character_count
15
+ @character_limit = character_limit
16
+ end
17
+
18
+ def to_s
19
+ "#{character_count} / #{character_limit}"
20
+ end
21
+
22
+ def quota_exceeded?
23
+ character_count >= character_limit
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright 2025 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ class StyleRuleApi
8
+ def initialize(api, options = {})
9
+ @api = api
10
+ @options = options
11
+ end
12
+
13
+ def list(options = {})
14
+ DeepL::Requests::StyleRule::List.new(@api, options).request
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ # Copyright 2024 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Utils
8
+ class BackoffTimer
9
+ # Implements exponential-backoff strategy.
10
+ # This strategy is based on the GRPC Connection Backoff Protocol:
11
+ # https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
12
+
13
+ BACKOFF_INITIAL = 1.0
14
+ BACKOFF_MAX = 120.0
15
+ BACKOFF_JITTER = 0.23
16
+ BACKOFF_MULTIPLIER = 1.6
17
+
18
+ attr_reader :num_retries
19
+
20
+ def initialize(min_connection_timeout = 10.0)
21
+ @num_retries = 0
22
+ @backoff = BACKOFF_INITIAL
23
+ @deadline = Time.now.to_f + @backoff
24
+ @min_connection_timeout = min_connection_timeout
25
+ end
26
+
27
+ def current_request_timeout
28
+ [time_until_deadline, @min_connection_timeout].max
29
+ end
30
+
31
+ def time_until_deadline
32
+ [@deadline - Time.now.to_f, 0.0].max
33
+ end
34
+
35
+ def sleep_until_deadline
36
+ sleep(time_until_deadline)
37
+ # Apply multiplier to current backoff time
38
+ @backoff = [@backoff * BACKOFF_MULTIPLIER, BACKOFF_MAX].min
39
+ # Get deadline by applying jitter as a proportion of backoff:
40
+ # if jitter is 0.1, then multiply backoff by random value in [0.9, 1.1]
41
+ @deadline = Time.now.to_f + (@backoff * (1 + (BACKOFF_JITTER * rand(-1.0..1.0))))
42
+ @num_retries += 1
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright 2022 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Utils
8
+ class ExceptionBuilder
9
+ attr_reader :request, :response
10
+
11
+ def self.error_class_from_response_code(code) # rubocop:disable Metrics/CyclomaticComplexity
12
+ case code
13
+ when 400 then Exceptions::BadRequest
14
+ when 401, 403 then Exceptions::AuthorizationFailed
15
+ when 404 then Exceptions::NotFound
16
+ when 413 then Exceptions::RequestEntityTooLarge
17
+ when 429 then Exceptions::LimitExceeded
18
+ when 456 then Exceptions::QuotaExceeded
19
+ when 500..599 then Exceptions::ServerError
20
+ else Exceptions::RequestError
21
+ end
22
+ end
23
+
24
+ def initialize(response)
25
+ @response = response
26
+ end
27
+
28
+ def build
29
+ error_class = self.class.error_class_from_response_code(response.code.to_i)
30
+ error_class.new(response)
31
+ end
32
+ end
33
+ end
34
+ end