tr8n_core 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![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
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
|