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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +69 -0
- data/Rakefile +9 -0
- data/config/config.yml +34 -0
- data/config/tokens/data.yml +45 -0
- data/config/tokens/decorations.yml +37 -0
- data/lib/tr8n/application.rb +320 -0
- data/lib/tr8n/base.rb +123 -0
- data/lib/tr8n/cache.rb +144 -0
- data/lib/tr8n/cache_adapters/cdb.rb +74 -0
- data/lib/tr8n/cache_adapters/file.rb +70 -0
- data/lib/tr8n/cache_adapters/memcache.rb +91 -0
- data/lib/tr8n/cache_adapters/redis.rb +94 -0
- data/lib/tr8n/component.rb +68 -0
- data/lib/tr8n/config.rb +291 -0
- data/lib/tr8n/decorators/base.rb +35 -0
- data/lib/tr8n/decorators/default.rb +30 -0
- data/lib/tr8n/decorators/html.rb +63 -0
- data/lib/tr8n/exception.rb +26 -0
- data/lib/tr8n/language.rb +250 -0
- data/lib/tr8n/language_case.rb +116 -0
- data/lib/tr8n/language_case_rule.rb +85 -0
- data/lib/tr8n/language_context.rb +115 -0
- data/lib/tr8n/language_context_rule.rb +62 -0
- data/lib/tr8n/logger.rb +83 -0
- data/lib/tr8n/rules_engine/evaluator.rb +156 -0
- data/lib/tr8n/rules_engine/parser.rb +83 -0
- data/lib/tr8n/source.rb +95 -0
- data/lib/tr8n/tokens/data.rb +410 -0
- data/lib/tr8n/tokens/data_tokenizer.rb +82 -0
- data/lib/tr8n/tokens/decoration_tokenizer.rb +200 -0
- data/lib/tr8n/tokens/hidden.rb +48 -0
- data/lib/tr8n/tokens/method.rb +52 -0
- data/lib/tr8n/tokens/transform.rb +191 -0
- data/lib/tr8n/translation.rb +104 -0
- data/lib/tr8n/translation_key.rb +205 -0
- data/lib/tr8n/translator.rb +62 -0
- data/lib/tr8n/utils.rb +124 -0
- data/lib/tr8n_core/ext/array.rb +74 -0
- data/lib/tr8n_core/ext/date.rb +63 -0
- data/lib/tr8n_core/ext/fixnum.rb +39 -0
- data/lib/tr8n_core/ext/hash.rb +126 -0
- data/lib/tr8n_core/ext/string.rb +44 -0
- data/lib/tr8n_core/ext/time.rb +71 -0
- data/lib/tr8n_core/generators/cache/base.rb +85 -0
- data/lib/tr8n_core/generators/cache/cdb.rb +27 -0
- data/lib/tr8n_core/generators/cache/file.rb +69 -0
- data/lib/tr8n_core/modules/logger.rb +43 -0
- data/lib/tr8n_core/version.rb +27 -0
- data/lib/tr8n_core.rb +68 -0
- data/spec/application_spec.rb +228 -0
- data/spec/base_spec.rb +19 -0
- data/spec/config_spec.rb +16 -0
- data/spec/decorator_spec.rb +10 -0
- data/spec/decorators/base_spec.rb +14 -0
- data/spec/decorators/default_spec.rb +12 -0
- data/spec/decorators/html_spec.rb +50 -0
- data/spec/fixtures/application.json +112 -0
- data/spec/fixtures/languages/en-US.json +1424 -0
- data/spec/fixtures/languages/es.json +291 -0
- data/spec/fixtures/languages/ru.json +550 -0
- data/spec/fixtures/translations/ru/basic.json +56 -0
- data/spec/fixtures/translations/ru/counters.json +43 -0
- data/spec/fixtures/translations/ru/genders.json +171 -0
- data/spec/fixtures/translations/ru/last_names.txt +200 -0
- data/spec/fixtures/translations/ru/names.json +1 -0
- data/spec/fixtures/translations/ru/names.txt +458 -0
- data/spec/helper.rb +84 -0
- data/spec/language_case_rule_spec.rb +57 -0
- data/spec/language_case_spec.rb +58 -0
- data/spec/language_context_rule_spec.rb +73 -0
- data/spec/language_context_spec.rb +331 -0
- data/spec/language_spec.rb +16 -0
- data/spec/rules_engine/evaluator_spec.rb +148 -0
- data/spec/rules_engine/parser_spec.rb +29 -0
- data/spec/tokens/data_spec.rb +160 -0
- data/spec/tokens/data_tokenizer_spec.rb +29 -0
- data/spec/tokens/decoration_tokenizer_spec.rb +81 -0
- data/spec/tokens/hidden_spec.rb +24 -0
- data/spec/tokens/method_spec.rb +84 -0
- data/spec/tokens/transform_spec.rb +50 -0
- data/spec/translation_key_spec.rb +96 -0
- data/spec/translation_spec.rb +24 -0
- data/spec/utils_spec.rb +64 -0
- 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
|
+
[](https://travis-ci.org/tr8n/tr8n_ruby_core)
|
11
|
+
[](https://coveralls.io/r/tr8n/tr8n_ruby_core)
|
12
|
+
[](http://badge.fury.io/rb/tr8n_core)
|
13
|
+
[](https://www.versioneye.com/user/projects/52e36cc8ec1375c6f4000078)
|
14
|
+
[](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
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: "–" # –
|
9
|
+
mdash: "—" # —
|
10
|
+
iexcl: "¡" # ¡
|
11
|
+
iquest: "¿" # ¿
|
12
|
+
quot: """ # "
|
13
|
+
ldquo: "“" # “
|
14
|
+
rdquo: "”" # ”
|
15
|
+
lsquo: "‘" # ‘
|
16
|
+
rsquo: "’" # ’
|
17
|
+
laquo: "«" # «
|
18
|
+
raquo: "»" # »
|
19
|
+
nbsp: " " # space
|
20
|
+
lsaquo: "‹" # ‹
|
21
|
+
rsaquo: "›" # ›
|
22
|
+
br: "<br/>" # line break
|
23
|
+
lbrace: "{"
|
24
|
+
rbrace: "}"
|
25
|
+
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
|