tml 4.3.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 +243 -0
- data/Rakefile +9 -0
- data/lib/tml.rb +56 -0
- data/lib/tml/api/client.rb +206 -0
- data/lib/tml/api/post_office.rb +71 -0
- data/lib/tml/application.rb +254 -0
- data/lib/tml/base.rb +116 -0
- data/lib/tml/cache.rb +143 -0
- data/lib/tml/cache_adapters/file.rb +89 -0
- data/lib/tml/cache_adapters/memcache.rb +104 -0
- data/lib/tml/cache_adapters/memory.rb +85 -0
- data/lib/tml/cache_adapters/redis.rb +108 -0
- data/lib/tml/config.rb +410 -0
- data/lib/tml/decorators/base.rb +52 -0
- data/lib/tml/decorators/default.rb +43 -0
- data/lib/tml/decorators/html.rb +102 -0
- data/lib/tml/exception.rb +35 -0
- data/lib/tml/ext/array.rb +86 -0
- data/lib/tml/ext/date.rb +99 -0
- data/lib/tml/ext/fixnum.rb +47 -0
- data/lib/tml/ext/hash.rb +99 -0
- data/lib/tml/ext/string.rb +56 -0
- data/lib/tml/ext/time.rb +89 -0
- data/lib/tml/generators/cache/base.rb +117 -0
- data/lib/tml/generators/cache/file.rb +159 -0
- data/lib/tml/language.rb +175 -0
- data/lib/tml/language_case.rb +105 -0
- data/lib/tml/language_case_rule.rb +76 -0
- data/lib/tml/language_context.rb +117 -0
- data/lib/tml/language_context_rule.rb +56 -0
- data/lib/tml/languages/en.json +1363 -0
- data/lib/tml/logger.rb +109 -0
- data/lib/tml/rules_engine/evaluator.rb +162 -0
- data/lib/tml/rules_engine/parser.rb +65 -0
- data/lib/tml/session.rb +199 -0
- data/lib/tml/source.rb +106 -0
- data/lib/tml/tokenizers/data.rb +96 -0
- data/lib/tml/tokenizers/decoration.rb +204 -0
- data/lib/tml/tokenizers/dom.rb +346 -0
- data/lib/tml/tokens/data.rb +403 -0
- data/lib/tml/tokens/method.rb +61 -0
- data/lib/tml/tokens/transform.rb +223 -0
- data/lib/tml/translation.rb +67 -0
- data/lib/tml/translation_key.rb +178 -0
- data/lib/tml/translator.rb +47 -0
- data/lib/tml/utils.rb +130 -0
- data/lib/tml/version.rb +34 -0
- metadata +121 -0
data/lib/tml/logger.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2015 Translation Exchange, Inc
|
4
|
+
#
|
5
|
+
# _______ _ _ _ ______ _
|
6
|
+
# |__ __| | | | | (_) | ____| | |
|
7
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
8
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
9
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
10
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
11
|
+
# __/ |
|
12
|
+
# |___/
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
# a copy of this software and associated documentation files (the
|
15
|
+
# "Software"), to deal in the Software without restriction, including
|
16
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
# the following conditions:
|
20
|
+
#
|
21
|
+
# The above copyright notice and this permission notice shall be
|
22
|
+
# included in all copies or substantial portions of the Software.
|
23
|
+
#
|
24
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
#++
|
32
|
+
|
33
|
+
require 'logger'
|
34
|
+
|
35
|
+
module Tml
|
36
|
+
|
37
|
+
def self.logger
|
38
|
+
@logger ||= begin
|
39
|
+
logfile_path = File.expand_path(Tml.config.logger[:path])
|
40
|
+
logfile_dir = logfile_path.split("/")[0..-2].join("/")
|
41
|
+
FileUtils.mkdir_p(logfile_dir) unless File.exist?(logfile_dir)
|
42
|
+
logfile = File.open(logfile_path, 'a')
|
43
|
+
logfile.sync = true
|
44
|
+
Tml::Logger.new(logfile)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Logger < ::Logger
|
49
|
+
|
50
|
+
def format_message(severity, timestamp, progname, msg)
|
51
|
+
return "" unless Tml.config.logger[:enabled]
|
52
|
+
# TODO: check for severity/level
|
53
|
+
"[#{timestamp.strftime("%D %T")}]: #{" " * stack.size}#{msg}\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
def add(severity, message = nil, progname = nil, &block)
|
57
|
+
return unless Tml.config.logger[:enabled]
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
61
|
+
def stack
|
62
|
+
@stack ||= []
|
63
|
+
end
|
64
|
+
|
65
|
+
def trace_api_call(path, params, opts = {})
|
66
|
+
#[:client_secret, :access_token].each do |param|
|
67
|
+
# params = params.merge(param => "##filtered##") if params[param]
|
68
|
+
#end
|
69
|
+
if opts[:method] == :post
|
70
|
+
debug("post: [#{path}] #{params.inspect}")
|
71
|
+
else
|
72
|
+
debug("get: #{path}?#{to_query(params)}")
|
73
|
+
end
|
74
|
+
stack.push(caller)
|
75
|
+
t0 = Time.now
|
76
|
+
if block_given?
|
77
|
+
ret = yield
|
78
|
+
end
|
79
|
+
t1 = Time.now
|
80
|
+
stack.pop
|
81
|
+
debug("call took #{t1 - t0} seconds")
|
82
|
+
ret
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_query(hash)
|
86
|
+
query = []
|
87
|
+
hash.each do |key, value|
|
88
|
+
query << "#{key}=#{value}"
|
89
|
+
end
|
90
|
+
query.join('&')
|
91
|
+
end
|
92
|
+
|
93
|
+
def trace(message)
|
94
|
+
debug(message)
|
95
|
+
stack.push(caller)
|
96
|
+
t0 = Time.now
|
97
|
+
if block_given?
|
98
|
+
ret = yield
|
99
|
+
end
|
100
|
+
t1 = Time.now
|
101
|
+
stack.pop
|
102
|
+
debug("execution took #{t1 - t0} seconds")
|
103
|
+
ret
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2015 Translation Exchange, Inc
|
4
|
+
#
|
5
|
+
# _______ _ _ _ ______ _
|
6
|
+
# |__ __| | | | | (_) | ____| | |
|
7
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
8
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
9
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
10
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
11
|
+
# __/ |
|
12
|
+
# |___/
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
# a copy of this software and associated documentation files (the
|
15
|
+
# "Software"), to deal in the Software without restriction, including
|
16
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
# the following conditions:
|
20
|
+
#
|
21
|
+
# The above copyright notice and this permission notice shall be
|
22
|
+
# included in all copies or substantial portions of the Software.
|
23
|
+
#
|
24
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
#++
|
32
|
+
|
33
|
+
module Tml
|
34
|
+
module RulesEngine
|
35
|
+
|
36
|
+
class Evaluator
|
37
|
+
attr_reader :env, :vars
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@vars = {}
|
41
|
+
@env = {
|
42
|
+
# McCarthy's Elementary S-functions and Predicates
|
43
|
+
'label' => lambda { |l, r| @vars[l] = r },
|
44
|
+
'quote' => lambda { |expr| expr },
|
45
|
+
'car' => lambda { |list| list[1] },
|
46
|
+
'cdr' => lambda { |list| list.drop(1) },
|
47
|
+
'cons' => lambda { |e, cell| [e] + cell },
|
48
|
+
'eq' => lambda { |l, r| l == r },
|
49
|
+
'atom' => lambda { |expr| [Symbol, String, Fixnum, Float].include?(expr.class) },
|
50
|
+
'cond' => lambda { |c, t, f| evaluate(c) ? evaluate(t) : evaluate(f) },
|
51
|
+
|
52
|
+
# Tml Extensions
|
53
|
+
'=' => lambda { |l, r| l == r }, # ['=', 1, 2]
|
54
|
+
'!=' => lambda { |l, r| l != r }, # ['!=', 1, 2]
|
55
|
+
'<' => lambda { |l, r| l < r }, # ['<', 1, 2]
|
56
|
+
'>' => lambda { |l, r| l > r }, # ['>', 1, 2]
|
57
|
+
'+' => lambda { |l, r| l + r }, # ['+', 1, 2]
|
58
|
+
'-' => lambda { |l, r| l - r }, # ['-', 1, 2]
|
59
|
+
'*' => lambda { |l, r| l * r }, # ['*', 1, 2]
|
60
|
+
'%' => lambda { |l, r| l % r }, # ['%', 14, 10]
|
61
|
+
'mod' => lambda { |l, r| l % r }, # ['mod', '@n', 10]
|
62
|
+
'/' => lambda { |l, r| (l * 1.0) / r }, # ['/', 1, 2]
|
63
|
+
'!' => lambda { |expr| not expr }, # ['!', ['true']]
|
64
|
+
'not' => lambda { |val| not val }, # ['not', ['true']]
|
65
|
+
'&&' => lambda { |*expr| expr.all?{|e| evaluate(e)} }, # ['&&', [], [], ...]
|
66
|
+
'and' => lambda { |*expr| expr.all?{|e| evaluate(e)} }, # ['and', [], [], ...]
|
67
|
+
'||' => lambda { |*expr| expr.any?{|e| evaluate(e)} }, # ['||', [], [], ...]
|
68
|
+
'or' => lambda { |*expr| expr.any?{|e| evaluate(e)} }, # ['or', [], [], ...]
|
69
|
+
'if' => lambda { |c, t, f| evaluate(c) ? evaluate(t) : evaluate(f) }, # ['if', 'cond', 'true', 'false']
|
70
|
+
'let' => lambda { |l, r| @vars[l] = r }, # ['let', 'n', 5]
|
71
|
+
'true' => lambda { true }, # ['true']
|
72
|
+
'false' => lambda { false }, # ['false']
|
73
|
+
|
74
|
+
'date' => lambda { |date| Date.strptime(date, '%Y-%m-%d') }, # ['date', '2010-01-01']
|
75
|
+
'today' => lambda { Time.now.to_date }, # ['today']
|
76
|
+
'time' => lambda { |expr| Time.strptime(expr, '%Y-%m-%d %H:%M:%S') }, # ['time', '2010-01-01 10:10:05']
|
77
|
+
'now' => lambda { Time.now }, # ['now']
|
78
|
+
|
79
|
+
'append' => lambda { |l, r| r.to_s + l.to_s }, # ['append', 'world', 'hello ']
|
80
|
+
'prepend' => lambda { |l, r| l.to_s + r.to_s }, # ['prepend', 'hello ', 'world']
|
81
|
+
'match' => lambda { |search, subject| # ['match', /a/, 'abc']
|
82
|
+
search = regexp_from_string(search)
|
83
|
+
not search.match(subject).nil?
|
84
|
+
},
|
85
|
+
'in' => lambda { |values, search| # ['in', '1,2,3,5..10,20..24', '@n']
|
86
|
+
search = search.to_s.strip
|
87
|
+
values.split(',').each do |e|
|
88
|
+
if e.index('..')
|
89
|
+
bounds = e.strip.split('..')
|
90
|
+
return true if (bounds.first.strip..bounds.last.strip).include?(search)
|
91
|
+
end
|
92
|
+
return true if e.strip == search
|
93
|
+
end
|
94
|
+
false
|
95
|
+
},
|
96
|
+
'within' => lambda { |values, search| # ['within', '0..3', '@n']
|
97
|
+
bounds = values.split('..').map{|d| Integer(d)}
|
98
|
+
(bounds.first..bounds.last).include?(search)
|
99
|
+
},
|
100
|
+
'replace' => lambda { |search, replace, subject| # ['replace', '/^a/', 'v', 'abc']
|
101
|
+
# handle regular expression
|
102
|
+
if /\/i$/.match(search)
|
103
|
+
replace = replace.gsub(/\$(\d+)/, '\\\\\1') # for compatibility with Perl notation
|
104
|
+
end
|
105
|
+
search = regexp_from_string(search)
|
106
|
+
subject.gsub(search, replace)
|
107
|
+
},
|
108
|
+
'count' => lambda { |list| # ['count', '@genders']
|
109
|
+
(list.is_a?(String) ? vars[list] : list).count
|
110
|
+
},
|
111
|
+
'all' => lambda { |list, value| # ['all', '@genders', 'male']
|
112
|
+
list = (list.is_a?(String) ? vars[list] : list)
|
113
|
+
list.is_a?(Array) ? list.all?{|e| e == value} : false
|
114
|
+
},
|
115
|
+
'any' => lambda { |list, value| # ['any', '@genders', 'female']
|
116
|
+
list = (list.is_a?(String) ? vars[list] : list)
|
117
|
+
list.is_a?(Array) ? list.any?{|e| e == value} : false
|
118
|
+
},
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def regexp_from_string(str)
|
123
|
+
return Regexp.new(/#{str}/) unless /^\//.match(str)
|
124
|
+
|
125
|
+
str = str.gsub(/^\//, '')
|
126
|
+
|
127
|
+
if /\/i$/.match(str)
|
128
|
+
str = str.gsub(/\/i$/, '')
|
129
|
+
return Regexp.new(/#{str}/i)
|
130
|
+
end
|
131
|
+
|
132
|
+
str = str.gsub(/\/$/, '')
|
133
|
+
Regexp.new(/#{str}/)
|
134
|
+
end
|
135
|
+
|
136
|
+
def reset!
|
137
|
+
@vars = {}
|
138
|
+
end
|
139
|
+
|
140
|
+
def apply(fn, args)
|
141
|
+
raise "undefined symbols #{fn}" unless @env.keys.include?(fn)
|
142
|
+
@env[fn].call(*args)
|
143
|
+
end
|
144
|
+
|
145
|
+
def evaluate(expr)
|
146
|
+
if @env['atom'].call(expr)
|
147
|
+
return @vars[expr] if expr.is_a?(String) and @vars[expr]
|
148
|
+
return expr
|
149
|
+
end
|
150
|
+
|
151
|
+
fn = expr[0]
|
152
|
+
args = expr.drop(1)
|
153
|
+
|
154
|
+
unless %w(quote car cdr cond if && || and or true false let count all any).member?(fn)
|
155
|
+
args = args.map { |a| self.evaluate(a) }
|
156
|
+
end
|
157
|
+
apply(fn, args)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2015 Translation Exchange, Inc
|
4
|
+
#
|
5
|
+
# _______ _ _ _ ______ _
|
6
|
+
# |__ __| | | | | (_) | ____| | |
|
7
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
8
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
9
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
10
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
11
|
+
# __/ |
|
12
|
+
# |___/
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
# a copy of this software and associated documentation files (the
|
15
|
+
# "Software"), to deal in the Software without restriction, including
|
16
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
# the following conditions:
|
20
|
+
#
|
21
|
+
# The above copyright notice and this permission notice shall be
|
22
|
+
# included in all copies or substantial portions of the Software.
|
23
|
+
#
|
24
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
#++
|
32
|
+
|
33
|
+
module Tml
|
34
|
+
module RulesEngine
|
35
|
+
|
36
|
+
class Parser
|
37
|
+
attr_reader :tokens, :expression
|
38
|
+
|
39
|
+
def initialize(expression)
|
40
|
+
@expression = expression
|
41
|
+
if expression =~ /^\(/
|
42
|
+
@tokens = expression.scan(/[()]|\w+|@\w+|[\+\-\!\|\=>&<\*\/%]+|".*?"|'.*?'/)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse
|
47
|
+
return @expression unless tokens
|
48
|
+
token = tokens.shift
|
49
|
+
return nil if token.nil?
|
50
|
+
return parse_list if (token) == '('
|
51
|
+
return token[1..-2].to_s if token =~ /^['"].*/
|
52
|
+
return token.to_i if token =~ /\d+/
|
53
|
+
token.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_list
|
57
|
+
list = []
|
58
|
+
list << parse until tokens.empty? or tokens.first == ')'
|
59
|
+
tokens.shift
|
60
|
+
list
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/tml/session.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2015 Translation Exchange, Inc
|
4
|
+
#
|
5
|
+
# _______ _ _ _ ______ _
|
6
|
+
# |__ __| | | | | (_) | ____| | |
|
7
|
+
# | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
|
8
|
+
# | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
|
9
|
+
# | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
|
10
|
+
# |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
|
11
|
+
# __/ |
|
12
|
+
# |___/
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
# a copy of this software and associated documentation files (the
|
15
|
+
# "Software"), to deal in the Software without restriction, including
|
16
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
# the following conditions:
|
20
|
+
#
|
21
|
+
# The above copyright notice and this permission notice shall be
|
22
|
+
# included in all copies or substantial portions of the Software.
|
23
|
+
#
|
24
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
+
#++
|
32
|
+
|
33
|
+
module Tml
|
34
|
+
|
35
|
+
def self.session
|
36
|
+
Thread.current[:session] ||= Tml::Session.new
|
37
|
+
end
|
38
|
+
|
39
|
+
class Session
|
40
|
+
# Session Attributes - Move to Session
|
41
|
+
attr_accessor :application, :current_user, :current_locale, :current_language, :current_translator,
|
42
|
+
:current_source, :current_component, :block_options, :cookie_params, :access_token, :tools_enabled
|
43
|
+
|
44
|
+
def self.access_token
|
45
|
+
@access_token
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.access_token=(token)
|
49
|
+
@access_token = token
|
50
|
+
end
|
51
|
+
|
52
|
+
def init(opts = {})
|
53
|
+
return unless Tml.config.enabled? and Tml.config.application
|
54
|
+
|
55
|
+
key = opts[:key] || Tml.config.application[:key]
|
56
|
+
secret = opts[:secret] || Tml.config.application[:secret]
|
57
|
+
host = opts[:host] || Tml.config.application[:host]
|
58
|
+
|
59
|
+
Tml::Session.access_token ||= begin
|
60
|
+
self.access_token = opts[:token] || Tml.config.application[:token]
|
61
|
+
self.access_token ||= opts[:access_token] || Tml.config.application[:access_token]
|
62
|
+
end
|
63
|
+
|
64
|
+
Tml.cache.reset_version
|
65
|
+
|
66
|
+
self.application = Tml.memory.fetch(Tml::Application.cache_key) do
|
67
|
+
Tml::Application.new(:host => host, :key => key, :secret => secret, :access_token => Tml::Session.access_token).fetch
|
68
|
+
end
|
69
|
+
|
70
|
+
if Tml.cache.read_only?
|
71
|
+
self.class.access_token = self.application.access_token
|
72
|
+
end
|
73
|
+
|
74
|
+
# Tml.logger.info(self.cookie_params.inspect)
|
75
|
+
|
76
|
+
self.cookie_params = begin
|
77
|
+
cookie_name = "trex_#{self.application.key}"
|
78
|
+
if opts[:cookies] and opts[:cookies][cookie_name]
|
79
|
+
begin
|
80
|
+
HashWithIndifferentAccess.new(Tml::Utils.decode_and_verify_params(opts[:cookies][cookie_name], secret))
|
81
|
+
rescue Exception => ex
|
82
|
+
Tml.logger.error("Failed to parse tml cookie: #{ex.message}")
|
83
|
+
{}
|
84
|
+
end
|
85
|
+
else
|
86
|
+
{}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
self.tools_enabled = opts[:tools_enabled]
|
91
|
+
self.current_user = opts[:user]
|
92
|
+
self.current_source = opts[:source] || '/tml/core'
|
93
|
+
self.current_component = opts[:component]
|
94
|
+
self.current_locale = opts[:locale] || self.cookie_params[:locale] || Tml.config.default_locale
|
95
|
+
|
96
|
+
if self.cookie_params['translator']
|
97
|
+
self.current_translator = Tml::Translator.new(self.cookie_params['translator'])
|
98
|
+
end
|
99
|
+
|
100
|
+
# if inline mode don't use any app cache
|
101
|
+
if inline_mode?
|
102
|
+
self.application = self.application.dup
|
103
|
+
self.application.reset_translation_cache
|
104
|
+
end
|
105
|
+
|
106
|
+
if self.current_translator
|
107
|
+
self.current_translator.application = self.application
|
108
|
+
end
|
109
|
+
|
110
|
+
self.current_language = self.application.language(self.current_locale)
|
111
|
+
end
|
112
|
+
|
113
|
+
def tools_enabled?
|
114
|
+
self.tools_enabled
|
115
|
+
end
|
116
|
+
|
117
|
+
def reset
|
118
|
+
self.application= nil
|
119
|
+
self.current_user= nil
|
120
|
+
self.current_language= nil
|
121
|
+
self.current_translator= nil
|
122
|
+
self.current_source= nil
|
123
|
+
self.current_component= nil
|
124
|
+
self.tools_enabled= nil
|
125
|
+
self.block_options= nil
|
126
|
+
end
|
127
|
+
|
128
|
+
def current_language
|
129
|
+
@current_language ||= Tml.config.default_language
|
130
|
+
end
|
131
|
+
|
132
|
+
def application
|
133
|
+
@application ||= Tml::Application.new(:host => Tml::Api::Client::API_HOST)
|
134
|
+
end
|
135
|
+
|
136
|
+
def source_language
|
137
|
+
arr = @block_options || []
|
138
|
+
arr.reverse.each do |opts|
|
139
|
+
return application.language(opts[:locale]) unless opts[:locale].blank?
|
140
|
+
end
|
141
|
+
application.language
|
142
|
+
end
|
143
|
+
|
144
|
+
def target_language
|
145
|
+
arr = @block_options || []
|
146
|
+
arr.reverse.each do |opts|
|
147
|
+
return application.language(opts[:target_locale]) unless opts[:target_locale].nil?
|
148
|
+
end
|
149
|
+
current_language
|
150
|
+
end
|
151
|
+
|
152
|
+
def inline_mode?
|
153
|
+
current_translator and current_translator.inline?
|
154
|
+
end
|
155
|
+
|
156
|
+
#########################################################
|
157
|
+
## Block Options
|
158
|
+
#########################################################
|
159
|
+
|
160
|
+
def push_block_options(opts)
|
161
|
+
(@block_options ||= []).push(opts)
|
162
|
+
end
|
163
|
+
|
164
|
+
def pop_block_options
|
165
|
+
return unless @block_options
|
166
|
+
@block_options.pop
|
167
|
+
end
|
168
|
+
|
169
|
+
def block_options
|
170
|
+
(@block_options ||= []).last || {}
|
171
|
+
end
|
172
|
+
|
173
|
+
def with_block_options(opts)
|
174
|
+
push_block_options(opts)
|
175
|
+
if block_given?
|
176
|
+
ret = yield
|
177
|
+
end
|
178
|
+
pop_block_options
|
179
|
+
ret
|
180
|
+
end
|
181
|
+
|
182
|
+
def current_source_from_block_options
|
183
|
+
arr = @block_options || []
|
184
|
+
arr.reverse.each do |opts|
|
185
|
+
return application.source_by_key(opts[:source]) unless opts[:source].blank?
|
186
|
+
end
|
187
|
+
nil
|
188
|
+
end
|
189
|
+
|
190
|
+
def current_component_from_block_options
|
191
|
+
arr = @block_options || []
|
192
|
+
arr.reverse.each do |opts|
|
193
|
+
return application.component_by_key(opts[:component]) unless opts[:component].blank?
|
194
|
+
end
|
195
|
+
Tml.config.current_component
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
end
|