tml 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +243 -0
  4. data/Rakefile +9 -0
  5. data/lib/tml.rb +56 -0
  6. data/lib/tml/api/client.rb +206 -0
  7. data/lib/tml/api/post_office.rb +71 -0
  8. data/lib/tml/application.rb +254 -0
  9. data/lib/tml/base.rb +116 -0
  10. data/lib/tml/cache.rb +143 -0
  11. data/lib/tml/cache_adapters/file.rb +89 -0
  12. data/lib/tml/cache_adapters/memcache.rb +104 -0
  13. data/lib/tml/cache_adapters/memory.rb +85 -0
  14. data/lib/tml/cache_adapters/redis.rb +108 -0
  15. data/lib/tml/config.rb +410 -0
  16. data/lib/tml/decorators/base.rb +52 -0
  17. data/lib/tml/decorators/default.rb +43 -0
  18. data/lib/tml/decorators/html.rb +102 -0
  19. data/lib/tml/exception.rb +35 -0
  20. data/lib/tml/ext/array.rb +86 -0
  21. data/lib/tml/ext/date.rb +99 -0
  22. data/lib/tml/ext/fixnum.rb +47 -0
  23. data/lib/tml/ext/hash.rb +99 -0
  24. data/lib/tml/ext/string.rb +56 -0
  25. data/lib/tml/ext/time.rb +89 -0
  26. data/lib/tml/generators/cache/base.rb +117 -0
  27. data/lib/tml/generators/cache/file.rb +159 -0
  28. data/lib/tml/language.rb +175 -0
  29. data/lib/tml/language_case.rb +105 -0
  30. data/lib/tml/language_case_rule.rb +76 -0
  31. data/lib/tml/language_context.rb +117 -0
  32. data/lib/tml/language_context_rule.rb +56 -0
  33. data/lib/tml/languages/en.json +1363 -0
  34. data/lib/tml/logger.rb +109 -0
  35. data/lib/tml/rules_engine/evaluator.rb +162 -0
  36. data/lib/tml/rules_engine/parser.rb +65 -0
  37. data/lib/tml/session.rb +199 -0
  38. data/lib/tml/source.rb +106 -0
  39. data/lib/tml/tokenizers/data.rb +96 -0
  40. data/lib/tml/tokenizers/decoration.rb +204 -0
  41. data/lib/tml/tokenizers/dom.rb +346 -0
  42. data/lib/tml/tokens/data.rb +403 -0
  43. data/lib/tml/tokens/method.rb +61 -0
  44. data/lib/tml/tokens/transform.rb +223 -0
  45. data/lib/tml/translation.rb +67 -0
  46. data/lib/tml/translation_key.rb +178 -0
  47. data/lib/tml/translator.rb +47 -0
  48. data/lib/tml/utils.rb +130 -0
  49. data/lib/tml/version.rb +34 -0
  50. metadata +121 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c925403dfc55ba7c108ece9aa49eb9dd15fd6de3
4
+ data.tar.gz: 71be83bb7940f664062fc0a451fa37f6d03761e3
5
+ SHA512:
6
+ metadata.gz: 71c46bc90a27f119d54e47ea4dd6b3b27380aea20b22862068d2a953da385152a635ac353b5656b25706e8dd531ceae691909fca95ca586fac5ed636d84db0e4
7
+ data.tar.gz: ab349cec99f87ac79a831d9d1e51318ccb0a213735949f8d0e99ab9d989daad01d46abd355f591a71232dc5480189894b9708da52f056a52c3cf9b79ed77cd15
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Translation Exchange, Inc
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,243 @@
1
+ <p align="center">
2
+ <img src="https://avatars0.githubusercontent.com/u/1316274?v=3&s=200">
3
+ </p>
4
+
5
+ TML Library For Ruby
6
+ ==================
7
+ [![Build Status](https://travis-ci.org/translationexchange/tml-ruby.png?branch=master)](https://travis-ci.org/translationexchange/tml-ruby)
8
+ [![Coverage Status](https://coveralls.io/repos/translationexchange/tml-ruby/badge.png?branch=master)](https://coveralls.io/r/translationexchange/tml-ruby?branch=master)
9
+ [![Gem Version](https://badge.fury.io/rb/tml-ruby.png)](http://badge.fury.io/rb/tml)
10
+ [![Dependency Status](https://www.versioneye.com/user/projects/54c1457a6c00352081000416/badge.svg?style=flat)](https://www.versioneye.com/user/projects/54c1457a6c00352081000416)
11
+
12
+ TML library for Ruby is a set of classes that provide translation functionality for any Ruby based application.
13
+ The library uses Translation Markup Language that allows you to encode complex language structures in simple, yet powerful forms.
14
+
15
+ The library works in conjunctions with TranslationExchange.com service that provides machine and human translations for your application.
16
+ In order to use the library, you should sign up at TranslationExchange.com, create a new application and copy the application key and secret.
17
+
18
+
19
+ Rails Integration
20
+ ==================
21
+
22
+ If you are planning on using TML in a Rails application, you should use tml-rails gem instead.
23
+
24
+ https://github.com/translationexchange/tml-rails
25
+
26
+
27
+ Installation
28
+ ==================
29
+
30
+ To install the gem, use:
31
+
32
+ ```ssh
33
+ gem install tml
34
+ ```
35
+
36
+
37
+ Registering Your App
38
+ ===================================
39
+
40
+ Before you can proceed with the integration, please register with http://translationexchange.com and create a new application.
41
+
42
+ At the end of the registration process you will be given a key and a secret. You will need to enter them in the initialization function of the TML SDK.
43
+
44
+
45
+
46
+ Usage
47
+ ==================
48
+
49
+ The library can be invoked from the IRB. To use TML client you must require it, and instantiate the application with the key and secret of your app from translationexchange.com:
50
+
51
+ ```ruby
52
+ irb(main)> require 'tml'
53
+ irb(main)> app = Tml.session.init(KEY, SECRET)
54
+ ```
55
+
56
+ Now you can use the application to get any language registered with your app:
57
+
58
+ ```ruby
59
+ irb(main)> english = app.language('en-US')
60
+ irb(main)> russian = app.language('ru')
61
+ irb(main)> spanish = app.language('es')
62
+ irb(main)> chinese = app.language('zh')
63
+ ```
64
+
65
+ Simple example:
66
+
67
+ ```ruby
68
+ irb(main)> english.translate('Hello World')
69
+ => "Hello World"
70
+ irb(main)> russian.translate('Hello World')
71
+ => "Привет Мир"
72
+ irb(main)> spanish.translate('Hello World')
73
+ => "Hola Mundo"
74
+ irb(main)> chinese.translate('Hello World')
75
+ => "你好世界"
76
+ ```
77
+
78
+ Using description context:
79
+
80
+ ```ruby
81
+ irb(main)> russian.translate('Invite', 'An invitation')
82
+ => "Приглашение"
83
+ irb(main)> russian.translate('Invite', 'An action to invite')
84
+ => "Пригласить"
85
+ ```
86
+
87
+ Numeric rules with piped tokens:
88
+
89
+ ```ruby
90
+ irb(main)> english.translate('You have {count||message}.', :count => 1)
91
+ => "You have 1 message."
92
+ irb(main)> english.translate('You have {count||message}.', :count => 2)
93
+ => "You have 2 messages."
94
+
95
+ irb(main)> russian.translate('You have {count||message}.', :count => 1)
96
+ => "У вас есть 1 сообщение."
97
+ irb(main)> russian.translate('You have {count||message}.', :count => 2)
98
+ => "У вас есть 2 сообщения."
99
+ irb(main)> russian.translate('You have {count||message}.', :count => 5)
100
+ => "У вас есть 5 сообщений."
101
+ ```
102
+
103
+ Gender rules:
104
+
105
+ ```ruby
106
+ irb(main)> user = {:gender => :female, :name => "Anna"}
107
+ irb(main)> english.translate('{user} updated {user| his, her} profile.', :user => {:object => user, :attribute => :name})
108
+ => "Anna updated her profile."
109
+
110
+ irb(main)> russian.translate('{user} updated {user| his, her} profile.', :user => {:object => user, :attribute => :name})
111
+ => "Anna обновила свой профиль."
112
+
113
+ irb(main)> user = {:gender => :male, :name => "Michael"}
114
+ irb(main)> english.translate('{user} updated {user| his, her} profile.', :user => {:object => user, :attribute => :name})
115
+ => "Michael updated his profile."
116
+
117
+ irb(main)> russian.translate('{user} updated {user| his, her} profile.', :user => {:object => user, :attribute => :name})
118
+ => "Michael обновил свой профиль."
119
+ ```
120
+
121
+ Gender rules with language cases:
122
+
123
+ ```ruby
124
+ irb(main)> actor = {:gender => :female, :name => "Анна"}
125
+ irb(main)> target = {:gender => :male, :name => "Михаил"}
126
+ irb(main)> russian.translate('{actor} sent {target} a gift.', :actor => {:object => actor, :attribute => :name}, :target => {:object => target, :attribute => :name})
127
+ => "Анна послала подарок Михаилу."
128
+ irb(main)> russian.translate('{actor} sent {target} a gift.', :actor => {:object => target, :attribute => :name}, :target => {:object => actor, :attribute => :name})
129
+ => "Михаил послал подарок Анне."
130
+ ```
131
+
132
+ Decoration tokens:
133
+
134
+ ```ruby
135
+ irb(main)> english.translate("[bold: This text] should be bold", :bold => lambda{|text| "<strong>#{text}</strong>"})
136
+ => "<strong>This text</strong> should be bold"
137
+ irb(main)> russian.translate("[bold: This text] should be bold", :bold => lambda{|text| "<strong>#{text}</strong>"})
138
+ => "<strong>Этот текст</strong> должны быть жирным"
139
+ ```
140
+
141
+ Nested decoration tokens:
142
+
143
+ ```ruby
144
+ irb(main)> english.translate("[bold: Bold text [italic: with italic text]] together", :bold => "<b>{$0}</b>", :italic => "<i>{$0}</i>")
145
+ => "<b>Bold text <i>with italic text</i></b> together"
146
+ irb(main)> russian.translate("[bold: Bold text [italic: with italic text]] together", :bold => "<b>{$0}</b>", :italic => "<i>{$0}</i>")
147
+ => "<b>Жирный текст <i>с курсив</i></b> вместе"
148
+ ```
149
+
150
+ Data tokens with decoration tokens together:
151
+
152
+ ```ruby
153
+ irb(main)> user = {:gender => :male, :name => "Michael"}
154
+ irb(main)> english.translate("[bold: {user}] received [link: [bold: {count}] {count|message}]", :user => {:object => user, :attribute => :name}, :bold => "<b>{$0}</b>", :count => 5, :link => "<a href='url'>{$0}</a>")
155
+ => "<b>Michael</b> received <a href='url'><b>5</b> messages</a>"
156
+ irb(main)> russian.translate("[bold: {user}] received [link: [bold: {count}] {count|message}]", :user => {:object => user, :attribute => :name}, :bold => "<b>{$0}</b>", :count => 5, :link => "<a href='url'>{$0}</a>")
157
+ => "<b>Michael</b> получил <a href='url'><b>5</b> сообщений</a>"
158
+ ```
159
+
160
+ PS. The Russian translation on translationexchange.com could either be provided by a set of 6-9 simple translations for {genders}(male, female, unknown) * count{one, few, many} or by a single advanced translation
161
+ in the form of:
162
+
163
+ ```ruby
164
+ [bold: {user}] {user| male: получил, female: получила} [link: [bold: {count}] {count| one: сообщение, few: сообщения, other: сообщений}]
165
+ ```
166
+
167
+ Or in a simpler form:
168
+
169
+ ```ruby
170
+ [bold: {user}] {user| получил, получила} [link: [bold: {count}] {count| сообщение, сообщения, сообщений}]
171
+ ```
172
+
173
+ One of the advantages of using TML is the ability to easily switch token values. The above example in a text based email can reuse translations:
174
+
175
+ ```ruby
176
+ irb(main)> english.translate("[bold: {user}] received [link: [bold: {count}] {count|message}]", :user => {:object => user, :attribute => :name}, :count => 1, :bold => "{$0}", :link => "{$0}")
177
+ => "Michael received 1 message"
178
+
179
+ irb(main)> russian.translate("[bold: {user}] received [link: [bold: {count}] {count|message}]", :user => {:object => user, :attribute => :name}, :count => 1, :bold => "{$0}", :link => "{$0}")
180
+ => "Michael получил 1 сообщение"
181
+ ```
182
+
183
+ You should also notice that all of the translation keys you've been using in your experiments will be registered under your application by the translationexchange.com service. You can view them all at:
184
+
185
+ https://dashboard.translationexchange.com/
186
+
187
+ If any translation key you've tried to translate was missing a translation, you can manually translate it using the service (with the help of a machine translation suggestion).
188
+
189
+ ```ruby
190
+ irb(main)> russian.translate('This is a new phrase without translations')
191
+ => "This is a new phrase without translations"
192
+ ```
193
+
194
+ Then without leaving your IRB session, you can call the following method to reset your application cache:
195
+
196
+ ```ruby
197
+ irb(main)> app.reset_translation_cache
198
+ ```
199
+
200
+ Then you can just rerun the translation method with the missing translation and you should get back the translated value.
201
+
202
+ ```ruby
203
+ irb(main)> russian.translate('This is a new phrase without translations')
204
+ => "Это новая фраза без перевода"
205
+ ```
206
+
207
+ Links
208
+ ==================
209
+
210
+ * Register on TranslationExchange.com: http://translationexchange.com
211
+
212
+ * Read TranslationExchange's documentation: http://translationexchange.com/docs
213
+
214
+ * Follow TranslationExchange on Twitter: https://twitter.com/translationx
215
+
216
+ * Connect with TranslationExchange on Facebook: https://www.facebook.com/translationexchange
217
+
218
+ * If you have any questions or suggestions, contact us: feedback@translationexchange.com
219
+
220
+
221
+ Copyright and license
222
+ ==================
223
+
224
+ Copyright (c) 2015 Translation Exchange, Inc
225
+
226
+ Permission is hereby granted, free of charge, to any person obtaining
227
+ a copy of this software and associated documentation files (the
228
+ "Software"), to deal in the Software without restriction, including
229
+ without limitation the rights to use, copy, modify, merge, publish,
230
+ distribute, sublicense, and/or sell copies of the Software, and to
231
+ permit persons to whom the Software is furnished to do so, subject to
232
+ the following conditions:
233
+
234
+ The above copyright notice and this permission notice shall be
235
+ included in all copies or substantial portions of the Software.
236
+
237
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
238
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
239
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
240
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
241
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
242
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
243
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ Dir["#{File.dirname(__FILE__)}/tasks/**/*.rake"].sort.each { |ext| load ext }
8
+
9
+ task :default => :spec
data/lib/tml.rb ADDED
@@ -0,0 +1,56 @@
1
+ # encoding: UTF-8
2
+ #--
3
+ # Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
4
+ #
5
+ # _______ _ _ _ ______ _
6
+ # |__ __| | | | | (_) | ____| | |
7
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
8
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
9
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
10
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
11
+ # __/ |
12
+ # |___/
13
+ # Permission is hereby granted, free of charge, to any person obtaining
14
+ # a copy of this software and associated documentation files (the
15
+ # "Software"), to deal in the Software without restriction, including
16
+ # without limitation the rights to use, copy, modify, merge, publish,
17
+ # distribute, sublicense, and/or sell copies of the Software, and to
18
+ # permit persons to whom the Software is furnished to do so, subject to
19
+ # the following conditions:
20
+ #
21
+ # The above copyright notice and this permission notice shall be
22
+ # included in all copies or substantial portions of the Software.
23
+ #
24
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+ #++
32
+
33
+ module Tml
34
+ module Api end
35
+ module Tokens end
36
+ module Tokenizers end
37
+ module Rules end
38
+ module Decorators end
39
+ module CacheAdapters end
40
+ module Generators
41
+ module Cache
42
+ end
43
+ end
44
+ end
45
+
46
+ %w(tml/base.rb tml tml/api tml/rules_engine tml/tokens tml/tokenizers tml/decorators tml/cache_adapters tml/cache tml/cache/generators tml/ext tml/modules tml/generators/cache).each do |f|
47
+ if f.index('.rb')
48
+ require(File.expand_path(File.join(File.dirname(__FILE__), f)))
49
+ next
50
+ end
51
+
52
+ Dir[File.expand_path("#{File.dirname(__FILE__)}/#{f}/*.rb")].sort.each do |file|
53
+ require(file)
54
+ end
55
+ end
56
+
@@ -0,0 +1,206 @@
1
+ # encoding: UTF-8
2
+ #--
3
+ # Copyright (c) 2015 Translation Exchange, Inc
4
+ #
5
+ # _______ _ _ _ ______ _
6
+ # |__ __| | | | | (_) | ____| | |
7
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
8
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
9
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
10
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
11
+ # __/ |
12
+ # |___/
13
+ # Permission is hereby granted, free of charge, to any person obtaining
14
+ # a copy of this software and associated documentation files (the
15
+ # "Software"), to deal in the Software without restriction, including
16
+ # without limitation the rights to use, copy, modify, merge, publish,
17
+ # distribute, sublicense, and/or sell copies of the Software, and to
18
+ # permit persons to whom the Software is furnished to do so, subject to
19
+ # the following conditions:
20
+ #
21
+ # The above copyright notice and this permission notice shall be
22
+ # included in all copies or substantial portions of the Software.
23
+ #
24
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+ #++
32
+
33
+ require 'faraday'
34
+
35
+ class Tml::Api::Client < Tml::Base
36
+ API_HOST = 'https://api.translationexchange.com'
37
+ API_PATH = '/v1'
38
+
39
+ attributes :application
40
+
41
+ def access_token
42
+ return Tml::Session.access_token if Tml::Session.access_token
43
+
44
+ application.access_token ||= begin
45
+ token = Tml.cache.fetch("#{application.key}/access_token") do
46
+ api('oauth/token', {
47
+ :client_id => application.key,
48
+ :client_secret => application.secret,
49
+ :grant_type => :client_credentials
50
+ }, {:method => :post})
51
+ end
52
+ Tml::Session.access_token = token['access_token']
53
+ Tml::Session.access_token
54
+ end
55
+ end
56
+
57
+ def results(path, params = {}, opts = {})
58
+ get(path, params, opts)['results']
59
+ end
60
+
61
+ def get(path, params = {}, opts = {})
62
+ api(path, params, opts.merge(:method => :get))
63
+ end
64
+
65
+ def post(path, params = {}, opts = {})
66
+ api(path, params, opts.merge(:method => :post))
67
+ end
68
+
69
+ def put(path, params = {}, opts = {})
70
+ api(path, params, opts.merge(:method => :put))
71
+ end
72
+
73
+ def delete(path, params = {}, opts = {})
74
+ api(path, params, opts.merge(:method => :delete))
75
+ end
76
+
77
+ def self.error?(data)
78
+ not data['error'].nil?
79
+ end
80
+
81
+ def host
82
+ application.host || API_HOST
83
+ end
84
+
85
+ def connection
86
+ @connection ||= Faraday.new(:url => host) do |faraday|
87
+ faraday.request(:url_encoded) # form-encode POST params
88
+ # faraday.response :logger # log requests to STDOUT
89
+ faraday.adapter(Faraday.default_adapter) # make requests with Net::HTTP
90
+ end
91
+ end
92
+
93
+ def api(path, params = {}, opts = {})
94
+ if Tml.session.inline_mode?
95
+ return process_response(execute_request(path, params, opts), opts)
96
+ end
97
+
98
+ if opts[:method] == :get and opts[:cache_key]
99
+ data = Tml.cache.fetch(opts[:cache_key]) do
100
+ Tml.cache.read_only? ? {} : execute_request(path, params, opts)
101
+ end
102
+ process_response(data, opts)
103
+ else
104
+ process_response(execute_request(path, params, opts), opts)
105
+ end
106
+ end
107
+
108
+ def paginate(path, params = {}, opts = {})
109
+ data = get(path, params, opts.merge({'raw' => true}))
110
+
111
+ while data
112
+ if data['results'].is_a?(Array)
113
+ data['results'].each do |result|
114
+ yield(result)
115
+ end
116
+ else
117
+ yield(data['results'])
118
+ end
119
+
120
+ if data['pagination'] and data['pagination']['links']['next']
121
+ data = get(data['pagination']['links']['next'], {}, opts.merge({'raw' => true}))
122
+ else
123
+ data = nil
124
+ end
125
+ end
126
+ end
127
+
128
+ def prepare_api_path(path)
129
+ return path if path.index('oauth')
130
+ return path if path.match(/^https?:\/\//)
131
+ "#{API_PATH}#{path[0] == '/' ? '' : '/'}#{path}"
132
+ end
133
+
134
+ def execute_request(path, params = {}, opts = {})
135
+ response = nil
136
+ error = nil
137
+
138
+ # oauth path is separate from versioned APIs
139
+ path = prepare_api_path(path)
140
+ params = params.merge(:access_token => access_token) unless path.index('oauth')
141
+
142
+ if opts[:method] == :post
143
+ params = params.merge(:api_key => application.key)
144
+ end
145
+
146
+ Tml.logger.trace_api_call(path, params, opts) do
147
+ begin
148
+ if opts[:method] == :post
149
+ response = connection.post(path, params)
150
+ elsif opts[:method] == :put
151
+ response = connection.put(path, params)
152
+ elsif opts[:method] == :delete
153
+ response = connection.delete(path, params)
154
+ else
155
+ response = connection.get(path, params)
156
+ end
157
+ rescue Exception => ex
158
+ Tml.logger.error("Failed to execute request: #{ex.message[0..255]}")
159
+ error = ex
160
+ nil
161
+ end
162
+ end
163
+ raise Tml::Exception.new("Error: #{error}") if error
164
+
165
+ if response.status >= 500 and response.status < 600
166
+ raise Tml::Exception.new("Error: #{response.body}")
167
+ end
168
+
169
+ return if response.body.nil? or response.body == ''
170
+
171
+ begin
172
+ data = JSON.parse(response.body)
173
+ rescue Exception => ex
174
+ raise Tml::Exception.new("Failed to parse response: #{ex.message[0..255]}")
175
+ end
176
+
177
+ if data.is_a?(Hash) and not data['error'].nil?
178
+ raise Tml::Exception.new("Error: #{data['error']}")
179
+ end
180
+
181
+ data
182
+ end
183
+
184
+ def object_class(opts)
185
+ return unless opts[:class]
186
+ opts[:class].is_a?(String) ? opts[:class].constantize : opts[:class]
187
+ end
188
+
189
+ def process_response(data, opts)
190
+ return data if opts['raw']
191
+
192
+ if data.is_a?(Hash) and data['results']
193
+ #Tml.logger.debug("received #{data['results'].size} result(s)")
194
+ return data['results'] unless object_class(opts)
195
+ objects = []
196
+ data['results'].each do |data|
197
+ objects << object_class(opts).new(data.merge(opts[:attributes] || {}))
198
+ end
199
+ return objects
200
+ end
201
+
202
+ return data unless object_class(opts)
203
+ object_class(opts).new(data.merge(opts[:attributes] || {}))
204
+ end
205
+
206
+ end