tml 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
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