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/language.rb
ADDED
@@ -0,0 +1,175 @@
|
|
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
|
+
class Tml::Language < Tml::Base
|
34
|
+
belongs_to :application
|
35
|
+
attributes :locale, :name, :english_name, :native_name, :right_to_left, :flag_url
|
36
|
+
has_many :contexts, :cases
|
37
|
+
|
38
|
+
def self.cache_key(locale)
|
39
|
+
File.join(locale, 'language')
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch
|
43
|
+
update_attributes(application.api_client.get("language/#{locale}", {}, {:cache_key => self.class.cache_key(locale)}))
|
44
|
+
rescue Tml::Exception => ex
|
45
|
+
Tml.logger.error("Failed to load language: #{ex}")
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_attributes(attrs)
|
50
|
+
super
|
51
|
+
|
52
|
+
self.attributes[:contexts] = {}
|
53
|
+
if hash_value(attrs, :contexts)
|
54
|
+
hash_value(attrs, :contexts).each do |key, context|
|
55
|
+
self.attributes[:contexts][key] = Tml::LanguageContext.new(context.merge(:keyword => key, :language => self))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
self.attributes[:cases] = {}
|
60
|
+
if hash_value(attrs, :cases)
|
61
|
+
hash_value(attrs, :cases).each do |key, lcase|
|
62
|
+
self.attributes[:cases][key] = Tml::LanguageCase.new(lcase.merge(:keyword => key, :language => self))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def context_by_keyword(keyword)
|
68
|
+
hash_value(contexts, keyword)
|
69
|
+
end
|
70
|
+
|
71
|
+
def context_by_token_name(token_name)
|
72
|
+
contexts.values.detect{|ctx| ctx.applies_to_token?(token_name)}
|
73
|
+
end
|
74
|
+
|
75
|
+
def case_by_keyword(keyword)
|
76
|
+
cases[keyword]
|
77
|
+
end
|
78
|
+
|
79
|
+
def has_definition?
|
80
|
+
contexts.any?
|
81
|
+
end
|
82
|
+
|
83
|
+
def default?
|
84
|
+
return true unless application
|
85
|
+
application.default_locale == locale
|
86
|
+
end
|
87
|
+
|
88
|
+
def dir
|
89
|
+
right_to_left? ? 'rtl' : 'ltr'
|
90
|
+
end
|
91
|
+
|
92
|
+
def align(dest)
|
93
|
+
return dest unless right_to_left?
|
94
|
+
dest.to_s == 'left' ? 'right' : 'left'
|
95
|
+
end
|
96
|
+
|
97
|
+
def full_name
|
98
|
+
return english_name if english_name == native_name
|
99
|
+
"#{english_name} - #{native_name}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def current_source(options)
|
103
|
+
(options[:source] || Tml.session.block_options[:source] || Tml.session.current_source || 'undefined').to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
#######################################################################################################
|
107
|
+
# Translation Methods
|
108
|
+
#
|
109
|
+
# Note - when inline translation mode is enable, cache will not be used and translators will
|
110
|
+
# always hit the live service to get the most recent translations
|
111
|
+
#
|
112
|
+
# Some cache adapters cache by source, others by key. Some are read-only, some are built on the fly.
|
113
|
+
#
|
114
|
+
# There are three ways to call the tr method
|
115
|
+
#
|
116
|
+
# tr(label, description = "", tokens = {}, options = {})
|
117
|
+
# or
|
118
|
+
# tr(label, tokens = {}, options = {})
|
119
|
+
# or
|
120
|
+
# tr(:label => label, :description => "", :tokens => {}, :options => {})
|
121
|
+
########################################################################################################
|
122
|
+
|
123
|
+
def translate(label, description = nil, tokens = {}, options = {})
|
124
|
+
params = Tml::Utils.normalize_tr_params(label, description, tokens, options)
|
125
|
+
return params[:label] if params[:label].tml_translated?
|
126
|
+
|
127
|
+
translation_key = Tml::TranslationKey.new({
|
128
|
+
:application => application,
|
129
|
+
:label => params[:label],
|
130
|
+
:description => params[:description],
|
131
|
+
:locale => hash_value(params[:options], :locale) || hash_value(Tml.session.block_options, :locale) || Tml.config.default_locale,
|
132
|
+
:level => hash_value(params[:options], :level) || hash_value(Tml.session.block_options, :level) || Tml.config.default_level,
|
133
|
+
:translations => []
|
134
|
+
})
|
135
|
+
|
136
|
+
#Tml.logger.info("Translating " + params[:label] + " from: " + translation_key.locale.inspect + " to " + locale.inspect)
|
137
|
+
|
138
|
+
params[:tokens] ||= {}
|
139
|
+
params[:tokens][:viewing_user] ||= Tml.session.current_user
|
140
|
+
|
141
|
+
if Tml.config.disabled? or self.locale == translation_key.locale
|
142
|
+
return translation_key.substitute_tokens(
|
143
|
+
params[:label],
|
144
|
+
params[:tokens],
|
145
|
+
self,
|
146
|
+
params[:options]
|
147
|
+
).tml_translated
|
148
|
+
end
|
149
|
+
|
150
|
+
cached_translations = application.cached_translations(locale, translation_key.key)
|
151
|
+
if cached_translations
|
152
|
+
translation_key.set_translations(locale, cached_translations)
|
153
|
+
return translation_key.translate(self, params[:tokens], params[:options]).tml_translated
|
154
|
+
end
|
155
|
+
|
156
|
+
source_key = current_source(options)
|
157
|
+
|
158
|
+
if Tml.cache.segmented? or Tml.session.inline_mode?
|
159
|
+
source = application.source(source_key, locale)
|
160
|
+
cached_translations = source.cached_translations(locale, translation_key.key)
|
161
|
+
else
|
162
|
+
cached_translations = application.fetch_translations(locale)[translation_key.key]
|
163
|
+
end
|
164
|
+
|
165
|
+
if cached_translations
|
166
|
+
translation_key.set_translations(locale, cached_translations)
|
167
|
+
else
|
168
|
+
application.register_missing_key(source_key, translation_key)
|
169
|
+
end
|
170
|
+
|
171
|
+
translation_key.translate(self, params[:tokens], params[:options]).tml_translated
|
172
|
+
end
|
173
|
+
alias :tr :translate
|
174
|
+
|
175
|
+
end
|
@@ -0,0 +1,105 @@
|
|
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
|
+
class Tml::LanguageCase < Tml::Base
|
34
|
+
belongs_to :language
|
35
|
+
attributes :id, :keyword, :latin_name, :native_name, :description, :application
|
36
|
+
has_many :rules
|
37
|
+
|
38
|
+
TR8N_HTML_TAGS_REGEX = /<\/?[^>]*>/
|
39
|
+
|
40
|
+
def initialize(attrs = {})
|
41
|
+
super
|
42
|
+
self.attributes[:rules] = []
|
43
|
+
if hash_value(attrs, :rules)
|
44
|
+
self.attributes[:rules] = hash_value(attrs, :rules).collect{ |rule| Tml::LanguageCaseRule.new(rule.merge(:language_case => self)) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_matching_rule(value, object = nil)
|
49
|
+
rules.each do |rule|
|
50
|
+
return rule if rule.evaluate(value, object)
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
#######################################################################################################
|
56
|
+
## Evaluation Methods
|
57
|
+
#######################################################################################################
|
58
|
+
|
59
|
+
def apply(value, object = nil, options = {})
|
60
|
+
value = value.to_s
|
61
|
+
|
62
|
+
decorator = Tml::Decorators::Base.decorator
|
63
|
+
|
64
|
+
options = options.merge(:skip_decorations => true) if value.index('not_translated')
|
65
|
+
|
66
|
+
html_tokens = value.scan(TR8N_HTML_TAGS_REGEX).uniq
|
67
|
+
sanitized_value = value.gsub(TR8N_HTML_TAGS_REGEX, '')
|
68
|
+
|
69
|
+
if application.to_s == 'phrase'
|
70
|
+
words = [sanitized_value]
|
71
|
+
else
|
72
|
+
words = sanitized_value.split(/[\s\/\\]/).uniq
|
73
|
+
end
|
74
|
+
|
75
|
+
# replace html tokens with temporary placeholders {$h1}
|
76
|
+
html_tokens.each_with_index do |html_token, index|
|
77
|
+
value = value.gsub(html_token, "{$h#{index}}")
|
78
|
+
end
|
79
|
+
|
80
|
+
# replace words with temporary placeholders {$w1}
|
81
|
+
words.each_with_index do |word, index|
|
82
|
+
value = value.gsub(word, "{$w#{index}}")
|
83
|
+
end
|
84
|
+
|
85
|
+
transformed_words = []
|
86
|
+
words.each do |word|
|
87
|
+
case_rule = find_matching_rule(word, object)
|
88
|
+
case_value = case_rule ? case_rule.apply(word) : word
|
89
|
+
transformed_words << decorator.decorate_language_case(self, case_rule, word, case_value, options)
|
90
|
+
end
|
91
|
+
|
92
|
+
# replace back the temporary placeholders with the html tokens
|
93
|
+
transformed_words.each_with_index do |word, index|
|
94
|
+
value = value.gsub("{$w#{index}}", word)
|
95
|
+
end
|
96
|
+
|
97
|
+
# replace back the temporary placeholders with the html tokens
|
98
|
+
html_tokens.each_with_index do |html_token, index|
|
99
|
+
value = value.gsub("{$h#{index}}", html_token)
|
100
|
+
end
|
101
|
+
|
102
|
+
value
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,76 @@
|
|
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
|
+
class Tml::LanguageCaseRule < Tml::Base
|
34
|
+
belongs_to :language_case
|
35
|
+
attributes :id, :description, :examples, :conditions, :conditions_expression, :operations, :operations_expression
|
36
|
+
|
37
|
+
def conditions_expression
|
38
|
+
self.attributes[:conditions_expression] ||= Tml::RulesEngine::Parser.new(self.conditions).parse
|
39
|
+
end
|
40
|
+
|
41
|
+
def operations_expression
|
42
|
+
self.attributes[:operations_expression] ||= Tml::RulesEngine::Parser.new(self.operations).parse
|
43
|
+
end
|
44
|
+
|
45
|
+
def gender_variables(object)
|
46
|
+
return {} unless self.conditions.index('@gender')
|
47
|
+
return {'@gender' => 'unknown'} unless object
|
48
|
+
context = language_case.language.context_by_keyword(:gender)
|
49
|
+
return {'@gender' => 'unknown'} unless context
|
50
|
+
context.vars(object)
|
51
|
+
end
|
52
|
+
|
53
|
+
def evaluate(value, object = nil)
|
54
|
+
return false if conditions.nil?
|
55
|
+
|
56
|
+
re = Tml::RulesEngine::Evaluator.new
|
57
|
+
re.evaluate(['let', '@value', value])
|
58
|
+
|
59
|
+
gender_variables(object).each do |key, value|
|
60
|
+
re.evaluate(['let', key, value])
|
61
|
+
end
|
62
|
+
|
63
|
+
re.evaluate(conditions_expression)
|
64
|
+
end
|
65
|
+
|
66
|
+
def apply(value)
|
67
|
+
value = value.to_s
|
68
|
+
return value if operations.nil?
|
69
|
+
|
70
|
+
re = Tml::RulesEngine::Evaluator.new
|
71
|
+
re.evaluate(['let', '@value', value])
|
72
|
+
|
73
|
+
re.evaluate(operations_expression)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,117 @@
|
|
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
|
+
class Tml::LanguageContext < Tml::Base
|
34
|
+
belongs_to :language
|
35
|
+
attributes :keyword, :description, :default_key, :token_expression, :variables, :token_mapping
|
36
|
+
has_many :keys, :rules
|
37
|
+
|
38
|
+
def initialize(attrs = {})
|
39
|
+
super
|
40
|
+
|
41
|
+
self.attributes[:rules] = {}
|
42
|
+
if hash_value(attrs, :rules)
|
43
|
+
hash_value(attrs, :rules).each do |key, rule|
|
44
|
+
self.attributes[:rules][key] = Tml::LanguageContextRule.new(rule.merge(
|
45
|
+
:keyword => key,
|
46
|
+
:language_context => self
|
47
|
+
))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def config
|
53
|
+
context_rules = Tml.config.context_rules
|
54
|
+
hash_value(context_rules, self.keyword.to_sym) || {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def token_expression
|
58
|
+
@token_expression ||= begin
|
59
|
+
exp = self.attributes[:token_expression]
|
60
|
+
exp = Regexp.new(exp[1..-2])
|
61
|
+
exp
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def applies_to_token?(token)
|
66
|
+
token_expression.match(token) != nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def fallback_rule
|
70
|
+
@fallback_rule ||= rules.values.detect{|rule| rule.fallback?}
|
71
|
+
end
|
72
|
+
|
73
|
+
# prepare variables for evaluation
|
74
|
+
def vars(obj)
|
75
|
+
vars = {}
|
76
|
+
|
77
|
+
variables.each do |key|
|
78
|
+
method = hash_value(config, "variables.#{key}")
|
79
|
+
unless method
|
80
|
+
vars[key] = obj
|
81
|
+
next
|
82
|
+
end
|
83
|
+
|
84
|
+
if method.is_a?(String)
|
85
|
+
if obj.is_a?(Hash)
|
86
|
+
object = hash_value(obj, 'object') || obj
|
87
|
+
if object.is_a?(Hash)
|
88
|
+
vars[key] = hash_value(object, method, :whole => true)
|
89
|
+
else
|
90
|
+
vars[key] = object.send(method)
|
91
|
+
end
|
92
|
+
elsif obj.is_a?(String)
|
93
|
+
vars[key] = obj
|
94
|
+
else
|
95
|
+
vars[key] = obj.send(method)
|
96
|
+
end
|
97
|
+
elsif method.is_a?(Proc)
|
98
|
+
vars[key] = method.call(obj)
|
99
|
+
else
|
100
|
+
vars[key] = obj
|
101
|
+
end
|
102
|
+
|
103
|
+
vars[key] = vars[key].to_s if vars[key].is_a?(Symbol)
|
104
|
+
end
|
105
|
+
vars
|
106
|
+
end
|
107
|
+
|
108
|
+
def find_matching_rule(obj)
|
109
|
+
token_vars = vars(obj)
|
110
|
+
rules.values.each do |rule|
|
111
|
+
next if rule.fallback?
|
112
|
+
return rule if rule.evaluate(token_vars)
|
113
|
+
end
|
114
|
+
fallback_rule
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|