tr8n_core 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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