tr8n_core 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +69 -0
  4. data/Rakefile +9 -0
  5. data/config/config.yml +34 -0
  6. data/config/tokens/data.yml +45 -0
  7. data/config/tokens/decorations.yml +37 -0
  8. data/lib/tr8n/application.rb +320 -0
  9. data/lib/tr8n/base.rb +123 -0
  10. data/lib/tr8n/cache.rb +144 -0
  11. data/lib/tr8n/cache_adapters/cdb.rb +74 -0
  12. data/lib/tr8n/cache_adapters/file.rb +70 -0
  13. data/lib/tr8n/cache_adapters/memcache.rb +91 -0
  14. data/lib/tr8n/cache_adapters/redis.rb +94 -0
  15. data/lib/tr8n/component.rb +68 -0
  16. data/lib/tr8n/config.rb +291 -0
  17. data/lib/tr8n/decorators/base.rb +35 -0
  18. data/lib/tr8n/decorators/default.rb +30 -0
  19. data/lib/tr8n/decorators/html.rb +63 -0
  20. data/lib/tr8n/exception.rb +26 -0
  21. data/lib/tr8n/language.rb +250 -0
  22. data/lib/tr8n/language_case.rb +116 -0
  23. data/lib/tr8n/language_case_rule.rb +85 -0
  24. data/lib/tr8n/language_context.rb +115 -0
  25. data/lib/tr8n/language_context_rule.rb +62 -0
  26. data/lib/tr8n/logger.rb +83 -0
  27. data/lib/tr8n/rules_engine/evaluator.rb +156 -0
  28. data/lib/tr8n/rules_engine/parser.rb +83 -0
  29. data/lib/tr8n/source.rb +95 -0
  30. data/lib/tr8n/tokens/data.rb +410 -0
  31. data/lib/tr8n/tokens/data_tokenizer.rb +82 -0
  32. data/lib/tr8n/tokens/decoration_tokenizer.rb +200 -0
  33. data/lib/tr8n/tokens/hidden.rb +48 -0
  34. data/lib/tr8n/tokens/method.rb +52 -0
  35. data/lib/tr8n/tokens/transform.rb +191 -0
  36. data/lib/tr8n/translation.rb +104 -0
  37. data/lib/tr8n/translation_key.rb +205 -0
  38. data/lib/tr8n/translator.rb +62 -0
  39. data/lib/tr8n/utils.rb +124 -0
  40. data/lib/tr8n_core/ext/array.rb +74 -0
  41. data/lib/tr8n_core/ext/date.rb +63 -0
  42. data/lib/tr8n_core/ext/fixnum.rb +39 -0
  43. data/lib/tr8n_core/ext/hash.rb +126 -0
  44. data/lib/tr8n_core/ext/string.rb +44 -0
  45. data/lib/tr8n_core/ext/time.rb +71 -0
  46. data/lib/tr8n_core/generators/cache/base.rb +85 -0
  47. data/lib/tr8n_core/generators/cache/cdb.rb +27 -0
  48. data/lib/tr8n_core/generators/cache/file.rb +69 -0
  49. data/lib/tr8n_core/modules/logger.rb +43 -0
  50. data/lib/tr8n_core/version.rb +27 -0
  51. data/lib/tr8n_core.rb +68 -0
  52. data/spec/application_spec.rb +228 -0
  53. data/spec/base_spec.rb +19 -0
  54. data/spec/config_spec.rb +16 -0
  55. data/spec/decorator_spec.rb +10 -0
  56. data/spec/decorators/base_spec.rb +14 -0
  57. data/spec/decorators/default_spec.rb +12 -0
  58. data/spec/decorators/html_spec.rb +50 -0
  59. data/spec/fixtures/application.json +112 -0
  60. data/spec/fixtures/languages/en-US.json +1424 -0
  61. data/spec/fixtures/languages/es.json +291 -0
  62. data/spec/fixtures/languages/ru.json +550 -0
  63. data/spec/fixtures/translations/ru/basic.json +56 -0
  64. data/spec/fixtures/translations/ru/counters.json +43 -0
  65. data/spec/fixtures/translations/ru/genders.json +171 -0
  66. data/spec/fixtures/translations/ru/last_names.txt +200 -0
  67. data/spec/fixtures/translations/ru/names.json +1 -0
  68. data/spec/fixtures/translations/ru/names.txt +458 -0
  69. data/spec/helper.rb +84 -0
  70. data/spec/language_case_rule_spec.rb +57 -0
  71. data/spec/language_case_spec.rb +58 -0
  72. data/spec/language_context_rule_spec.rb +73 -0
  73. data/spec/language_context_spec.rb +331 -0
  74. data/spec/language_spec.rb +16 -0
  75. data/spec/rules_engine/evaluator_spec.rb +148 -0
  76. data/spec/rules_engine/parser_spec.rb +29 -0
  77. data/spec/tokens/data_spec.rb +160 -0
  78. data/spec/tokens/data_tokenizer_spec.rb +29 -0
  79. data/spec/tokens/decoration_tokenizer_spec.rb +81 -0
  80. data/spec/tokens/hidden_spec.rb +24 -0
  81. data/spec/tokens/method_spec.rb +84 -0
  82. data/spec/tokens/transform_spec.rb +50 -0
  83. data/spec/translation_key_spec.rb +96 -0
  84. data/spec/translation_spec.rb +24 -0
  85. data/spec/utils_spec.rb +64 -0
  86. metadata +176 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a162e743cb6de4c17ba8f8f37d3213bdd9a99ba4
4
+ data.tar.gz: 8e94f38ea626036fd739a19d2c83c35fea72fc25
5
+ SHA512:
6
+ metadata.gz: 310264af7f85f21dae165ddb1b269218fc3941716292173251b198163fae28c9f561133f1d5efdcd86f27ce6e7b640605e600036e2150e3b8b8141113093b7d4
7
+ data.tar.gz: 3ec53152d4ce2a91302ba8737bee70773faad63342e0fc1c1d5a1166b8175680ac0d011cae0e91aeee967a451c2d1b75e2a1ae21edc90d85e41d867c9563321b
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Michael Berkovich, tr8nhub.com
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,69 @@
1
+ <p align="center">
2
+ <img src="https://raw.github.com/tr8n/tr8n/master/doc/screenshots/tr8nlogo.png">
3
+ </p>
4
+
5
+ Tr8n Core Library For Ruby
6
+ ==================
7
+
8
+ This library provides a set of Tr8n client side tools that can be used in any ruby based project.
9
+
10
+ [![Build Status](https://travis-ci.org/tr8n/tr8n_ruby_core.png?branch=master)](https://travis-ci.org/tr8n/tr8n_ruby_core)
11
+ [![Coverage Status](https://coveralls.io/repos/tr8n/tr8n_ruby_core/badge.png)](https://coveralls.io/r/tr8n/tr8n_ruby_core)
12
+ [![Gem Version](https://badge.fury.io/rb/tr8n_core.png)](http://badge.fury.io/rb/tr8n_core)
13
+ [![Dependency Status](https://www.versioneye.com/user/projects/52e36cc8ec1375c6f4000078/badge.png)](https://www.versioneye.com/user/projects/52e36cc8ec1375c6f4000078)
14
+ [![Project status](http://stillmaintained.com/tr8n/tr8n_ruby_core.png)](http://stillmaintained.com/tr8n/tr8n_ruby_core.png)
15
+
16
+ Installation
17
+ ==================
18
+
19
+ To install the gem, use:
20
+
21
+ gem install tr8n_core
22
+
23
+
24
+ Usage
25
+ ==================
26
+
27
+ The library can be invoked from the IRB. To use tr8n client you must first require it, and instantiat the application with the key and secret you aquired during registration:
28
+
29
+ irb(main):001:0> require 'tr8n_core'
30
+ irb(main):002:0> app = Tr8n::Application.init(TR8N_SERVICE_HOST, APPLICATION_KEY, SECRET)
31
+ irb(main):003:0> russian = app.language('ru')
32
+
33
+
34
+ Simplest example:
35
+
36
+ irb(main):004:0> russian.translate('Hello World')
37
+ => "Привет Мир"
38
+
39
+ Using context:
40
+
41
+ irb(main):004:0> russian.translate('Invite', 'An invitation')
42
+ => "Приглашение"
43
+ irb(main):005:0> russian.translate('Invite', 'An action to invite')
44
+ => "Пригласить"
45
+
46
+ Basic numeric rules with transform tokens:
47
+
48
+ irb(main):006:0> russian.translate('You have {count||message}.', :count => 1)
49
+ => "У вас есть 1 сообщение."
50
+ irb(main):007:0> russian.translate('You have {count||message}.', :count => 2)
51
+ => "У вас есть 2 сообщения."
52
+ irb(main):008:0> russian.translate('You have {count||message}.', :count => 5)
53
+ => "У вас есть 5 сообщений."
54
+
55
+ Basic gender rules with language cases:
56
+
57
+ irb(main):009:0> actor = {:gender => :female, :name => "Анна"}
58
+ irb(main):010:0> target = {:gender => :male, :name => "Михаил"}
59
+ irb(main):011:0> russian.translate('{actor} sent {target} a gift.', :actor => {:object => actor, :attribute => :name}, :target => {:object => target, :attribute => :name})
60
+ => "Анна послала подарок Михаилу."
61
+ irb(main):011:0> russian.translate('{actor} sent {target} a gift.', :actor => {:object => target, :attribute => :name}, :target => {:object => actor, :attribute => :name})
62
+ => "Михаил послал подарок Анне."
63
+
64
+ Tr8n
65
+ ==================
66
+
67
+ To read more about TML (Translation Markup Language) syntax, please visit http://wiki.tr8nhub.com
68
+
69
+ If you are planning to use Tr8n in Rails application, you can use the tr8n_rails_client_sdk gem instead.
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/config/config.yml ADDED
@@ -0,0 +1,34 @@
1
+ defaults:
2
+ enabled: true
3
+ default_locale: en-US
4
+ default_level: 0
5
+ application:
6
+ host: http://localhost:3000
7
+ key: default
8
+ secret: 12345
9
+ logger:
10
+ enabled: true
11
+ path: /log/tr8n.log
12
+ cache:
13
+ enabled: false
14
+ adapter: file
15
+ path: /cache
16
+ version: 1
17
+ timeout: 3600
18
+ host: localhost:11211
19
+ localization:
20
+ default_day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
21
+ default_abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
22
+ default_month_names: [January, February, March, April, May, June, July, August, September, October, November, December]
23
+ default_abbr_month_names: [Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
24
+ custom_date_formats:
25
+ default: '%m/%d/%Y' # 07/4/2008
26
+ short_numeric: '%m/%d' # 07/4
27
+ short_numeric_year: '%m/%d/%y' # 07/4/08
28
+ long_numeric: '%m/%d/%Y' # 07/4/2008
29
+ verbose: '%A, %B %d, %Y' # Friday, July 4, 2008
30
+ monthname: '%B %d' # July 4
31
+ monthname_year: '%B %d, %Y' # July 4, 2008
32
+ monthname_abbr: '%b %d' # Jul 4
33
+ monthname_abbr_year: '%b %d, %Y' # Jul 4, 2008
34
+ date_time: '%m/%d/%Y at %H:%M' # 01/03/1010 at 5:30
@@ -0,0 +1,45 @@
1
+ #############################################################################
2
+ #
3
+ # Tr8n Default Data Tokens
4
+ #
5
+ #############################################################################
6
+
7
+ html:
8
+ ndash: "&ndash;" # –
9
+ mdash: "&mdash;" # —
10
+ iexcl: "&iexcl;" # ¡
11
+ iquest: "&iquest;" # ¿
12
+ quot: "&quot;" # "
13
+ ldquo: "&ldquo;" # “
14
+ rdquo: "&rdquo;" # ”
15
+ lsquo: "&lsquo;" # ‘
16
+ rsquo: "&rsquo;" # ’
17
+ laquo: "&laquo;" # «
18
+ raquo: "&raquo;" # »
19
+ nbsp: "&nbsp;" # space
20
+ lsaquo: "&lsaquo;" # ‹
21
+ rsaquo: "&rsaquo;" # ›
22
+ br: "<br/>" # line break
23
+ lbrace: "{"
24
+ rbrace: "}"
25
+ trade: "&trade;" # TM
26
+
27
+ plain:
28
+ ndash: "–" # –
29
+ mdash: "—" # —
30
+ iexcl: "¡" # ¡
31
+ iquest: "¿" # ¿
32
+ quot: '"' # "
33
+ ldquo: "“" # “
34
+ rdquo: "”" # ”
35
+ lsquo: "‘" # ‘
36
+ rsquo: "’" # ’
37
+ laquo: "«" # «
38
+ raquo: "»" # »
39
+ nbsp: " " # space
40
+ lsaquo: "‹" # ‹
41
+ rsaquo: "›" # ›
42
+ br: "\n" # line break
43
+ lbrace: "{"
44
+ rbrace: "}"
45
+ trade: "™" # TM
@@ -0,0 +1,37 @@
1
+ #############################################################################
2
+ #
3
+ # Tr8n Default Decoration Tokens
4
+ #
5
+ #############################################################################
6
+
7
+ html:
8
+ strong: "<strong>{$0}</strong>"
9
+ bold: "<strong>{$0}</strong>"
10
+ b: "<strong>{$0}</strong>"
11
+ em: "<em>{$0}</em>"
12
+ italic: "<i>{$0}</i>"
13
+ i: "<i>{$0}</i>"
14
+ link: "<a href='{$href}'>{$0}</a>"
15
+ br: "<br>{$0}"
16
+ strike: "<strike>{$0}</strike>"
17
+ div: "<div id='{$id}' class='{$class}' style='{$style}'>{$0}</div>"
18
+ span: "<span id='{$id}' class='{$class}' style='{$style}'>{$0}</span>"
19
+ h1: "<h1>{$0}</h1>"
20
+ h2: "<h2>{$0}</h2>"
21
+ h3: "<h3>{$0}</h3>"
22
+
23
+ plain:
24
+ strong: "{$0}"
25
+ bold: "{$0}"
26
+ b: "{$0}"
27
+ em: "{$0}"
28
+ italic: "{$0}"
29
+ i: "{$0}"
30
+ link: "{$0}:{$1}"
31
+ br: "\n{$0}"
32
+ strike: "{$0}"
33
+ div: "{$0}"
34
+ span: "{$0}"
35
+ h1: "{$0}"
36
+ h2: "{$0}"
37
+ h3: "{$0}"
@@ -0,0 +1,320 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'faraday'
25
+
26
+ API_PATH = '/tr8n/api/'
27
+
28
+ class Tr8n::Application < Tr8n::Base
29
+ attributes :host, :key, :secret, :access_token, :name, :description, :threshold, :default_locale, :default_level
30
+ has_many :features, :languages, :sources, :components, :tokens
31
+
32
+ def self.init(host, key, secret, options = {})
33
+ options[:definition] = true if options[:definition].nil?
34
+
35
+ Tr8n.cache.reset_version
36
+
37
+ Tr8n.logger.info("Initializing application...")
38
+
39
+ Tr8n.config.application = Tr8n.cache.fetch(cache_key(key)) do
40
+ # get access token and store it in the application
41
+ access_token = api("oauth/request_token", {:client_id => key, :client_secret => secret, :grant_type => :client_credentials}, {:host => host})
42
+
43
+ # get application details
44
+ api("application", {:client_id => key, :definition => options[:definition]}, {:host => host, :access_token => access_token["access_token"], :class => Tr8n::Application, :attributes => {
45
+ :host => host,
46
+ :key => key,
47
+ :secret => secret,
48
+ :access_token => access_token["access_token"]
49
+ }})
50
+ end
51
+ end
52
+
53
+ def initialize(attrs = {})
54
+ super
55
+
56
+ self.attributes[:languages] = []
57
+ if hash_value(attrs, :languages)
58
+ self.attributes[:languages] = hash_value(attrs, :languages).collect{ |l| Tr8n::Language.new(l.merge(:application => self)) }
59
+ end
60
+
61
+ self.attributes[:sources] = []
62
+ if hash_value(attrs, :sources)
63
+ self.attributes[:sources] = hash_value(attrs, :sources).collect{ |l| Tr8n::Source.new(l.merge(:application => self)) }
64
+ end
65
+
66
+ self.attributes[:components] = []
67
+ if hash_value(attrs, :components)
68
+ self.attributes[:components] = hash_value(attrs, :components).collect{ |l| Tr8n::Component.new(l.merge(:application => self)) }
69
+ end
70
+
71
+ @translation_keys = {}
72
+
73
+ @languages_by_locale = nil
74
+ @sources_by_key = nil
75
+ @components_by_key = nil
76
+ @missing_keys_by_sources = nil
77
+ end
78
+
79
+ def language(locale = nil, fetch = true)
80
+ locale ||= default_locale || Tr8n.config.default_locale
81
+
82
+ @languages_by_locale ||= {}
83
+ return @languages_by_locale[locale] if @languages_by_locale[locale]
84
+
85
+ if Tr8n.config.cache_enabled?
86
+ language = Tr8n.cache.fetch(Tr8n::Language.cache_key(locale))
87
+ if language
88
+ language.application = self
89
+ @languages_by_locale[locale] = language
90
+ return language
91
+ end
92
+ end
93
+
94
+ return nil unless fetch
95
+
96
+ # for translator languages will continue to build application cache
97
+ @languages_by_locale[locale] = get("language", {:locale => locale}, {:class => Tr8n::Language, :attributes => {:application => self}})
98
+
99
+ if Tr8n.config.cache_enabled? and not Tr8n.cache.read_only?
100
+ Tr8n.cache.store(Tr8n::Language.cache_key(locale), @languages_by_locale[locale])
101
+ end
102
+
103
+ @languages_by_locale[locale]
104
+ end
105
+
106
+ def locales
107
+ @locales ||= languages.collect{|lang| lang.locale}
108
+ end
109
+
110
+ # Mostly used for testing
111
+ def add_language(new_language)
112
+ lang = language(new_language.locale, false)
113
+ return lang if lang
114
+
115
+ new_language.application = self
116
+ self.languages << new_language
117
+ @languages_by_locale[new_language.locale] = new_language
118
+ new_language
119
+ end
120
+
121
+ def source(key, register = true)
122
+ key = key.source if key.is_a?(Tr8n::Source)
123
+
124
+ @sources_by_key ||= begin
125
+ srcs = {}
126
+ (sources || []).each do |src|
127
+ srcs[src.source] = src
128
+ end
129
+ srcs
130
+ end
131
+
132
+ return @sources_by_key[key] if @sources_by_key[key]
133
+ return nil unless register
134
+
135
+ @sources_by_key[key] ||= post("source/register", {:source => key}, {:class => Tr8n::Source, :attributes => {:application => self}})
136
+ end
137
+
138
+ def component(key, register = true)
139
+ key = key.key if key.is_a?(Tr8n::Component)
140
+
141
+ @components_by_key ||= begin
142
+ cmps = {}
143
+ (components || []).each do |cmp|
144
+ cmps[cmp.key] = cmp
145
+ end
146
+ cmps
147
+ end
148
+
149
+ return @components_by_key[key] if @components_by_key[key]
150
+ return nil unless register
151
+
152
+ @components_by_key[key] ||= post("component/register", {:component => key}, {:class => Tr8n::Component, :attributes => {:application => self}})
153
+ end
154
+
155
+ def translation_keys
156
+ @translation_keys ||= {}
157
+ end
158
+
159
+ def translation_key(key)
160
+ translation_keys[key]
161
+ end
162
+
163
+ def cache_translation_key(tkey)
164
+ cached_key = translation_key(tkey.key)
165
+
166
+ if cached_key
167
+ # move translations from tkey to the cached key
168
+ tkey.translations.each do |locale, translations|
169
+ cached_key.set_language_translations(language(locale), translations)
170
+ end
171
+ return cached_key
172
+ end
173
+
174
+ tkey.set_application(self)
175
+ @translation_keys[tkey.key] = tkey
176
+ end
177
+
178
+ def cache_translation_keys(tkeys)
179
+ tkeys.each do |tkey|
180
+ cache_translation_key(tkey)
181
+ end
182
+ end
183
+
184
+ def register_missing_key(tkey, source)
185
+ @missing_keys_by_sources ||= {}
186
+ @missing_keys_by_sources[source.source] ||= {}
187
+ @missing_keys_by_sources[source.source][tkey.key] ||= tkey
188
+ end
189
+
190
+ def submit_missing_keys
191
+ return if @missing_keys_by_sources.nil? or @missing_keys_by_sources.empty?
192
+ params = []
193
+ @missing_keys_by_sources.each do |source, keys|
194
+ next unless keys.values.any?
195
+ params << {:source => source, :keys => keys.values.collect{|tkey| tkey.to_hash(:label, :description, :locale, :level)}}
196
+ end
197
+ post('source/register_keys', {:source_keys => params.to_json}, :method => :post)
198
+ @missing_keys_by_sources = nil
199
+ end
200
+
201
+ def featured_languages
202
+ @featured_languages ||= begin
203
+ locales = Tr8n.cache.fetch("featured_locales") do
204
+ get("application/featured_locales")
205
+ end
206
+ # use app languages, there is no need for rules for this call
207
+ (locales.nil? or locales.empty?) ? [] : languages.select{|l| locales.include?(l.locale)}
208
+ end
209
+ end
210
+
211
+ def translators
212
+ get("application/translators", {}, {:class => Tr8n::Translator, :attributes => {:application => self}})
213
+ end
214
+
215
+ def default_decoration_token(token)
216
+ hash_value(tokens, "decoration.#{token.to_s}")
217
+ end
218
+
219
+ def default_data_token(token)
220
+ hash_value(tokens, "data.#{token.to_s}")
221
+ end
222
+
223
+ def js_boot_url
224
+ "#{host}/tr8n/api/proxy/boot.js?client_id=#{key}"
225
+ end
226
+
227
+ def feature_enabled?(key)
228
+ hash_value(features, key.to_s)
229
+ end
230
+
231
+ #######################################################################################################
232
+ ## Cache Methods
233
+ #######################################################################################################
234
+
235
+ def self.cache_prefix
236
+ 'a@'
237
+ end
238
+
239
+ def self.cache_key(key)
240
+ "#{cache_prefix}_[#{key}]"
241
+ end
242
+
243
+ def to_cache_hash
244
+ hash = to_hash(:host, :key, :secret, :access_token, :name, :description, :threshold, :default_locale, :default_level, :features)
245
+ hash["languages"] = []
246
+ languages.each do |lang|
247
+ hash["languages"] << lang.to_hash(:locale, :name, :english_name, :native_name, :right_to_left, :flag_url)
248
+ end
249
+ hash
250
+ end
251
+
252
+ #######################################################################################################
253
+ ## API Methods
254
+ #######################################################################################################
255
+ ## TODO: maybe caching can be done generically on the API level during gets?
256
+ ## TODO: think about it...
257
+
258
+ def get(path, params = {}, opts = {})
259
+ api(path, params, opts)
260
+ end
261
+
262
+ def post(path, params = {}, opts = {})
263
+ api(path, params, opts.merge(:method => :post))
264
+ end
265
+
266
+ def self.error?(data)
267
+ not data["error"].nil?
268
+ end
269
+
270
+ def api(path, params = {}, opts = {})
271
+ #:access_token => access_token,
272
+ params = params.merge(:client_id => key, :t => Time.now.to_i)
273
+ self.class.api(path, params, opts.merge(:host => self.host))
274
+ end
275
+
276
+ def self.api(path, params = {}, opts = {})
277
+ Tr8n.logger.trace_api_call(path, params) do
278
+ conn = Faraday.new(:url => opts[:host]) do |faraday|
279
+ faraday.request(:url_encoded) # form-encode POST params
280
+ # faraday.response :logger # log requests to STDOUT
281
+ faraday.adapter(Faraday.default_adapter) # make requests with Net::HTTP
282
+ end
283
+
284
+ if opts[:method] == :post
285
+ response = conn.post("#{API_PATH}#{path}", params)
286
+ else
287
+ response = conn.get("#{API_PATH}#{path}", params)
288
+ end
289
+
290
+ data = JSON.parse(response.body)
291
+
292
+ unless data["error"].nil?
293
+ raise Tr8n::Exception.new("Error: #{data["error"]}")
294
+ end
295
+
296
+ process_response(data, opts)
297
+ end
298
+ end
299
+
300
+ def self.object_class(opts)
301
+ return unless opts[:class]
302
+ opts[:class].is_a?(String) ? opts[:class].constantize : opts[:class]
303
+ end
304
+
305
+ def self.process_response(data, opts)
306
+ if data["results"]
307
+ Tr8n.logger.debug("recieved #{data["results"].size} result(s)")
308
+ return data["results"] unless object_class(opts)
309
+ objects = []
310
+ data["results"].each do |data|
311
+ objects << object_class(opts).new(data.merge(opts[:attributes] || {}))
312
+ end
313
+ return objects
314
+ end
315
+
316
+ return data unless object_class(opts)
317
+ Tr8n.logger.debug("constructing #{object_class(opts).name}")
318
+ object_class(opts).new(data.merge(opts[:attributes] || {}))
319
+ end
320
+ end
data/lib/tr8n/base.rb ADDED
@@ -0,0 +1,123 @@
1
+ #--
2
+ # Copyright (c) 2013 Michael Berkovich, tr8nhub.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ class Tr8n::Base
25
+ attr_reader :attributes
26
+
27
+ def initialize(attrs = {})
28
+ @attributes = {}
29
+ attrs.each do |key, value|
30
+ # pp [self.class.name, key, self.class.attributes, self.class.attributes.include?(key.to_sym)]
31
+ next unless self.class.attributes.include?(key.to_sym)
32
+ @attributes[key.to_sym] = value
33
+ end
34
+ end
35
+
36
+ def self.attributes(*attrs)
37
+ @attribute_names ||= []
38
+ @attribute_names += attrs.collect{|a| a.to_sym} unless attrs.nil?
39
+ @attribute_names
40
+ end
41
+ def self.belongs_to(*attrs) self.attributes(*attrs); end
42
+ def self.has_many(*attrs) self.attributes(*attrs); end
43
+
44
+ def self.thread_safe_attributes(*attrs)
45
+ @thread_safe_attribute_names ||= []
46
+ @thread_safe_attribute_names += attrs.collect{|a| a.to_sym} unless attrs.nil?
47
+ @thread_safe_attribute_names
48
+ end
49
+
50
+ def method_missing(meth, *args, &block)
51
+ method_name = meth.to_s
52
+ method_suffix = method_name[-1, 1]
53
+ method_key = method_name.to_sym
54
+ if ['=', '?'].include?(method_suffix)
55
+ method_key = method_name[0..-2].to_sym
56
+ end
57
+
58
+ if self.class.attributes.index(method_key)
59
+ if method_name[-1, 1] == '='
60
+ attributes[method_key] = args.first
61
+ return attributes[method_key]
62
+ end
63
+ return attributes[method_key]
64
+ end
65
+
66
+ if self.class.thread_safe_attributes.index(method_key)
67
+ if method_name[-1, 1] == '='
68
+ Thread.current[method_key] = args.first
69
+ return Thread.current[method_key]
70
+ end
71
+ return Thread.current[method_key]
72
+ end
73
+
74
+ super
75
+ end
76
+
77
+ def self.hash_value(hash, key, opts = {})
78
+ return nil unless hash.is_a?(Hash)
79
+ return hash[key.to_s] || hash[key.to_sym] if opts[:whole]
80
+
81
+ value = hash
82
+ key.to_s.split('.').each do |part|
83
+ return nil unless value.is_a?(Hash)
84
+ value = value[part.to_sym] || value[part.to_s]
85
+ end
86
+ value
87
+ end
88
+
89
+ def hash_value(hash, key, opts = {})
90
+ self.class.hash_value(hash, key, opts)
91
+ end
92
+
93
+ def reset!
94
+ self.class.thread_safe_attributes.each do |key|
95
+ Thread.current[key] = nil
96
+ end
97
+ end
98
+
99
+ def to_hash(*attrs)
100
+ if attrs.nil? or attrs.empty?
101
+ # default hashing only includes basic types
102
+ keys = []
103
+ self.class.attributes.each do |key|
104
+ value = attributes[key]
105
+ next if value.kind_of?(Tr8n::Base) or value.kind_of?(Hash) or value.kind_of?(Array)
106
+ keys << key
107
+ end
108
+ else
109
+ keys = attrs
110
+ end
111
+
112
+ hash = {}
113
+ keys.each do |key|
114
+ hash[key] = attributes[key]
115
+ end
116
+
117
+ proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&proc); nil) : v.nil? }
118
+ hash.delete_if(&proc)
119
+
120
+ hash
121
+ end
122
+
123
+ end