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,99 @@
1
+ #--
2
+ # Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
3
+ #
4
+ # _______ _ _ _ ______ _
5
+ # |__ __| | | | | (_) | ____| | |
6
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
7
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
8
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
9
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
10
+ # __/ |
11
+ # |___/
12
+ # Permission is hereby granted, free of charge, to any person obtaining
13
+ # a copy of this software and associated documentation files (the
14
+ # "Software"), to deal in the Software without restriction, including
15
+ # without limitation the rights to use, copy, modify, merge, publish,
16
+ # distribute, sublicense, and/or sell copies of the Software, and to
17
+ # permit persons to whom the Software is furnished to do so, subject to
18
+ # the following conditions:
19
+ #
20
+ # The above copyright notice and this permission notice shall be
21
+ # included in all copies or substantial portions of the Software.
22
+ #
23
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ #++
31
+
32
+ class Hash
33
+ #
34
+ # = Hash Recursive Merge
35
+ #
36
+ # Merges a Ruby Hash recursively, Also known as deep merge.
37
+ # Recursive version of Hash#merge and Hash#merge!.
38
+ #
39
+ # Category:: Ruby
40
+ # Package:: Hash
41
+ # Author:: Simone Carletti <weppos@weppos.net>
42
+ # Copyright:: 2007-2008 The Authors
43
+ # License:: MIT License
44
+ # Link:: http://www.simonecarletti.com/
45
+ # Source:: http://gist.github.com/gists/6391/
46
+
47
+ #
48
+ # Recursive version of Hash#merge!
49
+ #
50
+ # Adds the contents of +other_hash+ to +hsh+,
51
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
52
+ #
53
+ # Compared with Hash#merge!, this method supports nested hashes.
54
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
55
+ # it merges and returns the values from both arrays.
56
+ #
57
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
58
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
59
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
60
+ #
61
+ # Simply using Hash#merge! would return
62
+ #
63
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
64
+ #
65
+ def rmerge!(other_hash)
66
+ merge!(other_hash) do |key, oldval, newval|
67
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
68
+ end
69
+ end
70
+
71
+ #
72
+ # Recursive version of Hash#merge
73
+ #
74
+ # Compared with Hash#merge!, this method supports nested hashes.
75
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
76
+ # it merges and returns the values from both arrays.
77
+ #
78
+ # Compared with Hash#merge, this method provides a different approch
79
+ # for merging nasted hashes.
80
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
81
+ # includes the same key, the value is merged instead replaced with
82
+ # +other_hash+ value.
83
+ #
84
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
85
+ # h2 = {"b" => 254, "c" => 300, "c" => {"c1" => 16, "c3" => 94}}
86
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
87
+ #
88
+ # Simply using Hash#merge would return
89
+ #
90
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
91
+ #
92
+ def rmerge(other_hash)
93
+ r = {}
94
+ merge(other_hash) do |key, oldval, newval|
95
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
96
+ end
97
+ end
98
+
99
+ end
@@ -0,0 +1,56 @@
1
+ #--
2
+ # Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
3
+ #
4
+ # _______ _ _ _ ______ _
5
+ # |__ __| | | | | (_) | ____| | |
6
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
7
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
8
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
9
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
10
+ # __/ |
11
+ # |___/
12
+ # Permission is hereby granted, free of charge, to any person obtaining
13
+ # a copy of this software and associated documentation files (the
14
+ # "Software"), to deal in the Software without restriction, including
15
+ # without limitation the rights to use, copy, modify, merge, publish,
16
+ # distribute, sublicense, and/or sell copies of the Software, and to
17
+ # permit persons to whom the Software is furnished to do so, subject to
18
+ # the following conditions:
19
+ #
20
+ # The above copyright notice and this permission notice shall be
21
+ # included in all copies or substantial portions of the Software.
22
+ #
23
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ #++
31
+
32
+ class String
33
+
34
+ def translate(desc = '', tokens = {}, options = {})
35
+ Tml.session.target_language.translate(
36
+ Tml::Utils.normalize_tr_params(self, desc, tokens, options)
37
+ )
38
+ end
39
+
40
+ def trl(desc = '', tokens = {}, options = {})
41
+ Tml.session.target_language.translate(
42
+ Tml::Utils.normalize_tr_params(self, desc, tokens, options.merge(:skip_decorations => true))
43
+ )
44
+ end
45
+
46
+ def tml_translated
47
+ return self if frozen?
48
+ @tml_translated = true
49
+ self
50
+ end
51
+
52
+ def tml_translated?
53
+ @tml_translated
54
+ end
55
+
56
+ end
@@ -0,0 +1,89 @@
1
+ #--
2
+ # Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
3
+ #
4
+ # _______ _ _ _ ______ _
5
+ # |__ __| | | | | (_) | ____| | |
6
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
7
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
8
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
9
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
10
+ # __/ |
11
+ # |___/
12
+ # Permission is hereby granted, free of charge, to any person obtaining
13
+ # a copy of this software and associated documentation files (the
14
+ # "Software"), to deal in the Software without restriction, including
15
+ # without limitation the rights to use, copy, modify, merge, publish,
16
+ # distribute, sublicense, and/or sell copies of the Software, and to
17
+ # permit persons to whom the Software is furnished to do so, subject to
18
+ # the following conditions:
19
+ #
20
+ # The above copyright notice and this permission notice shall be
21
+ # included in all copies or substantial portions of the Software.
22
+ #
23
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ #++
31
+
32
+ class Time
33
+
34
+ def translate(format = :default, options = {})
35
+ language = Tml.session.target_language
36
+
37
+ label = (format.is_a?(String) ? format.clone : Tml.config.default_date_formats[format].clone)
38
+ symbols = label.scan(/(%\w)/).flatten.uniq
39
+
40
+ selected_tokens = []
41
+ using_tokens = label.index('{')
42
+
43
+ if using_tokens
44
+ selected_tokens = Tml::Tokens::Data.parse(label).collect{ |token| token.name(:parens => true) }
45
+ else
46
+ symbols.each do |symbol|
47
+ token = Tml.config.strftime_symbol_to_token(symbol)
48
+ next unless token
49
+ selected_tokens << token
50
+ label.gsub!(symbol, token)
51
+ end
52
+ end
53
+
54
+ tokens = {}
55
+ selected_tokens.each do |token|
56
+ case token
57
+ when '{days}' then tokens[:days] = options[:with_leading_zero] ? day.with_leading_zero : day.to_s
58
+ when '{year_days}' then tokens[:year_days] = options[:with_leading_zero] ? yday.with_leading_zero : yday.to_s
59
+ when '{months}' then tokens[:months] = options[:with_leading_zero] ? month.with_leading_zero : month.to_s
60
+ when '{week_num}' then tokens[:week_num] = wday
61
+ when '{week_days}' then tokens[:week_days] = strftime('%w')
62
+ when '{short_years}' then tokens[:short_years] = strftime('%y')
63
+ when '{years}' then tokens[:years] = year
64
+ when '{short_week_day_name}' then tokens[:short_week_day_name] = language.tr(Tml.config.default_abbr_day_name(wday), 'Short name for a day of a week', {}, options)
65
+ when '{week_day_name}' then tokens[:week_day_name] = language.tr(Tml.config.default_day_name(wday), 'Day of a week', {}, options)
66
+ when '{short_month_name}' then tokens[:short_month_name] = language.tr(Tml.config.default_abbr_month_name(month - 1), 'Short month name', {}, options)
67
+ when '{month_name}' then tokens[:month_name] = language.tr(Tml.config.default_month_name(month - 1), 'Month name', {}, options)
68
+ when '{am_pm}' then tokens[:am_pm] = language.tr(strftime('%p'), 'Meridian indicator', {}, options)
69
+ when '{full_hours}' then tokens[:full_hours] = hour
70
+ when '{short_hours}' then tokens[:short_hours] = strftime('%I')
71
+ when '{trimed_hour}' then tokens[:trimed_hour] = strftime('%l')
72
+ when '{minutes}' then tokens[:minutes] = strftime('%M')
73
+ when '{seconds}' then tokens[:seconds] = strftime('%S')
74
+ when '{since_epoch}' then tokens[:since_epoch] = strftime('%s')
75
+ when '{day_of_month}' then tokens[:day_of_month] = strftime('%e')
76
+ else
77
+ ''
78
+ end
79
+ end
80
+
81
+ language.tr(label, nil, tokens, options)
82
+ end
83
+ alias :tr :translate
84
+
85
+ def trl(format = :default, options = {})
86
+ tr(format, options.merge!(:skip_decorations => true))
87
+ end
88
+
89
+ end
@@ -0,0 +1,117 @@
1
+ #--
2
+ # Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
3
+ #
4
+ # _______ _ _ _ ______ _
5
+ # |__ __| | | | | (_) | ____| | |
6
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
7
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
8
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
9
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
10
+ # __/ |
11
+ # |___/
12
+ # Permission is hereby granted, free of charge, to any person obtaining
13
+ # a copy of this software and associated documentation files (the
14
+ # "Software"), to deal in the Software without restriction, including
15
+ # without limitation the rights to use, copy, modify, merge, publish,
16
+ # distribute, sublicense, and/or sell copies of the Software, and to
17
+ # permit persons to whom the Software is furnished to do so, subject to
18
+ # the following conditions:
19
+ #
20
+ # The above copyright notice and this permission notice shall be
21
+ # included in all copies or substantial portions of the Software.
22
+ #
23
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ #++
31
+
32
+ class Tml::Generators::Cache::Base
33
+
34
+ attr_accessor :started_at, :finished_at
35
+
36
+ def log(msg)
37
+ msg = "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}: #{msg}"
38
+ puts msg
39
+ Tml.logger.debug(msg)
40
+ end
41
+
42
+ def cache_path
43
+ raise Tml::Exception.new('Must be implemented by the subclass')
44
+ end
45
+
46
+ def cache_version
47
+ @started_at.strftime('%Y%m%d%H%M%S')
48
+ end
49
+
50
+ def cache(key, data)
51
+ raise Tml::Exception.new('Must be implemented by the subclass')
52
+ end
53
+
54
+ def execute
55
+ raise Tml::Exception.new('Must be implemented by the subclass')
56
+ end
57
+
58
+ def run
59
+ prepare
60
+ execute
61
+ finalize
62
+ end
63
+
64
+ def prepare
65
+ @started_at = Time.now
66
+ Tml.session.init
67
+ cache_path
68
+ end
69
+
70
+ def api_client
71
+ Tml.session.application.api_client
72
+ end
73
+
74
+ def application
75
+ @application ||= api_client.get('applications/current', {:definition => true})
76
+ end
77
+
78
+ def languages
79
+ application['languages']
80
+ end
81
+
82
+ def finalize
83
+ @finished_at = Time.now
84
+ log("Cache has been stored in #{cache_path}")
85
+ log("Cache generation took #{@finished_at - @started_at} mls.")
86
+ log('Done.')
87
+ end
88
+
89
+ def cache_application
90
+ log('Downloading application...')
91
+ cache(Tml::Application.cache_key, application)
92
+ log('Application has been cached.')
93
+ end
94
+
95
+ def cache_languages
96
+ log('Downloading languages...')
97
+ unless application['languages']
98
+ log('No languages are available...')
99
+ return
100
+ end
101
+ languages.each do |lang|
102
+ language = api_client.get("languages/#{lang['locale']}", :definition => true)
103
+ cache(Tml::Language.cache_key(language['locale']), language)
104
+ end
105
+ log("#{application['languages'].count} languages have been cached.")
106
+ end
107
+
108
+ def symlink_path
109
+ "#{Tml.config.cache[:path]}/current"
110
+ end
111
+
112
+ def generate_symlink
113
+ FileUtils.rm(symlink_path) if File.exist?(symlink_path)
114
+ FileUtils.ln_s(cache_version, symlink_path)
115
+ log('Symlink has been updated.')
116
+ end
117
+ end
@@ -0,0 +1,159 @@
1
+ #--
2
+ # Copyright (c) 2015 Translation Exchange Inc. http://translationexchange.com
3
+ #
4
+ # _______ _ _ _ ______ _
5
+ # |__ __| | | | | (_) | ____| | |
6
+ # | |_ __ __ _ _ __ ___| | __ _| |_ _ ___ _ __ | |__ __ _____| |__ __ _ _ __ __ _ ___
7
+ # | | '__/ _` | '_ \/ __| |/ _` | __| |/ _ \| '_ \| __| \ \/ / __| '_ \ / _` | '_ \ / _` |/ _ \
8
+ # | | | | (_| | | | \__ \ | (_| | |_| | (_) | | | | |____ > < (__| | | | (_| | | | | (_| | __/
9
+ # |_|_| \__,_|_| |_|___/_|\__,_|\__|_|\___/|_| |_|______/_/\_\___|_| |_|\__,_|_| |_|\__, |\___|
10
+ # __/ |
11
+ # |___/
12
+ # Permission is hereby granted, free of charge, to any person obtaining
13
+ # a copy of this software and associated documentation files (the
14
+ # "Software"), to deal in the Software without restriction, including
15
+ # without limitation the rights to use, copy, modify, merge, publish,
16
+ # distribute, sublicense, and/or sell copies of the Software, and to
17
+ # permit persons to whom the Software is furnished to do so, subject to
18
+ # the following conditions:
19
+ #
20
+ # The above copyright notice and this permission notice shall be
21
+ # included in all copies or substantial portions of the Software.
22
+ #
23
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ #++
31
+
32
+ class Tml::Generators::Cache::File < Tml::Generators::Cache::Base
33
+
34
+ def cache_path
35
+ @cache_path ||= begin
36
+ path = "#{Tml.config.cache[:path]}/#{cache_version}"
37
+ log("Cache will be stored in #{path}")
38
+ FileUtils.mkdir_p(path)
39
+ FileUtils.chmod(0777, path)
40
+ path
41
+ end
42
+ end
43
+
44
+ def file_path(key)
45
+ path = key.split('/')
46
+ if path.count > 1
47
+ filename = path.pop
48
+ path = File.join(cache_path, path)
49
+ FileUtils.mkdir_p(path)
50
+ File.join(path, "#{filename}.json")
51
+ else
52
+ File.join(cache_path, "#{path.first}.json")
53
+ end
54
+ end
55
+
56
+ def cache(key, data)
57
+ File.open(file_path(key), 'w') { |file| file.write(JSON.pretty_generate(data)) }
58
+ end
59
+
60
+ def execute
61
+ cache_application
62
+ cache_languages
63
+ cache_translations
64
+ generate_symlink
65
+ end
66
+
67
+ def cache_translations
68
+ log('Downloading translations...')
69
+
70
+ languages.each do |language|
71
+ log("Downloading #{language['english_name']} language...")
72
+
73
+ if Tml.config.cache[:segmented]
74
+ api_client.paginate('applications/current/sources') do |source|
75
+ next unless source['source']
76
+
77
+ cache_path = Tml::Source.cache_key(language['locale'], source['source'])
78
+ log("Downloading #{source['source']} in #{language['locale']} to #{cache_path}...")
79
+
80
+ data = api_client.get("sources/#{source['key']}/translations", {:locale => language['locale'], :original => true, :per_page => 1000})
81
+ cache(cache_path, data)
82
+ end
83
+ else
84
+ cache_path = Tml::Application.translations_cache_key(language['locale'])
85
+ log("Downloading translations in #{language['locale']} to #{cache_path}...")
86
+ data = {}
87
+ api_client.paginate('applications/current/translations', {:locale => language['locale'], :original => true, :per_page => 1000}) do |translations|
88
+ data.merge!(translations)
89
+ end
90
+ cache(cache_path, data)
91
+ end
92
+ end
93
+ end
94
+
95
+ def rollback
96
+ folders = Dir["#{Tml.config.cache[:path]}/*"]
97
+ folders.delete_if{|e| e.index('current')}.sort!
98
+
99
+ if File.exist?(symlink_path)
100
+ current_dest = File.readlink("#{Tml.config.cache[:path]}/current")
101
+ current_dest = "#{Tml.config.cache[:path]}/#{current_dest}"
102
+ else
103
+ current_dest = 'undefined'
104
+ end
105
+
106
+ index = folders.index(current_dest)
107
+
108
+ if index == 0
109
+ log('There are no earlier cache versions')
110
+ return
111
+ end
112
+
113
+ if index.nil?
114
+ new_version_path = folders[folders.size-1]
115
+ else
116
+ new_version_path = folders[index-1]
117
+ end
118
+
119
+ new_version_path = new_version_path.split('/').last
120
+
121
+ FileUtils.rm(symlink_path) if File.exist?(symlink_path)
122
+ FileUtils.ln_s(new_version_path, symlink_path)
123
+
124
+ log("Cache has been rolled back to version #{new_version_path}.")
125
+ end
126
+
127
+ def rollup
128
+ folders = Dir["#{Tml.config.cache[:path]}/*"]
129
+ folders.delete_if{|e| e.index('current')}.sort!
130
+
131
+ if File.exist?(symlink_path)
132
+ current_dest = File.readlink("#{Tml.config.cache[:path]}/current")
133
+ current_dest = "#{Tml.config.cache[:path]}/#{current_dest}"
134
+ else
135
+ current_dest = 'undefined'
136
+ end
137
+
138
+ index = folders.index(current_dest)
139
+
140
+ if index == (folders.size - 1)
141
+ log('You are on the latest version of the cache already. No further versions are available')
142
+ return
143
+ end
144
+
145
+ if index.nil?
146
+ new_version_path = folders[0]
147
+ else
148
+ new_version_path = folders[index+1]
149
+ end
150
+
151
+ new_version_path = new_version_path.split('/').last
152
+
153
+ FileUtils.rm(symlink_path) if File.exist?(symlink_path)
154
+ FileUtils.ln_s(new_version_path, symlink_path)
155
+
156
+ log("Cache has been upgraded to version #{new_version_path}.")
157
+ end
158
+
159
+ end