deepl-rb 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +39 -0
- data/.rubocop.yml +17 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +113 -0
- data/LICENSE.md +25 -0
- data/README.md +102 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/deepl-rb.gemspec +69 -0
- data/lib/deepl.rb +56 -0
- data/lib/deepl/api.rb +10 -0
- data/lib/deepl/configuration.rb +25 -0
- data/lib/deepl/exceptions/authorization_failed.rb +9 -0
- data/lib/deepl/exceptions/bad_request.rb +9 -0
- data/lib/deepl/exceptions/error.rb +6 -0
- data/lib/deepl/exceptions/limit_exceeded.rb +9 -0
- data/lib/deepl/exceptions/request_error.rb +16 -0
- data/lib/deepl/requests/base.rb +72 -0
- data/lib/deepl/requests/translate_text.rb +36 -0
- data/lib/deepl/resources/base.rb +12 -0
- data/lib/deepl/resources/text.rb +18 -0
- data/spec/api/api_spec.rb +14 -0
- data/spec/api/configuration_spec.rb +44 -0
- data/spec/api/deepl_spec.rb +59 -0
- data/spec/fixtures/vcr_cassettes/deepl_translate.yml +110 -0
- data/spec/fixtures/vcr_cassettes/translate_texts.yml +177 -0
- data/spec/requests/translate_text_spec.rb +80 -0
- data/spec/resources/text_spec.rb +18 -0
- data/spec/spec_helper.rb +22 -0
- metadata +89 -0
data/lib/deepl/api.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module DeepL
|
2
|
+
class Configuration
|
3
|
+
ATTRIBUTES = %i[auth_key host].freeze
|
4
|
+
|
5
|
+
attr_accessor(*ATTRIBUTES)
|
6
|
+
|
7
|
+
def initialize(data = {})
|
8
|
+
data.each { |key, value| send("#{key}=", value) }
|
9
|
+
@auth_key ||= ENV['DEEPL_AUTH_KEY']
|
10
|
+
@host ||= 'https://api.deepl.com'
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate!
|
14
|
+
raise Exceptions::Error, 'auth_key not provided' if auth_key.nil? || auth_key.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def attributes
|
18
|
+
ATTRIBUTES.map { |attr| [attr, send(attr)] }.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
attributes == other.attributes
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module DeepL
|
2
|
+
module Requests
|
3
|
+
class Base
|
4
|
+
API_VERSION = 'v1'.freeze
|
5
|
+
|
6
|
+
attr_reader :api, :response
|
7
|
+
|
8
|
+
def initialize(api)
|
9
|
+
@api = api
|
10
|
+
end
|
11
|
+
|
12
|
+
def request
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def post(payload)
|
19
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
20
|
+
request.set_form_data(payload.compact)
|
21
|
+
response = http.request(request)
|
22
|
+
|
23
|
+
validate_response!(request, response)
|
24
|
+
[request, response]
|
25
|
+
end
|
26
|
+
|
27
|
+
def http
|
28
|
+
@http ||= begin
|
29
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
http.use_ssl = uri.scheme == 'https'
|
31
|
+
http
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_response!(request, response)
|
36
|
+
return if response.is_a?(Net::HTTPSuccess)
|
37
|
+
|
38
|
+
case response.code
|
39
|
+
when '400' then raise Exceptions::BadRequest.new(request, response)
|
40
|
+
when '403' then raise Exceptions::AuthorizationFailed.new(request, response)
|
41
|
+
when '429' then raise Exceptions::LimitExceeded.new(request, response)
|
42
|
+
else raise Exceptions::RequestError.new(request, response)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def path
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
def url
|
51
|
+
"#{host}/#{API_VERSION}/#{path}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def uri
|
55
|
+
@uri ||= begin
|
56
|
+
uri = URI(url)
|
57
|
+
new_query = URI.decode_www_form(uri.query || '') + query_params.to_a
|
58
|
+
uri.query = URI.encode_www_form(new_query)
|
59
|
+
uri
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def host
|
64
|
+
api.configuration.host
|
65
|
+
end
|
66
|
+
|
67
|
+
def query_params
|
68
|
+
{ auth_key: api.configuration.auth_key }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DeepL
|
2
|
+
module Requests
|
3
|
+
class TranslateText < Base
|
4
|
+
attr_reader :text, :source_lang, :target_lang
|
5
|
+
|
6
|
+
def initialize(api, text, source_lang, target_lang)
|
7
|
+
super(api)
|
8
|
+
@text = text
|
9
|
+
@source_lang = source_lang
|
10
|
+
@target_lang = target_lang
|
11
|
+
end
|
12
|
+
|
13
|
+
def request
|
14
|
+
payload = { text: text, source_lang: source_lang, target_lang: target_lang }
|
15
|
+
build_texts(*post(payload))
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_texts(request, response)
|
21
|
+
data = JSON.parse(response.body)
|
22
|
+
|
23
|
+
texts = data['translations'].map do |translation|
|
24
|
+
Resources::Text.new(translation['text'], translation['detected_source_language'],
|
25
|
+
request, response)
|
26
|
+
end
|
27
|
+
|
28
|
+
texts.size == 1 ? texts.first : texts
|
29
|
+
end
|
30
|
+
|
31
|
+
def path
|
32
|
+
'translate'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DeepL
|
2
|
+
module Resources
|
3
|
+
class Text < Base
|
4
|
+
attr_reader :text, :detected_source_language
|
5
|
+
|
6
|
+
def initialize(text, detected_source_language, *args)
|
7
|
+
super(*args)
|
8
|
+
|
9
|
+
@text = text
|
10
|
+
@detected_source_language = detected_source_language
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
text
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DeepL::API do
|
4
|
+
let(:configuration) { DeepL::Configuration.new }
|
5
|
+
subject { DeepL::API.new(configuration) }
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
context 'When building an API object' do
|
9
|
+
it 'should save the configuration' do
|
10
|
+
expect(subject.configuration).to be(configuration)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DeepL::Configuration do
|
4
|
+
let(:attributes) { {} }
|
5
|
+
subject { DeepL::Configuration.new(attributes) }
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
context 'When using default configuration attributes' do
|
9
|
+
it 'should use default attributes' do
|
10
|
+
expect(subject.auth_key).to eq(ENV['DEEPL_AUTH_KEY'])
|
11
|
+
expect(subject.host).to eq('https://api.deepl.com')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'When using custom configuration attributes' do
|
16
|
+
let(:attributes) { { auth_key: 'SAMPLE', host: 'http://www.example.org' } }
|
17
|
+
|
18
|
+
it 'should use custom attributes' do
|
19
|
+
expect(subject.auth_key).to eq(attributes[:auth_key])
|
20
|
+
expect(subject.host).to eq(attributes[:host])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#validate!' do
|
26
|
+
let(:auth_message) { 'auth_key not provided' }
|
27
|
+
|
28
|
+
context 'When providing a valid auth key' do
|
29
|
+
let(:attributes) { { auth_key: '' } }
|
30
|
+
|
31
|
+
it 'should raise an error' do
|
32
|
+
expect { subject.validate! }.to raise_error(DeepL::Exceptions::Error, auth_message)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'When providing an invalid auth key' do
|
37
|
+
let(:attributes) { { auth_key: 'not-empty' } }
|
38
|
+
|
39
|
+
it 'should not raise an error' do
|
40
|
+
expect { subject.validate! }.not_to raise_error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DeepL do
|
4
|
+
subject { DeepL.dup }
|
5
|
+
|
6
|
+
describe '#configure' do
|
7
|
+
context 'When providing no block' do
|
8
|
+
let(:configuration) { DeepL::Configuration.new }
|
9
|
+
before { subject.configure }
|
10
|
+
|
11
|
+
it 'should use default configuration' do
|
12
|
+
expect(subject.configuration).to eq(configuration)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'When providing a valid configuration' do
|
17
|
+
let(:configuration) do
|
18
|
+
DeepL::Configuration.new(auth_key: 'VALID', host: 'http://www.example.org')
|
19
|
+
end
|
20
|
+
before do
|
21
|
+
subject.configure do |config|
|
22
|
+
config.auth_key = configuration.auth_key
|
23
|
+
config.host = configuration.host
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should use the provided configuration' do
|
28
|
+
expect(subject.configuration).to eq(configuration)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'When providing an invalid configuration' do
|
33
|
+
it 'should raise an error' do
|
34
|
+
expect { subject.configure { |c| c.auth_key = '' } }
|
35
|
+
.to raise_error(DeepL::Exceptions::Error)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#translate' do
|
41
|
+
let(:input) { 'Sample' }
|
42
|
+
let(:source_lang) { 'EN' }
|
43
|
+
let(:target_lang) { 'ES' }
|
44
|
+
|
45
|
+
around do |example|
|
46
|
+
VCR.use_cassette('deepl_translate') { example.call }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'When translating a text' do
|
50
|
+
it 'should create and call a request object' do
|
51
|
+
expect(DeepL::Requests::TranslateText).to receive(:new)
|
52
|
+
.with(subject.api, input, source_lang, target_lang).and_call_original
|
53
|
+
|
54
|
+
text = subject.translate(input, source_lang: source_lang, target_lang: target_lang)
|
55
|
+
expect(text).to be_a(DeepL::Resources::Text)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://api.deepl.com/v1/translate?auth_key=VALID_TOKEN
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: source_lang=EN&target_lang=ES
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- "*/*"
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
Content-Type:
|
17
|
+
- application/x-www-form-urlencoded
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 400
|
21
|
+
message: Bad Request
|
22
|
+
headers:
|
23
|
+
Server:
|
24
|
+
- nginx
|
25
|
+
Date:
|
26
|
+
- Sun, 10 Dec 2017 21:20:25 GMT
|
27
|
+
Content-Length:
|
28
|
+
- '45'
|
29
|
+
Connection:
|
30
|
+
- keep-alive
|
31
|
+
body:
|
32
|
+
encoding: UTF-8
|
33
|
+
string: '{"message":"Parameter ''text'' not specified."}'
|
34
|
+
http_version:
|
35
|
+
recorded_at: Sun, 10 Dec 2017 21:20:25 GMT
|
36
|
+
- request:
|
37
|
+
method: post
|
38
|
+
uri: https://api.deepl.com/v1/translate?auth_key=VALID_TOKEN
|
39
|
+
body:
|
40
|
+
encoding: US-ASCII
|
41
|
+
string: text=Abc&source_lang=EN&target_lang=ES
|
42
|
+
headers:
|
43
|
+
Accept-Encoding:
|
44
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
45
|
+
Accept:
|
46
|
+
- "*/*"
|
47
|
+
User-Agent:
|
48
|
+
- Ruby
|
49
|
+
Content-Type:
|
50
|
+
- application/x-www-form-urlencoded
|
51
|
+
response:
|
52
|
+
status:
|
53
|
+
code: 200
|
54
|
+
message: OK
|
55
|
+
headers:
|
56
|
+
Server:
|
57
|
+
- nginx
|
58
|
+
Date:
|
59
|
+
- Sun, 10 Dec 2017 21:21:23 GMT
|
60
|
+
Content-Type:
|
61
|
+
- application/json
|
62
|
+
Content-Length:
|
63
|
+
- '65'
|
64
|
+
Connection:
|
65
|
+
- keep-alive
|
66
|
+
Access-Control-Allow-Origin:
|
67
|
+
- "*"
|
68
|
+
body:
|
69
|
+
encoding: UTF-8
|
70
|
+
string: '{"translations":[{"detected_source_language":"EN","text":"ABC"}]}'
|
71
|
+
http_version:
|
72
|
+
recorded_at: Sun, 10 Dec 2017 21:21:23 GMT
|
73
|
+
- request:
|
74
|
+
method: post
|
75
|
+
uri: https://api.deepl.com/v1/translate?auth_key=VALID_TOKEN
|
76
|
+
body:
|
77
|
+
encoding: US-ASCII
|
78
|
+
string: text=Sample&source_lang=EN&target_lang=ES
|
79
|
+
headers:
|
80
|
+
Accept-Encoding:
|
81
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
82
|
+
Accept:
|
83
|
+
- "*/*"
|
84
|
+
User-Agent:
|
85
|
+
- Ruby
|
86
|
+
Content-Type:
|
87
|
+
- application/x-www-form-urlencoded
|
88
|
+
response:
|
89
|
+
status:
|
90
|
+
code: 200
|
91
|
+
message: OK
|
92
|
+
headers:
|
93
|
+
Server:
|
94
|
+
- nginx
|
95
|
+
Date:
|
96
|
+
- Sun, 10 Dec 2017 21:22:11 GMT
|
97
|
+
Content-Type:
|
98
|
+
- application/json
|
99
|
+
Content-Length:
|
100
|
+
- '69'
|
101
|
+
Connection:
|
102
|
+
- keep-alive
|
103
|
+
Access-Control-Allow-Origin:
|
104
|
+
- "*"
|
105
|
+
body:
|
106
|
+
encoding: UTF-8
|
107
|
+
string: '{"translations":[{"detected_source_language":"EN","text":"Muestra"}]}'
|
108
|
+
http_version:
|
109
|
+
recorded_at: Sun, 10 Dec 2017 21:22:11 GMT
|
110
|
+
recorded_with: VCR 4.0.0
|