tml 4.3.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 +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
@@ -0,0 +1,403 @@
|
|
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 Tokens
|
35
|
+
class Data
|
36
|
+
|
37
|
+
attr_reader :label, :full_name, :short_name, :case_keys, :context_keys
|
38
|
+
|
39
|
+
def self.expression
|
40
|
+
/(\{[^_:][\w]*(:[\w]+)*(::[\w]+)*\})/
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.parse(label, opts = {})
|
44
|
+
tokens = []
|
45
|
+
label.scan(expression).uniq.each do |token_array|
|
46
|
+
tokens << self.new(label, token_array.first)
|
47
|
+
end
|
48
|
+
tokens
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(label, token)
|
52
|
+
@label = label
|
53
|
+
@full_name = token
|
54
|
+
parse_elements
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_elements
|
58
|
+
name_without_parens = @full_name[1..-2]
|
59
|
+
name_without_case_keys = name_without_parens.split('::').first.strip
|
60
|
+
|
61
|
+
@short_name = name_without_parens.split(':').first.strip
|
62
|
+
@case_keys = name_without_parens.scan(/(::\w+)/).flatten.uniq.collect{|c| c.gsub('::', '')}
|
63
|
+
@context_keys = name_without_case_keys.scan(/(:\w+)/).flatten.uniq.collect{|c| c.gsub(':', '')}
|
64
|
+
end
|
65
|
+
|
66
|
+
def name(opts = {})
|
67
|
+
val = short_name
|
68
|
+
val = "#{val}:#{context_keys.join(':')}" if opts[:context_keys] and context_keys.any?
|
69
|
+
val = "#{val}::#{case_keys.join('::')}" if opts[:case_keys] and case_keys.any?
|
70
|
+
val = "{#{val}}" if opts[:parens]
|
71
|
+
val
|
72
|
+
end
|
73
|
+
|
74
|
+
def key
|
75
|
+
short_name.to_sym
|
76
|
+
end
|
77
|
+
|
78
|
+
# used by the translator submit dialog
|
79
|
+
def name_for_case_keys(keys)
|
80
|
+
keys = [keys] unless keys.is_a?(Array)
|
81
|
+
"#{name}::#{keys.join('::')}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def context_for_language(language)
|
85
|
+
if context_keys.any?
|
86
|
+
language.context_by_keyword(context_keys.first)
|
87
|
+
else
|
88
|
+
language.context_by_token_name(short_name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Utility method for errors
|
93
|
+
def error(msg, return_token = true)
|
94
|
+
Tml.logger.error(msg)
|
95
|
+
return_token ? full_name : label
|
96
|
+
end
|
97
|
+
|
98
|
+
##############################################################################
|
99
|
+
#
|
100
|
+
# returns token object from tokens param
|
101
|
+
#
|
102
|
+
##############################################################################
|
103
|
+
|
104
|
+
def self.token_object(token_values, token_name)
|
105
|
+
return nil if token_values.nil?
|
106
|
+
token_object = Tml::Utils.hash_value(token_values, token_name)
|
107
|
+
return token_object.first if token_object.is_a?(Array)
|
108
|
+
if token_object.is_a?(Hash)
|
109
|
+
object = Tml::Utils.hash_value(token_object, :object)
|
110
|
+
return object if object
|
111
|
+
end
|
112
|
+
token_object
|
113
|
+
end
|
114
|
+
|
115
|
+
##############################################################################
|
116
|
+
#
|
117
|
+
# tr("Hello {user_list}!", "", {:user_list => [[user1, user2, user3], :name]}}
|
118
|
+
#
|
119
|
+
# first element is an array, the rest of the elements are similar to the
|
120
|
+
# regular tokens lambda, symbol, string, with parameters that follow
|
121
|
+
#
|
122
|
+
# if you want to pass options, then make the second parameter an array as well
|
123
|
+
#
|
124
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], :name]})
|
125
|
+
#
|
126
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], lambda{|user| user.name}]})
|
127
|
+
#
|
128
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], {:attribute => :name})
|
129
|
+
#
|
130
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], {:attribute => :name, :value => "<strong>{$0}</strong>"})
|
131
|
+
#
|
132
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], "<strong>{$0}</strong>")
|
133
|
+
#
|
134
|
+
# tr("{users} joined the site", {:users => [[user1, user2, user3], :name, {
|
135
|
+
# :limit => 4,
|
136
|
+
# :separator => ', ',
|
137
|
+
# :joiner => 'and',
|
138
|
+
# :remainder => lambda{|elements| tr("#{count||other}", :count => elements.size)},
|
139
|
+
# :expandable => true,
|
140
|
+
# :collapsable => true
|
141
|
+
# })
|
142
|
+
#
|
143
|
+
#
|
144
|
+
##############################################################################
|
145
|
+
def token_values_from_array(params, language, options)
|
146
|
+
list_options = {
|
147
|
+
:description => "List joiner",
|
148
|
+
:limit => 4,
|
149
|
+
:separator => ", ",
|
150
|
+
:joiner => 'and',
|
151
|
+
:less => '{laquo} less',
|
152
|
+
:expandable => true,
|
153
|
+
:collapsable => true
|
154
|
+
}
|
155
|
+
|
156
|
+
objects = params[0]
|
157
|
+
method = params[1]
|
158
|
+
list_options.merge!(params[2]) if params.size > 2
|
159
|
+
list_options[:expandable] = false if options[:skip_decorations]
|
160
|
+
|
161
|
+
values = objects.collect do |obj|
|
162
|
+
if method.is_a?(String)
|
163
|
+
method.gsub("{$0}", sanitize(obj.to_s, obj, language, options.merge(:safe => false)))
|
164
|
+
elsif method.is_a?(Symbol)
|
165
|
+
if obj.is_a?(Hash)
|
166
|
+
value = Tml::Utils.hash_value(obj, method)
|
167
|
+
else
|
168
|
+
value = obj.send(method)
|
169
|
+
end
|
170
|
+
sanitize(value, obj, language, options.merge(:safe => false))
|
171
|
+
elsif method.is_a?(Hash)
|
172
|
+
attr = Tml::Utils.hash_value(method, :attribute) || Tml::Utils.hash_value(method, :property)
|
173
|
+
if obj.is_a?(Hash)
|
174
|
+
value = Tml::Utils.hash_value(obj, attr)
|
175
|
+
else
|
176
|
+
value = obj.send(method)
|
177
|
+
end
|
178
|
+
|
179
|
+
hash_value = Tml::Utils.hash_value(method, :value)
|
180
|
+
if hash_value
|
181
|
+
hash_value.gsub("{$0}", sanitize(value, obj, language, options.merge(:safe => false)))
|
182
|
+
else
|
183
|
+
sanitize(value, obj, language, options.merge(:safe => false))
|
184
|
+
end
|
185
|
+
elsif method.is_a?(Proc)
|
186
|
+
sanitize(method.call(obj), obj, language, options.merge(:safe => true))
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
return values.first if objects.size == 1
|
191
|
+
return values.join(list_options[:separator]) if list_options[:joiner].nil? || list_options[:joiner] == ""
|
192
|
+
|
193
|
+
joiner = language.translate(list_options[:joiner], list_options[:description], {}, options)
|
194
|
+
if values.size <= list_options[:limit]
|
195
|
+
return "#{values[0..-2].join(list_options[:separator])} #{joiner} #{values.last}"
|
196
|
+
end
|
197
|
+
|
198
|
+
display_ary = values[0..(list_options[:limit]-1)]
|
199
|
+
remaining_ary = values[list_options[:limit]..-1]
|
200
|
+
result = "#{display_ary.join(list_options[:separator])}"
|
201
|
+
|
202
|
+
unless list_options[:expandable]
|
203
|
+
result << " " << joiner << " "
|
204
|
+
if list_options[:remainder] and list_options[:remainder].is_a?(Proc)
|
205
|
+
result << list_options[:remainder].call(remaining_ary)
|
206
|
+
else
|
207
|
+
result << language.translate("{count||other}", list_options[:description], {:count => remaining_ary.size}, options)
|
208
|
+
end
|
209
|
+
return result
|
210
|
+
end
|
211
|
+
|
212
|
+
uniq_id = Tml::TranslationKey.generate_key(label, values.join(","))
|
213
|
+
result << "<span id=\"tml_other_link_#{uniq_id}\"> #{joiner} "
|
214
|
+
|
215
|
+
result << "<a href='#' onClick=\"Tml.Utils.Effects.hide('tml_other_link_#{uniq_id}'); Tml.Utils.Effects.show('tml_other_elements_#{uniq_id}'); return false;\">"
|
216
|
+
if list_options[:remainder] and list_options[:remainder].is_a?(Proc)
|
217
|
+
result << list_options[:remainder].call(remaining_ary)
|
218
|
+
else
|
219
|
+
result << language.translate("{count||other}", list_options[:description], {:count => remaining_ary.size}, options)
|
220
|
+
end
|
221
|
+
result << "</a></span>"
|
222
|
+
|
223
|
+
result << "<span id=\"tml_other_elements_#{uniq_id}\" style='display:none'>"
|
224
|
+
result << list_options[:separator] << " "
|
225
|
+
result << remaining_ary[0..-2].join(list_options[:separator])
|
226
|
+
result << " #{joiner} "
|
227
|
+
result << remaining_ary.last
|
228
|
+
|
229
|
+
if list_options[:collapsable]
|
230
|
+
result << "<a href='#' style='font-size:smaller;white-space:nowrap' onClick=\"Tml.Utils.Effects.show('tml_other_link_#{uniq_id}'); Tml.Utils.Effects.hide('tml_other_elements_#{uniq_id}'); return false;\"> "
|
231
|
+
result << language.translate(list_options[:less], list_options[:description], {}, options)
|
232
|
+
result << "</a>"
|
233
|
+
end
|
234
|
+
|
235
|
+
result << "</span>"
|
236
|
+
end
|
237
|
+
|
238
|
+
##############################################################################
|
239
|
+
#
|
240
|
+
# gets the value based on various evaluation methods
|
241
|
+
#
|
242
|
+
# examples:
|
243
|
+
#
|
244
|
+
# tr("Hello {user}", {:user => [current_user, current_user.name]}}
|
245
|
+
# tr("Hello {user}", {:user => [current_user, :name]}}
|
246
|
+
#
|
247
|
+
# tr("Hello {user}", {:user => [{:name => "Michael", :gender => :male}, current_user.name]}}
|
248
|
+
# tr("Hello {user}", {:user => [{:name => "Michael", :gender => :male}, :name]}}
|
249
|
+
#
|
250
|
+
##############################################################################
|
251
|
+
|
252
|
+
def token_value_from_array_param(array, language, options)
|
253
|
+
# if you provided an array, it better have some values
|
254
|
+
if array.size < 2
|
255
|
+
return error("Invalid value for array token #{full_name} in #{label}")
|
256
|
+
end
|
257
|
+
|
258
|
+
# if the first value of an array is an array handle it here
|
259
|
+
if array[0].is_a?(Array)
|
260
|
+
return token_values_from_array(array, language, options)
|
261
|
+
end
|
262
|
+
|
263
|
+
if array[1].is_a?(String)
|
264
|
+
return sanitize(array[1], array[0], language, options.merge(:safe => true))
|
265
|
+
end
|
266
|
+
|
267
|
+
if array[0].is_a?(Hash)
|
268
|
+
if array[1].is_a?(Symbol)
|
269
|
+
return sanitize(Tml::Utils.hash_value(array[0], array[1]), array[0], language, options.merge(:safe => false))
|
270
|
+
end
|
271
|
+
|
272
|
+
return error("Invalid value for array token #{full_name} in #{label}")
|
273
|
+
end
|
274
|
+
|
275
|
+
# if second param is symbol, invoke the method on the object with the remaining values
|
276
|
+
if array[1].is_a?(Symbol)
|
277
|
+
return sanitize(array[0].send(array[1]), array[0], language, options.merge(:safe => false))
|
278
|
+
end
|
279
|
+
|
280
|
+
error("Invalid value for array token #{full_name} in #{label}")
|
281
|
+
end
|
282
|
+
|
283
|
+
##############################################################################
|
284
|
+
#
|
285
|
+
# examples:
|
286
|
+
#
|
287
|
+
# tr("Hello {user}", {:user => {:value => "Michael", :gender => :male}}}
|
288
|
+
#
|
289
|
+
# tr("Hello {user}", {:user => {:object => {:gender => :male}, :value => "Michael"}}}
|
290
|
+
# tr("Hello {user}", {:user => {:object => {:name => "Michael", :gender => :male}, :property => :name}}}
|
291
|
+
# tr("Hello {user}", {:user => {:object => {:name => "Michael", :gender => :male}, :attribute => :name}}}
|
292
|
+
#
|
293
|
+
# tr("Hello {user}", {:user => {:object => user, :value => "Michael"}}}
|
294
|
+
# tr("Hello {user}", {:user => {:object => user, :property => :name}}}
|
295
|
+
# tr("Hello {user}", {:user => {:object => user, :attribute => :name}}}
|
296
|
+
#
|
297
|
+
##############################################################################
|
298
|
+
|
299
|
+
def token_value_from_hash_param(hash, language, options)
|
300
|
+
value = Tml::Utils.hash_value(hash, :value)
|
301
|
+
object = Tml::Utils.hash_value(hash, :object)
|
302
|
+
|
303
|
+
unless value.nil?
|
304
|
+
return sanitize(value, object || hash, language, options.merge(:safe => true))
|
305
|
+
end
|
306
|
+
|
307
|
+
if object.nil?
|
308
|
+
return error("Missing value for hash token #{full_name} in #{label}")
|
309
|
+
end
|
310
|
+
|
311
|
+
attr = Tml::Utils.hash_value(hash, :attribute) || Tml::Utils.hash_value(hash, :property)
|
312
|
+
|
313
|
+
if object.is_a?(Hash)
|
314
|
+
unless attr.nil?
|
315
|
+
return sanitize(Tml::Utils.hash_value(object, attr), object, language, options.merge(:safe => false))
|
316
|
+
end
|
317
|
+
|
318
|
+
return error("Missing value for hash token #{full_name} in #{label}")
|
319
|
+
end
|
320
|
+
|
321
|
+
sanitize(object.send(attr), object, language, options.merge(:safe => false))
|
322
|
+
end
|
323
|
+
|
324
|
+
# evaluate all possible methods for the token value and return sanitized result
|
325
|
+
def token_value(object, language, options = {})
|
326
|
+
return token_value_from_array_param(object, language, options) if object.is_a?(Array)
|
327
|
+
return token_value_from_hash_param(object, language, options) if object.is_a?(Hash)
|
328
|
+
sanitize(object, object, language, options)
|
329
|
+
end
|
330
|
+
|
331
|
+
def sanitize(value, object, language, options)
|
332
|
+
value = value.to_s
|
333
|
+
|
334
|
+
unless Tml.session.block_options[:skip_html_escaping]
|
335
|
+
if options[:safe] == false
|
336
|
+
value = ERB::Util.html_escape(value)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
return value unless language_cases_enabled?
|
341
|
+
apply_language_cases(value, object, language, options)
|
342
|
+
end
|
343
|
+
|
344
|
+
def language_cases_enabled?
|
345
|
+
Tml.session.application and Tml.session.application.feature_enabled?(:language_cases)
|
346
|
+
end
|
347
|
+
|
348
|
+
##############################################################################
|
349
|
+
#
|
350
|
+
# chooses the appropriate case for the token value. case is identified with ::
|
351
|
+
#
|
352
|
+
# examples:
|
353
|
+
#
|
354
|
+
# tr("Hello {user::nom}", "", :user => current_user)
|
355
|
+
# tr("{actor} gave {target::dat} a present", "", :actor => user1, :target => user2)
|
356
|
+
# tr("This is {user::pos} toy", "", :user => current_user)
|
357
|
+
#
|
358
|
+
##############################################################################
|
359
|
+
def apply_case(key, value, object, language, options)
|
360
|
+
lcase = language.case_by_keyword(key)
|
361
|
+
return value unless lcase
|
362
|
+
lcase.apply(value, object, options)
|
363
|
+
end
|
364
|
+
|
365
|
+
def apply_language_cases(value, object, language, options)
|
366
|
+
case_keys.each do |key|
|
367
|
+
value = apply_case(key, value, object, language, options)
|
368
|
+
end
|
369
|
+
|
370
|
+
value
|
371
|
+
end
|
372
|
+
|
373
|
+
def substitute(label, context, language, options = {})
|
374
|
+
# get the object from the values
|
375
|
+
object = Tml::Utils.hash_value(context, key)
|
376
|
+
|
377
|
+
# see if the token is a default html token
|
378
|
+
object = Tml.config.default_token_value(key) if object.nil?
|
379
|
+
|
380
|
+
if object.nil? and not context.key?(key)
|
381
|
+
return error("Missing value for #{full_name} in #{label}", false)
|
382
|
+
end
|
383
|
+
|
384
|
+
if object.nil? and not Tml::Config.allow_nil_token_values?
|
385
|
+
return error("Token value is nil for #{full_name} in #{label}", false)
|
386
|
+
end
|
387
|
+
|
388
|
+
return label.gsub(full_name, "") if object.nil?
|
389
|
+
|
390
|
+
value = token_value(object, language, options)
|
391
|
+
label.gsub(full_name, value)
|
392
|
+
end
|
393
|
+
|
394
|
+
def sanitized_name
|
395
|
+
name(:parens => true)
|
396
|
+
end
|
397
|
+
|
398
|
+
def to_s
|
399
|
+
full_name
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
@@ -0,0 +1,61 @@
|
|
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
|
+
#######################################################################
|
34
|
+
#
|
35
|
+
# Method Token Forms
|
36
|
+
#
|
37
|
+
# {user.name}
|
38
|
+
# {user.name:gender}
|
39
|
+
#
|
40
|
+
#######################################################################
|
41
|
+
|
42
|
+
class Tml::Tokens::Method < Tml::Tokens::Data
|
43
|
+
def self.expression
|
44
|
+
/(\{[^_:.][\w]*(\.[\w]+)(:[\w]+)*(::[\w]+)*\})/
|
45
|
+
end
|
46
|
+
|
47
|
+
def object_name
|
48
|
+
@object_name ||= short_name.split(".").first
|
49
|
+
end
|
50
|
+
|
51
|
+
def object_method_name
|
52
|
+
@object_method_name ||= short_name.split(".").last
|
53
|
+
end
|
54
|
+
|
55
|
+
def substitute(label, context, language, options = {})
|
56
|
+
object = Tml::Utils.hash_value(context, object_name)
|
57
|
+
raise Tml::Exception.new("Missing value for a token: #{full_name}") unless object
|
58
|
+
object_value = sanitize(object.send(object_method_name), object, language, options.merge(:safe => false))
|
59
|
+
label.gsub(full_name, object_value)
|
60
|
+
end
|
61
|
+
end
|