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
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
|