abbyy-cloud 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +24 -0
  4. data/README.md +60 -33
  5. data/abbyy-cloud.gemspec +1 -1
  6. data/lib/abbyy/cloud.rb +10 -16
  7. data/lib/abbyy/cloud/connection.rb +31 -17
  8. data/lib/abbyy/cloud/models/direction.rb +4 -2
  9. data/lib/abbyy/cloud/models/discount.rb +12 -0
  10. data/lib/abbyy/cloud/models/engine.rb +4 -1
  11. data/lib/abbyy/cloud/models/locale.rb +23 -0
  12. data/lib/abbyy/cloud/models/price.rb +22 -0
  13. data/lib/abbyy/cloud/models/source_segment.rb +14 -0
  14. data/lib/abbyy/cloud/models/source_tag.rb +15 -0
  15. data/lib/abbyy/cloud/models/transfer_data.rb +14 -0
  16. data/lib/abbyy/cloud/models/translation.rb +1 -1
  17. data/lib/abbyy/cloud/models/translation_segment.rb +16 -0
  18. data/lib/abbyy/cloud/models/unit_price.rb +13 -0
  19. data/lib/abbyy/cloud/namespaces/machine_translations.rb +47 -1
  20. data/lib/abbyy/cloud/namespaces/prices.rb +29 -0
  21. data/lib/abbyy/cloud/operations/base.rb +20 -6
  22. data/lib/abbyy/cloud/operations/engines.rb +3 -1
  23. data/lib/abbyy/cloud/operations/prices.rb +23 -0
  24. data/lib/abbyy/cloud/operations/translate.rb +5 -3
  25. data/lib/abbyy/cloud/operations/translate_segments.rb +22 -0
  26. data/lib/abbyy/cloud/settings.rb +4 -5
  27. data/lib/abbyy/cloud/types.rb +14 -10
  28. data/spec/abbyy/cloud/connection_spec.rb +1 -1
  29. data/spec/abbyy/cloud/models/discount_spec.rb +32 -0
  30. data/spec/abbyy/cloud/models/locale_spec.rb +55 -0
  31. data/spec/abbyy/cloud/models/price_spec.rb +107 -0
  32. data/spec/abbyy/cloud/models/source_segment_spec.rb +37 -0
  33. data/spec/abbyy/cloud/models/source_tag_spec.rb +56 -0
  34. data/spec/abbyy/cloud/models/transfer_data_spec.rb +40 -0
  35. data/spec/abbyy/cloud/models/translation_segment_spec.rb +38 -0
  36. data/spec/abbyy/cloud/models/unit_price_spec.rb +48 -0
  37. data/spec/abbyy/cloud/settings_spec.rb +2 -24
  38. data/spec/abbyy/cloud_spec.rb +3 -4
  39. data/spec/feature/abbyy/mt_default_engine_spec.rb +12 -0
  40. data/spec/feature/abbyy/mt_engine_spec.rb +15 -0
  41. data/spec/feature/abbyy/mt_translate_segments_spec.rb +136 -0
  42. data/spec/feature/abbyy/{order_translate_spec.rb → mt_translate_spec.rb} +2 -2
  43. data/spec/feature/abbyy/prices_details_spec.rb +70 -0
  44. metadata +39 -5
  45. data/lib/abbyy/cloud/namespaces/orders.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 19c1ff610b7210be89b61f821ff6642ce0d149c3
4
- data.tar.gz: 896dbcb70a0796ef0445b7184d703bba31c2259e
3
+ metadata.gz: 86781ecddc4c6d75b7eca75244bb940bf82c0df1
4
+ data.tar.gz: 4fbafddffeab9bbbbc23d8e951e7f04bf5ece6c0
5
5
  SHA512:
6
- metadata.gz: ca702183cde764f614c77f26bb5bafa17a2f96614d230efccf97775f7f839fea047c35ea6bdb67b24fb270cada0402f89caefe0fcc8f7264fd295218e43f4a95
7
- data.tar.gz: f4ec867431b3eab6e736700831db4f62aaa047289c90ee81847347bdf933331a1c742973c1ed96557a8f5f69fd4206577253a628d3fd4049049ae7f07f35dcb4
6
+ metadata.gz: 46fdfc138c2b78f7e555ee80dc19c2b971cf12768e10569ee6eda8c6529030e9401d6d35ab50dc08266862b60af5e70f4a725bd6ff9df00c7caea2ff4cb355e0
7
+ data.tar.gz: ca541399867b4f463ce81f261f2e189afff31b218a7d8532008a78bbc9849ddd6f2d7b10c111396fb4b43245acb88d62b963ba3b8b3d698b3b900f816a6cefed
data/.rubocop.yml CHANGED
@@ -78,6 +78,9 @@ Style/StringLiterals:
78
78
  Style/StringLiteralsInInterpolation:
79
79
  EnforcedStyle: double_quotes
80
80
 
81
+ Style/TernaryParentheses:
82
+ Enabled: false
83
+
81
84
  Style/TrivialAccessors:
82
85
  Exclude:
83
86
  - spec/**/*_spec.rb
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ # [v0.0.4 2016-08-23](https://github.com/nepalez/abbyy-cloud/tree/v0.0.4)
2
+
3
+ ### Added
4
+
5
+ * Operation `mt.engine(name)` (nepalez)
6
+ * Operation `mt.default_engine` (nepalez)
7
+ * Operation `mt.translate` instead of `orders.translate` (nepalez)
8
+ * Operation `mt.translate_segments` (nepalez)
9
+ * Operation `prices.details` (nepalez)
10
+ * Validation of locales following IANA & RFC-5646 (nepalez)
11
+
12
+ ### Deleted
13
+
14
+ * Namespace `orders` (nepalez)
15
+ * Remove :version settings (nepalez)
16
+ Every operation has its own version. No global API version is supported.
17
+ When necessary, a version will be send to the operation.
18
+
19
+ ### Internal
20
+
21
+ * Added support for query part of a request (nepalez)
22
+
23
+ [Compare v0.0.3...v0.0.4](https://github.com/nepalez/abbyy-cloud/compare/v0.0.3...v0.0.4)
24
+
1
25
  # [v0.0.3 2016-08-18](https://github.com/nepalez/abbyy-cloud/tree/v0.0.3)
2
26
 
3
27
  ### Bugs Fixed
data/README.md CHANGED
@@ -22,20 +22,31 @@ require "abbyy/cloud"
22
22
  CLIENT = ABBYY::Cloud.new(id: "foo", token: "bar")
23
23
  ```
24
24
 
25
- The only supported API version is `0`. By default the translation engine is set to "Sandbox".
26
-
27
25
  You can set these options explicitly:
28
26
 
29
27
  ```ruby
30
28
  CLIENT = ABBYY::Cloud.new id: "foo",
31
29
  token: "bar",
32
- engine: "Sandbox", # default engine for translations
33
- version: 0 # the only supported version
30
+ engine: "Sandbox" # default engine for translations
31
+ ```
32
+
33
+ And then use the client to provide requests:
34
+
35
+ ```ruby
36
+ res = CLIENT.mt.translate("To be or not to be", from: :en, to: :ru)
37
+ res.translation # => "Быть или не быть"
38
+
39
+ res = CLIENT.mt.translate_segments ["To be or not to be", "That is the question"], from: "en", to: "ru"
40
+ res.map(&:text) # => ["Быть или не быть", "Это вопрос"]
34
41
  ```
35
42
 
43
+ ## Namespaces and Operations
44
+
36
45
  ### Machine Translations
37
46
 
38
- #### Engines
47
+ The namespace `mt` contains (synchronous) operations with machine translation.
48
+
49
+ #### engines
39
50
 
40
51
  See [the specification](https://api.abbyy.cloud/swagger/ui/index#!/MachineTranslation)
41
52
 
@@ -44,7 +55,7 @@ result = CLIENT.mt.engines
44
55
  # => [#<ABBYY::Cloud::Models::Engine @name="Sandbox">]
45
56
  ```
46
57
 
47
- #### Engine
58
+ #### engine
48
59
 
49
60
  This operation is built on top of the previous one and simply takes settings for the specified engine:
50
61
 
@@ -59,14 +70,24 @@ result.to_h
59
70
  # => { name: "Sandbox", languages: ["en", "ru"], translation_directions: [{ source: "en", target: "ru" }] }
60
71
  ```
61
72
 
62
- ### Orders
73
+ #### default_engine
74
+
75
+ Returns settings for the engine used in the initializer
76
+
77
+ ```ruby
78
+ CLIENT = ABBYY::Cloud.new(id: "foo", token: "bar", engine: "Bing")
79
+
80
+ settings_for_bing = CLIENT.mt.default_engine
81
+ ```
82
+
83
+ #### translate
63
84
 
64
- #### Instant Translation
85
+ Translates a string.
65
86
 
66
87
  See [the specification](https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_Translate).
67
88
 
68
89
  ```ruby
69
- result = CLIENT.orders.translate("To be or not to be", from: :en, to: :ru)
90
+ result = CLIENT.mt.translate("To be or not to be", from: :en, to: :ru)
70
91
 
71
92
  result.class # => ABBYY::Cloud::Models::Translation
72
93
  result.translation # => "Быть или не быть"
@@ -74,42 +95,48 @@ result.id # => "2832934"
74
95
  result.to_h # => { id: "2832934", translation: "Быть или не быть" }
75
96
  ```
76
97
 
77
- You can specify an engine (different from default):
98
+ #### translate_segments
78
99
 
79
- ```ruby
80
- CLIENT.orders.translate("To be or not to be", from: :en, to: :ru, engine: "Bing")
81
- ```
100
+ Translates an array of strings in one request
82
101
 
83
- The method raises an exception in case the cloud responded with error.
102
+ See [the specification](https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_TranslateSegments)
84
103
 
85
104
  ```ruby
86
- error = \
87
- begin
88
- CLIENT.orders.translate("To be or not to be")
89
- rescue ABBYY::Cloud::Error => error
90
- error
91
- end
92
-
93
- error.class # => ABBYY::Cloud::ResponceError
94
- error.status # => 400
105
+ result = CLIENT.mt.translate_segments(["To be", "or not to be"], from: :en, to: :ru)
106
+
107
+ result.class # => ABBYY::Cloud::Models::TranslationSequence
108
+ result.map(&:text) # => ["Быть", "или не быть"]
95
109
  ```
96
110
 
97
- The exception carries returned error model in its `#data` attribute:
111
+ ### Prices
112
+
113
+ The namespace `prices` contains operations with prices details.
114
+
115
+ #### details
116
+
117
+ See [the specification](https://api.abbyy.cloud/swagger/ui/index#!/Prices/Prices_GetAccountPrices).
98
118
 
99
119
  ```ruby
100
- error.data # => ABBYY::Cloud::Models::Error
101
- error.data.to_h
102
- # => {
103
- # model_state: {},
104
- # requiest_id: "string",
105
- # error: "error message",
106
- # error_description: "some details"
120
+ list = CLIENT.prices.details
121
+
122
+ list.first.to_h
123
+ # {
124
+ # id: "foo",
125
+ # account_id: "bar",
126
+ # type: "qux",
127
+ # from: "ru",
128
+ # to: "en",
129
+ # unit_prices: [{ unit_type: "Words", currency: "USD", amount: 0.03 }],
130
+ # discounts: [{ discount_type: "TMTextMatch", discount: 0.01 }],
131
+ # created: Time.now
107
132
  # }
108
133
 
109
- error.data.error # => "error message"
110
- # etc.
111
134
  ```
112
135
 
136
+ The number of items can be several thousands. You can specify `skip` and `take` options to take necessary items only, otherwise it will return all prices.
137
+
138
+ Notice, though, that every single request can return no more than 1000 items. If you request more prices, several requests will be made one-by-one. Parsing all the results can be pretty slow.
139
+
113
140
  ## Compatibility
114
141
 
115
142
  [WIP] Compatible to [ABBYY Cloud API v.0][abbyy-api].
data/abbyy-cloud.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = "abbyy-cloud"
3
- gem.version = "0.0.3"
3
+ gem.version = "0.0.4"
4
4
  gem.authors = ["Andrew Kozin"]
5
5
  gem.email = ["andrew.kozin@gmail.com"]
6
6
  gem.summary = "HTTP client to ABBYY Cloud API"
data/lib/abbyy/cloud.rb CHANGED
@@ -11,22 +11,12 @@ module ABBYY
11
11
  require_relative "cloud/connection"
12
12
  require_relative "cloud/settings"
13
13
 
14
- require_relative "cloud/exceptions/response_error"
15
- require_relative "cloud/exceptions/argument_error"
16
- require_relative "cloud/exceptions/type_error"
17
-
18
- require_relative "cloud/models/error"
19
- require_relative "cloud/models/translation"
20
- require_relative "cloud/models/direction"
21
- require_relative "cloud/models/engine"
22
-
23
- require_relative "cloud/operations/base"
24
- require_relative "cloud/operations/translate"
25
- require_relative "cloud/operations/engines"
26
-
27
- require_relative "cloud/namespaces/base"
28
- require_relative "cloud/namespaces/orders"
29
- require_relative "cloud/namespaces/machine_translations"
14
+ Dir[
15
+ "lib/abbyy/cloud/exceptions/*.rb",
16
+ "lib/abbyy/cloud/models/*.rb",
17
+ "lib/abbyy/cloud/operations/*.rb",
18
+ "lib/abbyy/cloud/namespaces/*.rb"
19
+ ].each { |f| require File.expand_path(f) }
30
20
 
31
21
  attr_reader :settings
32
22
 
@@ -38,6 +28,10 @@ module ABBYY
38
28
  Namespaces::Orders.new(settings)
39
29
  end
40
30
 
31
+ def prices
32
+ Namespaces::Prices.new(settings)
33
+ end
34
+
41
35
  private
42
36
 
43
37
  def initialize(*args)
@@ -7,35 +7,49 @@
7
7
  class ABBYY::Cloud
8
8
  class Connection
9
9
  include Dry::Initializer.define -> do
10
- option :version
11
10
  option :id
12
11
  option :token
13
12
  end
14
13
 
15
- def call(http_method, path, body: nil, headers: nil)
16
- uri = root.merge(path).tap { |item| item.scheme = "https" }
17
- req = prepare_request(http_method, uri, JSON(body.to_h), headers.to_h)
14
+ attr_reader :root
18
15
 
19
- Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
20
- handle_response http.request(req)
16
+ def call(http_method, path, **options)
17
+ uri = prepare_uri(path, options)
18
+ req = Net::HTTP.const_get(http_method.capitalize).new(uri)
19
+ setup_headers(req, options)
20
+ setup_body(req, options)
21
+
22
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
23
+ http.request(req)
21
24
  end
25
+ handle_response(res)
22
26
  end
23
27
 
24
28
  private
25
29
 
26
- def root
27
- @root ||= URI("https://api.abbyy.cloud").merge("v#{version}/")
30
+ def initialize(*)
31
+ super
32
+ @root = URI("https://api.abbyy.cloud").tap { |obj| obj.scheme = "https" }
28
33
  end
29
34
 
30
- def prepare_request(http_method, uri, body, headers)
31
- Net::HTTP.const_get(http_method.capitalize).new(uri).tap do |req|
32
- req.body = body
33
- req.basic_auth id, token
34
- req["accept-charset"] = "utf-8"
35
- req["accept"] = "application/json"
36
- req["content-type"] = "application/json"
37
- headers.each { |key, value| req[key.to_s] = value }
38
- end
35
+ def prepare_uri(path, query: nil, **)
36
+ uri = root.merge(path)
37
+ query = query.to_h.map { |k, v| "#{k}=#{v}" if v }.compact.join("&")
38
+ uri.query = query unless query.empty?
39
+ uri
40
+ end
41
+
42
+ def setup_headers(req, headers: nil, **)
43
+ req.basic_auth id, token
44
+ req["accept-charset"] = "utf-8"
45
+ req["accept"] = "application/json"
46
+ req["content-type"] = "application/json"
47
+ headers.to_h.each { |key, value| req[key.to_s] = value }
48
+ end
49
+
50
+ def setup_body(req, body: nil, **)
51
+ return unless req["content-type"] == "application/json"
52
+ req.body = JSON(body.to_h)
39
53
  end
40
54
 
41
55
  def handle_response(response)
@@ -1,9 +1,11 @@
1
+ require_relative "locale"
2
+
1
3
  class ABBYY::Cloud
2
4
  module Models
3
5
  # Description of the translation direction
4
6
  class Direction < Struct
5
- attribute :source, Types::Language
6
- attribute :target, Types::Language
7
+ attribute :source, Types::Locale
8
+ attribute :target, Types::Locale
7
9
  end
8
10
 
9
11
  # Registers type Types::Direction
@@ -0,0 +1,12 @@
1
+ class ABBYY::Cloud
2
+ module Models
3
+ # Description of the price discount
4
+ class Discount < Struct
5
+ attribute :discount_type, Types::DiscountType
6
+ attribute :discount, Types::Coercible::Float
7
+ end
8
+
9
+ # Registers type Types::Discount
10
+ Types.register_type Discount
11
+ end
12
+ end
@@ -1,10 +1,13 @@
1
+ require_relative "locale"
2
+ require_relative "direction"
3
+
1
4
  class ABBYY::Cloud
2
5
  # Collection of models returned in requests
3
6
  module Models
4
7
  # Description of the engine
5
8
  class Engine < Struct
6
9
  attribute :name, Types::Strict::String
7
- attribute :languages, Types::Array.member(Types::Language)
10
+ attribute :languages, Types::Array.member(Types::Locale)
8
11
  attribute :translation_directions, Types::Array.member(Types::Direction)
9
12
  end
10
13
 
@@ -0,0 +1,23 @@
1
+ class ABBYY::Cloud
2
+ module Models
3
+ class Locale < String
4
+ FORMAT = /\A[A-Za-z0-9]{2,}(-[A-Za-z0-9]+)*\z|\A[i|x](-[A-Za-z0-9]+)+\z/
5
+
6
+ class << self
7
+ def new(value)
8
+ return unless value
9
+ return super if value[FORMAT]
10
+ raise <<-MESSAGE.gsub(" +\|", "")
11
+ |'#{value}' is not a valid locale. See:
12
+ | RFC-5656 (https://tools.ietf.org/html/rfc5646) for valid format
13
+ | Full list of locales at http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
14
+ MESSAGE
15
+ end
16
+ alias_method :[], :new
17
+ end
18
+ end
19
+
20
+ # Registers type Types::Locale
21
+ Types.register_type Locale
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "locale"
2
+ require_relative "unit_price"
3
+ require_relative "discount"
4
+
5
+ class ABBYY::Cloud
6
+ module Models
7
+ # Price details
8
+ class Price < Struct
9
+ attribute :id, Types::Strict::String
10
+ attribute :account_id, Types::Strict::String
11
+ attribute :type, Types::Strict::String
12
+ attribute :from, Types::Locale
13
+ attribute :to, Types::Locale
14
+ attribute :unit_prices, Types::Array.member(Types::UnitPrice)
15
+ attribute :discounts, Types::Array.member(Types::Discount)
16
+ attribute :created, Types::Coercible::Time
17
+ end
18
+
19
+ # Registers type Types::Price
20
+ Types.register_type Price
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "source_tag"
2
+
3
+ class ABBYY::Cloud
4
+ module Models
5
+ # A segment for the translation
6
+ class SourceSegment < Struct
7
+ attribute :text, Types::Strict::String
8
+ attribute :tags, Types::Array.member(Types::SourceTag).default([])
9
+ end
10
+
11
+ # Registers type Types::SourceSegment
12
+ Types.register_type SourceSegment
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ class ABBYY::Cloud
2
+ module Models
3
+ TYPES = %w(Start End Placeholder).freeze
4
+
5
+ # Tag for translation source/target
6
+ class SourceTag < Struct
7
+ attribute :number, Types::Strict::Int
8
+ attribute :position, Types::Strict::Int
9
+ attribute :type, Types::Strict::String.constrained(included_in: TYPES)
10
+ end
11
+
12
+ # Registers type Types::SourceTag
13
+ Types.register_type SourceTag
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ class ABBYY::Cloud
2
+ module Models
3
+ TYPES = %w(Start End Placeholder).freeze
4
+
5
+ # Tag for translation source/target
6
+ class TransferData < Struct
7
+ attribute :position, Types::Strict::Int
8
+ attribute :order, Types::Strict::Int
9
+ end
10
+
11
+ # Registers type Types::TransferData
12
+ Types.register_type TransferData
13
+ end
14
+ end