Class: BetterTranslate::Providers::BaseHttpProvider Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/better_translate/providers/base_http_provider.rb

Overview

This class is abstract.

Subclasses must implement #translate_text and #translate_batch

Base class for HTTP-based translation providers

Implements common functionality:

  • Faraday HTTP client with retry logic
  • Exponential backoff with jitter
  • Rate limiting
  • Caching
  • Error handling

Examples:

Creating a custom provider

class MyProvider < BaseHttpProvider
  def translate_text(text, target_lang_code, target_lang_name)
    # Implementation
  end

  def translate_batch(texts, target_lang_code, target_lang_name)
    # Implementation
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ BaseHttpProvider

Initialize the provider

Examples:

config = Configuration.new
provider = BaseHttpProvider.new(config)

Parameters:



52
53
54
55
56
# File 'lib/better_translate/providers/base_http_provider.rb', line 52

def initialize(config)
  @config = config
  @cache = Cache.new(capacity: config.cache_size, ttl: config.cache_ttl)
  @rate_limiter = RateLimiter.new(delay: 0.5)
end

Instance Attribute Details

#cacheCache (readonly)

Returns The cache instance.

Returns:

  • (Cache)

    The cache instance



39
40
41
# File 'lib/better_translate/providers/base_http_provider.rb', line 39

def cache
  @cache
end

#configConfiguration (readonly)

Returns The configuration object.

Returns:



36
37
38
# File 'lib/better_translate/providers/base_http_provider.rb', line 36

def config
  @config
end

#rate_limiterRateLimiter (readonly)

Returns The rate limiter instance.

Returns:



42
43
44
# File 'lib/better_translate/providers/base_http_provider.rb', line 42

def rate_limiter
  @rate_limiter
end

Instance Method Details

#build_cache_key(text, target_lang_code) ⇒ String (protected)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Build cache key

Parameters:

  • text (String)

    Text being translated

  • target_lang_code (String)

    Target language code

Returns:

  • (String)

    Cache key



234
235
236
# File 'lib/better_translate/providers/base_http_provider.rb', line 234

def build_cache_key(text, target_lang_code)
  "#{text}:#{target_lang_code}"
end

#calculate_backoff(attempt) ⇒ Float (protected)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Calculate exponential backoff with jitter

Parameters:

  • attempt (Integer)

    Current attempt number

Returns:

  • (Float)

    Delay in seconds



172
173
174
175
176
177
178
179
# File 'lib/better_translate/providers/base_http_provider.rb', line 172

def calculate_backoff(attempt)
  base_delay = config.retry_delay
  max_delay = 60.0
  jitter = rand * 0.3 # 0-30% jitter

  delay = base_delay * (2**(attempt - 1)) * (1 + jitter)
  [delay, max_delay].min
end

#handle_response(response) ⇒ void (protected)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Handle HTTP response

Parameters:

  • response (Faraday::Response)

    HTTP response

Raises:



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/better_translate/providers/base_http_provider.rb', line 139

def handle_response(response)
  case response.status
  when 200..299
    # Success
  when 429
    raise RateLimitError.new(
      "Rate limit exceeded",
      context: { status: response.status, body: response.body }
    )
  when 400..499
    raise ApiError.new(
      "Client error: #{response.status}",
      context: { status: response.status, body: response.body }
    )
  when 500..599
    raise ApiError.new(
      "Server error: #{response.status}",
      context: { status: response.status, body: response.body }
    )
  else
    raise ApiError.new(
      "Unexpected status: #{response.status}",
      context: { status: response.status, body: response.body }
    )
  end
end

#http_clientFaraday::Connection (protected)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get or create HTTP client

Returns:

  • (Faraday::Connection)

    HTTP client



201
202
203
204
205
206
207
# File 'lib/better_translate/providers/base_http_provider.rb', line 201

def http_client
  @http_client ||= Faraday.new do |f|
    f.options.timeout = config.request_timeout
    f.options.open_timeout = 10
    f.adapter Faraday.default_adapter
  end
end

#log_retry(attempt, delay, error) ⇒ void (protected)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Log retry attempt

Parameters:

  • attempt (Integer)

    Current attempt number

  • delay (Float)

    Delay before retry

  • error (StandardError)

    The error that triggered retry



189
190
191
192
193
194
# File 'lib/better_translate/providers/base_http_provider.rb', line 189

def log_retry(attempt, delay, error)
  return unless config.verbose

  puts "[BetterTranslate] Retry #{attempt}/#{config.max_retries} " \
       "after #{delay.round(2)}s (#{error.class}: #{error.message})"
end

#make_request(method, url, body: nil, headers: {}) ⇒ Faraday::Response (protected)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Make an HTTP request with retry logic

Implements exponential backoff with jitter and rate limiting. Automatically retries on rate limit and API errors.

Parameters:

  • method (Symbol)

    HTTP method (:get, :post, etc.)

  • url (String)

    Request URL

  • body (Hash, nil) (defaults to: nil)

    Request body

  • headers (Hash) (defaults to: {})

    Request headers

Returns:

  • (Faraday::Response)

    HTTP response

Raises:

  • (ApiError)

    if request fails after retries



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/better_translate/providers/base_http_provider.rb', line 105

def make_request(method, url, body: nil, headers: {})
  attempt = 0

  loop do
    attempt += 1

    begin
      rate_limiter.wait
      response = http_client.send(method, url) do |req|
        req.headers.merge!(headers)
        req.body = body.to_json if body
      end
      rate_limiter.record_request

      handle_response(response)
      return response
    rescue RateLimitError, ApiError => e
      raise if attempt >= config.max_retries

      delay = calculate_backoff(attempt)
      log_retry(attempt, delay, e)
      sleep(delay)
    end
  end
end

#translate_batch(texts, target_lang_code, target_lang_name) ⇒ Array<String>

Translate multiple texts in a batch

Examples:

provider.translate_batch(["Hello", "World"], "it", "Italian")
#=> ["Ciao", "Mondo"]

Parameters:

  • texts (Array<String>)

    Texts to translate

  • target_lang_code (String)

    Target language code

  • target_lang_name (String)

    Target language name

Returns:

  • (Array<String>)

    Translated texts

Raises:

  • (NotImplementedError)

    Must be implemented by subclasses



86
87
88
# File 'lib/better_translate/providers/base_http_provider.rb', line 86

def translate_batch(texts, target_lang_code, target_lang_name)
  raise NotImplementedError, "#{self.class} must implement #translate_batch"
end

#translate_text(text, target_lang_code, target_lang_name) ⇒ String

Translate a single text string

Examples:

provider.translate_text("Hello", "it", "Italian")
#=> "Ciao"

Parameters:

  • text (String)

    Text to translate

  • target_lang_code (String)

    Target language code

  • target_lang_name (String)

    Target language name

Returns:

  • (String)

    Translated text

Raises:

  • (NotImplementedError)

    Must be implemented by subclasses



70
71
72
# File 'lib/better_translate/providers/base_http_provider.rb', line 70

def translate_text(text, target_lang_code, target_lang_name)
  raise NotImplementedError, "#{self.class} must implement #translate_text"
end

#with_cache(cache_key) ⇒ String (protected)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get from cache or execute block

Parameters:

  • cache_key (String)

    Cache key

Yield Returns:

  • (String)

    Value to cache if not found

Returns:

  • (String)

    Cached or newly computed value



216
217
218
219
220
221
222
223
224
225
# File 'lib/better_translate/providers/base_http_provider.rb', line 216

def with_cache(cache_key)
  return yield unless config.cache_enabled

  cached = cache.get(cache_key)
  return cached if cached

  result = yield
  cache.set(cache_key, result)
  result
end