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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +243 -0
  4. data/Rakefile +9 -0
  5. data/lib/tml.rb +56 -0
  6. data/lib/tml/api/client.rb +206 -0
  7. data/lib/tml/api/post_office.rb +71 -0
  8. data/lib/tml/application.rb +254 -0
  9. data/lib/tml/base.rb +116 -0
  10. data/lib/tml/cache.rb +143 -0
  11. data/lib/tml/cache_adapters/file.rb +89 -0
  12. data/lib/tml/cache_adapters/memcache.rb +104 -0
  13. data/lib/tml/cache_adapters/memory.rb +85 -0
  14. data/lib/tml/cache_adapters/redis.rb +108 -0
  15. data/lib/tml/config.rb +410 -0
  16. data/lib/tml/decorators/base.rb +52 -0
  17. data/lib/tml/decorators/default.rb +43 -0
  18. data/lib/tml/decorators/html.rb +102 -0
  19. data/lib/tml/exception.rb +35 -0
  20. data/lib/tml/ext/array.rb +86 -0
  21. data/lib/tml/ext/date.rb +99 -0
  22. data/lib/tml/ext/fixnum.rb +47 -0
  23. data/lib/tml/ext/hash.rb +99 -0
  24. data/lib/tml/ext/string.rb +56 -0
  25. data/lib/tml/ext/time.rb +89 -0
  26. data/lib/tml/generators/cache/base.rb +117 -0
  27. data/lib/tml/generators/cache/file.rb +159 -0
  28. data/lib/tml/language.rb +175 -0
  29. data/lib/tml/language_case.rb +105 -0
  30. data/lib/tml/language_case_rule.rb +76 -0
  31. data/lib/tml/language_context.rb +117 -0
  32. data/lib/tml/language_context_rule.rb +56 -0
  33. data/lib/tml/languages/en.json +1363 -0
  34. data/lib/tml/logger.rb +109 -0
  35. data/lib/tml/rules_engine/evaluator.rb +162 -0
  36. data/lib/tml/rules_engine/parser.rb +65 -0
  37. data/lib/tml/session.rb +199 -0
  38. data/lib/tml/source.rb +106 -0
  39. data/lib/tml/tokenizers/data.rb +96 -0
  40. data/lib/tml/tokenizers/decoration.rb +204 -0
  41. data/lib/tml/tokenizers/dom.rb +346 -0
  42. data/lib/tml/tokens/data.rb +403 -0
  43. data/lib/tml/tokens/method.rb +61 -0
  44. data/lib/tml/tokens/transform.rb +223 -0
  45. data/lib/tml/translation.rb +67 -0
  46. data/lib/tml/translation_key.rb +178 -0
  47. data/lib/tml/translator.rb +47 -0
  48. data/lib/tml/utils.rb +130 -0
  49. data/lib/tml/version.rb +34 -0
  50. metadata +121 -0
@@ -0,0 +1,71 @@
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::Api::PostOffice < Tml::Api::Client
34
+ POST_OFFICE_HOST = 'https://postoffice.translationexchange.com'
35
+ POST_OFFICE_API_VERSION = 'v1'
36
+
37
+ attributes :application
38
+
39
+ def templates
40
+ api('templates', {
41
+ :access_token => access_token
42
+ })
43
+ end
44
+
45
+ def channels
46
+ api('channels', {
47
+ :access_token => access_token
48
+ })
49
+ end
50
+
51
+ def deliver(template, to, tokens = {}, options = {})
52
+ api("templates/#{template}/deliver", {
53
+ :access_token => access_token,
54
+ :tokens => tokens,
55
+ :to => to,
56
+ :via => options[:via],
57
+ :from => options[:from],
58
+ :locale => options[:locale],
59
+ :realtime => options[:realtime]
60
+ }, options.merge(:method => :post))
61
+ end
62
+
63
+ def host
64
+ application.tools['postoffice'] || POST_OFFICE_HOST
65
+ end
66
+
67
+ def prepare_api_path(path)
68
+ "/api/#{POST_OFFICE_API_VERSION}#{path.first == '/' ? '' : '/'}#{path}"
69
+ end
70
+
71
+ end
@@ -0,0 +1,254 @@
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 'faraday'
34
+
35
+ class Tml::Application < Tml::Base
36
+ attributes :host, :id, :key, :secret, :access_token, :name, :description, :threshold, :default_locale, :default_level, :tools
37
+ has_many :features, :languages, :featured_locales, :sources, :components, :tokens, :css, :shortcuts, :translations
38
+
39
+ def self.cache_key
40
+ 'application'
41
+ end
42
+
43
+ def self.translations_cache_key(locale)
44
+ "#{locale}/translations"
45
+ end
46
+
47
+ def fetch
48
+ update_attributes(api_client.get('applications/current', {:definition => true}, {:cache_key => self.class.cache_key}))
49
+ rescue Tml::Exception => ex
50
+ Tml.logger.error("Failed to load application: #{ex}")
51
+ self
52
+ end
53
+
54
+ def update_attributes(attrs)
55
+ super
56
+
57
+ self.attributes[:languages] = []
58
+ if hash_value(attrs, :languages)
59
+ self.attributes[:languages] = hash_value(attrs, :languages).collect{ |l| Tml::Language.new(l.merge(:application => self)) }
60
+ end
61
+
62
+ self
63
+ end
64
+
65
+ def language(locale = nil)
66
+ locale = nil if locale.strip == ''
67
+ locale ||= default_locale || Tml.config.default_locale
68
+ @languages_by_locale ||= {}
69
+ @languages_by_locale[locale] ||= api_client.get(
70
+ "languages/#{locale}",
71
+ {:definition => true},
72
+ {
73
+ :class => Tml::Language,
74
+ :attributes => {:locale => locale, :application => self},
75
+ :cache_key => Tml::Language.cache_key(locale)
76
+ }
77
+ )
78
+ rescue Tml::Exception => e
79
+ Tml.logger.error(e)
80
+ Tml.logger.error(e.backtrace)
81
+ @languages_by_locale[locale] = Tml.config.default_language
82
+ end
83
+
84
+ # Mostly used for testing
85
+ def add_language(new_language)
86
+ @languages_by_locale ||= {}
87
+ return @languages_by_locale[new_language.locale] if @languages_by_locale[new_language.locale]
88
+ new_language.application = self
89
+ self.languages << new_language
90
+ @languages_by_locale[new_language.locale] = new_language
91
+ new_language
92
+ end
93
+
94
+ def locales
95
+ @locales ||= languages.collect{|lang| lang.locale}
96
+ end
97
+
98
+ def tools
99
+ @attributes[:tools] || {}
100
+ end
101
+
102
+ def url_for(path)
103
+ "#{tools['assets']}#{path}"
104
+ end
105
+
106
+ def source(source, locale)
107
+ self.sources ||= {}
108
+ self.sources[source] ||= Tml::Source.new(
109
+ :application => self,
110
+ :source => source
111
+ ).fetch_translations(locale)
112
+ end
113
+
114
+ def component(key, register = true)
115
+ key = key.key if key.is_a?(Tml::Component)
116
+
117
+ return self.components[key] if self.components[key]
118
+ return nil unless register
119
+
120
+ self.components[key] ||= api_client.post('components/register', {:component => key}, {:class => Tml::Component, :attributes => {:application => self}})
121
+ end
122
+
123
+ def register_missing_key(source_key, tkey)
124
+ return if Tml.cache.read_only? and not Tml.session.inline_mode?
125
+
126
+ @missing_keys_by_sources ||= {}
127
+ @missing_keys_by_sources[source_key] ||= {}
128
+ @missing_keys_by_sources[source_key][tkey.key] ||= tkey
129
+ submit_missing_keys if Tml.config.submit_missing_keys_realtime
130
+ end
131
+
132
+ def register_keys(keys)
133
+ params = []
134
+ keys.each do |source_key, keys|
135
+ next unless keys.values.any?
136
+ source = Tml::Source.new(:source => source_key, :application => self)
137
+ params << {:source => source_key, :keys => keys.values.collect{|tkey| tkey.to_hash(:label, :description, :locale, :level)}}
138
+ source.reset_cache
139
+ end
140
+
141
+ api_client.post('sources/register_keys', {:source_keys => params.to_json})
142
+ rescue Tml::Exception => e
143
+ Tml.logger.error('Failed to register missing translation keys...')
144
+ Tml.logger.error(e)
145
+ Tml.logger.error(e.backtrace)
146
+ end
147
+
148
+ def submit_missing_keys
149
+ return if @missing_keys_by_sources.nil? or @missing_keys_by_sources.empty?
150
+ register_keys(@missing_keys_by_sources)
151
+ @missing_keys_by_sources = nil
152
+ end
153
+
154
+ def featured_languages
155
+ @featured_languages ||= begin
156
+ locales = api_client.get('applications/current/featured_locales', {}, {:cache_key => 'featured_locales'})
157
+ (locales.nil? or locales.empty?) ? [] : languages.select{|l| locales.include?(l.locale)}
158
+ end
159
+ rescue
160
+ []
161
+ end
162
+
163
+ def translators
164
+ @translators ||= api_client.get('applications/current/translators', {}, {:class => Tml::Translator, :attributes => {:application => self}})
165
+ rescue
166
+ []
167
+ end
168
+
169
+ def reset_translation_cache
170
+ self.sources = {}
171
+ self.translations = {}
172
+ @languages_by_locale = nil
173
+ @missing_keys_by_sources = nil
174
+ end
175
+
176
+ def fetch_translations(locale)
177
+ self.translations ||= {}
178
+ self.translations[locale] ||= begin
179
+ results = Tml.cache.fetch(Tml::Application.translations_cache_key(locale)) do
180
+ data = {}
181
+ unless Tml.cache.read_only?
182
+ api_client.paginate('applications/current/translations', :per_page => 1000) do |translations|
183
+ data.merge!(translations)
184
+ end
185
+ end
186
+ data
187
+ end
188
+
189
+ translations_by_key = {}
190
+ results.each do |key, data|
191
+ translations_data = data.is_a?(Hash) ? data['translations'] : data
192
+ translations_by_key[key] = translations_data.collect do |t|
193
+ Tml::Translation.new(
194
+ :locale => t['locale'] || locale,
195
+ :label => t['label'],
196
+ :context => t['context']
197
+ )
198
+ end
199
+ end
200
+ translations_by_key
201
+ end
202
+ rescue Tml::Exception => ex
203
+ {}
204
+ end
205
+
206
+ def cache_translations(locale, key, new_translations)
207
+ self.translations ||= {}
208
+ self.translations[locale] ||= {}
209
+ self.translations[locale][key] = new_translations.collect do |t|
210
+ Tml::Translation.new(
211
+ :locale => t['locale'] || locale,
212
+ :label => t['label'],
213
+ :context => t['context']
214
+ )
215
+ end
216
+ end
217
+
218
+ def cached_translations(locale, key)
219
+ return unless self.translations and self.translations[locale]
220
+ self.translations[locale][key]
221
+ end
222
+
223
+ def debug_translations
224
+ return 'no translations' unless self.translations
225
+ self.translations.each do |locale, keys|
226
+ pp [locale, keys.collect{|key, translations|
227
+ [key, translations.collect{|t|
228
+ [t.label, t.context]
229
+ }]
230
+ }]
231
+ end
232
+ end
233
+
234
+ def default_decoration_token(token)
235
+ hash_value(tokens, "decoration.#{token.to_s}")
236
+ end
237
+
238
+ def default_data_token(token)
239
+ hash_value(tokens, "data.#{token.to_s}")
240
+ end
241
+
242
+ def feature_enabled?(key)
243
+ hash_value(features, key.to_s)
244
+ end
245
+
246
+ def api_client
247
+ @api_client ||= Tml::Api::Client.new(:application => self)
248
+ end
249
+
250
+ def postoffice
251
+ @postoffice ||= Tml::Api::PostOffice.new(:application => self)
252
+ end
253
+
254
+ end
data/lib/tml/base.rb ADDED
@@ -0,0 +1,116 @@
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::Base
34
+ attr_reader :attributes
35
+
36
+ def initialize(attrs = {})
37
+ @attributes = {}
38
+ update_attributes(attrs)
39
+ end
40
+
41
+ def update_attributes(attrs = {})
42
+ attrs.each do |key, value|
43
+ #pp [self.class.name, key, self.class.attributes, self.class.attributes.include?(key.to_sym)]
44
+ next unless self.class.attributes.include?(key.to_sym)
45
+ @attributes[key.to_sym] = value
46
+ end
47
+ end
48
+
49
+ def self.attributes(*attrs)
50
+ @attribute_names ||= []
51
+ @attribute_names += attrs.collect{|a| a.to_sym} unless attrs.nil?
52
+ @attribute_names
53
+ end
54
+ def self.belongs_to(*attrs) self.attributes(*attrs); end
55
+ def self.has_many(*attrs) self.attributes(*attrs); end
56
+
57
+ def method_missing(meth, *args, &block)
58
+ method_name = meth.to_s
59
+ method_suffix = method_name[-1, 1]
60
+ method_key = method_name.to_sym
61
+ if %w(= ?).include?(method_suffix)
62
+ method_key = method_name[0..-2].to_sym
63
+ end
64
+
65
+ if self.class.attributes.index(method_key)
66
+ if method_suffix == '='
67
+ attributes[method_key] = args.first
68
+ return attributes[method_key]
69
+ end
70
+ return attributes[method_key]
71
+ end
72
+
73
+ super
74
+ end
75
+
76
+ def self.hash_value(hash, key, opts = {})
77
+ return nil unless hash.is_a?(Hash)
78
+ return hash[key.to_s] || hash[key.to_sym] if opts[:whole]
79
+
80
+ value = hash
81
+ key.to_s.split('.').each do |part|
82
+ return nil unless value.is_a?(Hash)
83
+ value = value[part.to_sym] || value[part.to_s]
84
+ end
85
+ value
86
+ end
87
+
88
+ def hash_value(hash, key, opts = {})
89
+ self.class.hash_value(hash, key, opts)
90
+ end
91
+
92
+ def to_hash(*attrs)
93
+ if attrs.nil? or attrs.empty?
94
+ # default hashing only includes basic types
95
+ keys = []
96
+ self.class.attributes.each do |key|
97
+ value = attributes[key]
98
+ next if value.kind_of?(Tml::Base) or value.kind_of?(Hash) or value.kind_of?(Array)
99
+ keys << key
100
+ end
101
+ else
102
+ keys = attrs
103
+ end
104
+
105
+ hash = {}
106
+ keys.each do |key|
107
+ hash[key] = attributes[key]
108
+ end
109
+
110
+ proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&proc); nil) : v.nil? }
111
+ hash.delete_if(&proc)
112
+
113
+ hash
114
+ end
115
+
116
+ end
data/lib/tml/cache.rb ADDED
@@ -0,0 +1,143 @@
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
+ CACHE_VERSION_KEY = '__tml_version__'
36
+
37
+ def self.memory
38
+ @memory ||= Tml::CacheAdapters::Memory.new
39
+ end
40
+
41
+ def self.cache
42
+ @cache ||= begin
43
+ if Tml.config.cache_enabled?
44
+ klass = Tml::CacheAdapters.const_get(Tml.config.cache[:adapter].camelcase)
45
+ klass.new
46
+ else
47
+ # blank implementation
48
+ Tml::Cache.new
49
+ end
50
+ end
51
+ end
52
+
53
+ class Cache
54
+
55
+ def version
56
+ Tml.config.cache[:version] ||= 1
57
+
58
+ @version ||= begin
59
+ v = fetch(CACHE_VERSION_KEY) do
60
+ {'version' => Tml.config.cache[:version]}
61
+ end
62
+ v['version']
63
+ end
64
+
65
+ @version ||= Tml.config.cache[:version]
66
+
67
+ if Tml.config.cache[:version] > @version
68
+ update_version(Tml.config.cache[:version])
69
+ @version = Tml.config.cache[:version]
70
+ end
71
+
72
+ @version
73
+ end
74
+
75
+ def update_version(new_version)
76
+ store(CACHE_VERSION_KEY, {'version' => new_version}, :ttl => 0)
77
+ end
78
+
79
+ def upgrade_version
80
+ update_version(version + 1)
81
+ @version = nil
82
+ end
83
+
84
+ def reset_version
85
+ @version = nil
86
+ end
87
+
88
+ def enabled?
89
+ Tml.config.cache[:enabled]
90
+ end
91
+
92
+ def cached_by_source?
93
+ true
94
+ end
95
+
96
+ def read_only?
97
+ false
98
+ end
99
+
100
+ def segmented?
101
+ true
102
+ end
103
+
104
+ def cache_name
105
+ self.class.name.split('::').last
106
+ end
107
+
108
+ def info(msg)
109
+ Tml.logger.info("#{cache_name} - #{msg}")
110
+ end
111
+
112
+ def warn(msg)
113
+ Tml.logger.warn("#{cache_name} - #{msg}")
114
+ end
115
+
116
+ def versioned_key(key, opts = {})
117
+ return key if CACHE_VERSION_KEY == key
118
+ "tml_rc_v#{version}_#{key}"
119
+ end
120
+
121
+ def fetch(key, opts = {})
122
+ return nil unless block_given?
123
+ yield
124
+ end
125
+
126
+ def store(key, data, opts = {})
127
+ # do nothing
128
+ end
129
+
130
+ def delete(key, opts = {})
131
+ # do nothing
132
+ end
133
+
134
+ def exist?(key, opts = {})
135
+ false
136
+ end
137
+
138
+ def clear(opts = {})
139
+ # do nothing
140
+ end
141
+
142
+ end
143
+ end