tool_pouch 0.0.34

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.
@@ -0,0 +1,43 @@
1
+ require 'base64'
2
+ require 'cgi'
3
+ require 'digest/sha1'
4
+ require 'digest/md5'
5
+ require 'find'
6
+ require 'net/http'
7
+ require 'openssl'
8
+ require 'yaml'
9
+ require 'uri'
10
+ # HashTree
11
+
12
+ module ToolPouch
13
+ DATA_PATH = File.expand_path("../../data", __FILE__) + '/'
14
+
15
+ def self.get_data(name, extension='yml')
16
+ set_data_cache(name, YAML.load_file("#{DATA_PATH}#{name}.#{extension}")['data']) if not data_cached?(name)
17
+
18
+ return get_data_cache(name)
19
+ end
20
+
21
+ def self.get_data_cache(name)
22
+ @data_cache[name]
23
+ end
24
+
25
+ def self.set_data_cache(name, data)
26
+ @data_cache = {} if not @data_cache
27
+
28
+ @data_cache[name] = data
29
+
30
+ return @data_cache[name]
31
+ end
32
+
33
+ def self.data_cached?(name)
34
+ @data_cache and @data_cache[name]
35
+ end
36
+ end
37
+
38
+ require 'tool_pouch/babel'
39
+ require 'tool_pouch/krono'
40
+ require 'tool_pouch/locksmith'
41
+ require 'tool_pouch/mathema'
42
+ require 'tool_pouch/trail'
43
+ require 'tool_pouch/transistor'
@@ -0,0 +1,225 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module ToolPouch::Babel
3
+ def self.bytes_to(size, options={})
4
+ options.reverse_merge!(:unit => nil, :show_unit => true)
5
+
6
+ unit = options[:unit]
7
+
8
+ divider = 1
9
+
10
+ unless options[:unit]
11
+ if size < 1000
12
+ unit = :b
13
+ elsif size < 1000000
14
+ unit = :kb
15
+ elsif size < 1000000000
16
+ unit = :mb
17
+ else
18
+ unit = :gb
19
+ end
20
+ end
21
+
22
+ case unit
23
+ when :kb then divider = 1000
24
+ when :mb then divider = 1000000
25
+ when :gb then divider = 1000000000
26
+ end
27
+
28
+ sprintf("%0.02f", (size.to_f / divider)) + (options[:show_unit] ? ' ' + unit.to_s.upcase_utf8 : '')
29
+ end
30
+
31
+ def self.convert_accents(str)
32
+ str_to_convert = String.new(str)
33
+ table = {'Á' => 'A', 'Â' => 'A', 'Ä' => 'A', 'À' => 'A', 'Å' => 'A', 'Ç' => 'C', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'È' => 'E', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ì' => 'I', 'Ñ' => 'N', 'Œ' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Ö' => 'O', 'Ò' => 'O', 'Õ' => 'O', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ù' => 'U', 'Ý' => 'Y', 'Ÿ' => 'Y', 'Ž' => 'Z', 'á' => 'a', 'â' => 'a', 'ä' => 'a', 'æ' => 'a', 'à' => 'a', 'å' => 'a', 'ã' => 'a', 'ç' => 'c', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'è' => 'e', 'ð' => 'o', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ì' => 'i', 'ñ' => 'n', 'ó' => 'o', 'ô' => 'o', 'ö' => 'o', 'œ' => 'o', 'ò' => 'o', 'õ' => 'o', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ù' => 'u', 'ý' => 'y', 'ÿ' => 'y', 'ž' => 'z'}
34
+
35
+ table.each { |k, v| str_to_convert.gsub!(k, v) }
36
+
37
+ return str_to_convert
38
+ end
39
+
40
+ def self.currency_for_country(country)
41
+ case country
42
+ when 'fra', 'ita', 'lux', 'bel', 'deu' then 'eur'
43
+ when 'can' then 'cad'
44
+ when 'usa' then 'usd'
45
+ when 'che' then 'chf'
46
+ end
47
+ end
48
+
49
+ def self.currency_for_region(region)
50
+ return case region
51
+ when 'europe' then 'eur'
52
+ when 'quebec' then 'cad'
53
+ when 'usa' then 'usd'
54
+ when 'suisse' then 'chf'
55
+ else ''
56
+ end
57
+ end
58
+
59
+ def self.enumerate(terms)
60
+ last_element = terms.pop;
61
+
62
+ if terms.length == 0
63
+ return last_element;
64
+ else
65
+ return terms.join(', ') + ' ' + I18n.t('and') + ' ' + last_element;
66
+ end
67
+ end
68
+
69
+ def self.extract_first_quotes(value)
70
+ value.index('"') ? value.split('"')[1] : ''
71
+ end
72
+
73
+ def self.extract_first_tag(value)
74
+ if value.index('<') and value.index('>')
75
+ return value[value.index('<') + 2, value.rindex('>') - value.index('<')-2]
76
+ else
77
+ return ''
78
+ end
79
+ end
80
+
81
+ def self.random_string(size, options={})
82
+ options.reverse_merge!(:force_array => false, :quantity => 1, :reject => [])
83
+
84
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
85
+ strings = []
86
+ try = 0
87
+
88
+ options[:quantity].times do
89
+ new_random = ''
90
+ while new_random.empty? do
91
+ 1.upto(size) { |i| new_random << chars[rand(chars.size)] }
92
+
93
+ new_random = '' if options[:reject].include?(new_random)
94
+ try += 1
95
+
96
+ return nil if try > 500
97
+ end
98
+ options[:reject] << new_random
99
+ strings << new_random
100
+ end
101
+
102
+ (options[:quantity] == 1 and not options[:force_array]) ? strings.first : strings
103
+ end
104
+
105
+ def self.region_for_country(country)
106
+ return case country.downcase
107
+ when 'ita', 'fra', 'lux', 'bel', 'deu' then 'europe'
108
+ when 'che' then 'suisse'
109
+ when 'can' then 'quebec'
110
+ when 'usa' then 'usa'
111
+ end
112
+ end
113
+
114
+ def self.strip_html(value)
115
+ (value) ? value.to_s.gsub(/<\/?[^>]*>/, "") : value
116
+ end
117
+
118
+ def self.to_currency_symbol(unit)
119
+ return case unit.downcase
120
+ when 'cad', 'usd' then '$'
121
+ when 'eur' then '€'
122
+ when 'chf' then 'CHF'
123
+ else ''
124
+ end
125
+ end
126
+
127
+ # TODO : Finish the method
128
+ def self.to_iso3166_1(country_code, options={})
129
+ options.reverse_merge!(:to_alpha => '3')
130
+
131
+ from_2_to_3 = { "be" => "bel", "ca" => "can", "ch" => "che", "de" => "deu", "fr" => "fra", "it" => "ita", "lx" => "lux", "us" => "usa" }
132
+ from_3_to_2 = from_2_to_3.invert
133
+
134
+ if options[:to_alpha].to_s == '3'
135
+ return from_2_to_3[country_code.to_s.downcase]
136
+ elsif options[:to_alpha].to_s == '2'
137
+ return from_3_to_2[country_code.to_s.downcase]
138
+ else
139
+ return nil
140
+ end
141
+ end
142
+
143
+ def self.to_iso639(language, options={})
144
+ options.reverse_merge!(:from => nil, :to => '2b')
145
+
146
+ options[:from] = options[:from] ? [options[:from]].flatten : ['1', '2b', '2t', '3']
147
+
148
+ options[:from].each do |from|
149
+ table = ToolPouch.get_data('iso639')['iso639_convert']["#{from}_to_#{options[:to]}"]
150
+
151
+ return table[language] if table and not table[language].to_s.empty?
152
+ end
153
+
154
+ return language
155
+ end
156
+
157
+ def self.to_latin_encoded(value)
158
+ latin_table = { 'Æ' => '306', 'Á' => '301', 'Â' => '302', 'Ä' => '304', 'À' => '300', 'Å' => '305', 'Ã' => '303', 'Ç' => '307', 'É' => '311', 'Ê' => '312', 'Ë' => '313', 'È' => '310', 'Ð' => '320', '€' => '240', 'Í' => '315', 'Î' => '316', 'Ï' => '317', 'Ì' => '314', 'Ł' => '225', 'Ñ' => '321', 'Œ' => '226', 'Ó' => '323', 'Ô' => '324', 'Ö' => '326', 'Ò' => '322', 'Ø' => '330', 'Õ' => '325', 'Š' => '227', 'Þ' => '336', 'Ú' => '332', 'Û' => '333', 'Ü' => '334', 'Ù' => '331', 'Ý' => '335', 'Ÿ' => '230', 'Ž' => '231', 'á' => '341', 'â' => '342', '´' => '264', 'ä' => '344', 'æ' => '346', 'à' => '340', 'å' => '345', '^' => '136', '~' => '176', '*' => '052', 'ã' => '343', '˘' => '030', '¦' => '246', '•' => '200', 'ˇ' => '031', 'ç' => '347', '¸' => '270', '¢' => '242', 'ˆ' => '032', '©' => '251', '¤' => '244', '†' => '201', '‡' => '202', '°' => '260', '÷' => '367', 'ı' => '232', 'é' => '351', 'ê' => '352', 'ë' => '353', 'è' => '350', 'ð' => '360', '¡' => '241', 'fi' => '223', 'fl' => '224', 'ƒ' => '206', 'ß' => '337', '«' => '253', '»' => '273', 'í' => '355', 'î' => '356', 'ï' => '357', 'ì' => '354', 'ł' => '233', 'µ' => '265', '×' => '327', 'ñ' => '361', '#' => '043', 'ó' => '363', 'ô' => '364', 'ö' => '366', 'œ' => '234', 'ò' => '362', 'ª' => '252', 'º' => '272', 'ø' => '370', 'õ' => '365', '¶' => '266', '¿' => '277', '„' => '214', '“' => '215', '”' => '216', '®' => '256', 'š' => '235', '§' => '247', '£' => '243', 'þ' => '376', '™' => '222', 'ú' => '372', 'û' => '373', 'ü' => '374', 'ù' => '371', 'ý' => '375', 'ÿ' => '377', '¥' => '245', 'ž' => '236' }
159
+ value_encoded = ''
160
+
161
+ value.each_char { |c| value_encoded << (latin_table[c] ? "\\" + latin_table[c] : c) }
162
+
163
+ value_encoded
164
+ end
165
+
166
+ def self.to_price(amount, options={})
167
+ options.reverse_merge!(:separator => ',', :format => '%n%u', :unit => 'cad')
168
+
169
+ price = amount.to_s.rjust(3,'0')
170
+ price = price[0..-3] + options[:separator] + price[-2..-1]
171
+
172
+ if options[:unit]
173
+ unit_symbol = to_currency_symbol(options[:unit])
174
+ price = options[:format].gsub('%u', unit_symbol).gsub('%n', price)
175
+ end
176
+
177
+ return price
178
+ end
179
+
180
+ def self.to_roman(numeric)
181
+ numerals = [
182
+ { :roman => 'M', :arab => 1000 },
183
+ { :roman => 'CM', :arab => 900 },
184
+ { :roman => 'D', :arab => 500 },
185
+ { :roman => 'CD', :arab => 400 },
186
+ { :roman => 'C', :arab => 100 },
187
+ { :roman => 'XC', :arab => 90 },
188
+ { :roman => 'L', :arab => 50 },
189
+ { :roman => 'XL', :arab => 40 },
190
+ { :roman => 'X', :arab => 10 },
191
+ { :roman => 'IX', :arab => 9 },
192
+ { :roman => 'V', :arab => 5 },
193
+ { :roman => 'IV', :arab => 4 },
194
+ { :roman => 'I', :arab => 1 }
195
+ ]
196
+
197
+ roman = '';
198
+ numerals.each do |num|
199
+ while numeric >= num[:arab]
200
+ roman += num[:roman]
201
+ numeric -= num[:arab]
202
+ end
203
+ end
204
+
205
+ return roman
206
+ end
207
+
208
+ def self.to_slug(value)
209
+ value.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.gsub(/[^[:alnum:]]/,'-')
210
+ end
211
+
212
+ def self.to_slug_advanced(value)
213
+ to_slug(value).gsub(/-{2,}/,'-').gsub(/-\Z/, '')
214
+ end
215
+
216
+ def self.validate_email?(email, options={})
217
+ options.reverse_merge!(:allow_blank => false)
218
+
219
+ if options[:allow_blank] and email.blank?
220
+ return true
221
+ else
222
+ return (email.match(/^\S+@\S+\.\S+$/) ? true : false)
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,153 @@
1
+ module ToolPouch::Krono
2
+ def self.datestamp(date=nil)
3
+ date = Time.now unless date
4
+
5
+ return date.strftime("%Y%m%d")
6
+ end
7
+
8
+ def self.floor(datetime, seconds=60)
9
+ return Time.at((datetime.to_f / seconds).floor * seconds)
10
+ end
11
+
12
+ def self.format_in_time(number)
13
+ minutes = (number >= 1) ? number.modulo(number.floor) : number
14
+ minutes = (minutes * 60).to_i.to_s
15
+
16
+ return number.floor.to_s.rjust(2, '0') + ':' + minutes.rjust(2, '0')
17
+ end
18
+
19
+ def self.is_iso8601?(date)
20
+ date.is_a? String and date =~ /^\d{4}-(0\d|1[0-2])-([0-2]\d|3[0-1])T([0-1]\d|2[0-4]):([0-5]\d):([0-5]\d)([zZ]|([+-](0\d|1[0-4]):[0-5]\d))/
21
+ end
22
+
23
+ def self.is_valid?(strtime, options={})
24
+ options.reverse_merge!(:blank_is_valid => true)
25
+
26
+ if strtime.blank?
27
+ return options[:blank_is_valid]
28
+ else
29
+ begin
30
+ return (Time.zone.parse(strtime) ? true : false)
31
+ rescue
32
+ return false
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.parse_in_utc(date_string)
38
+ last_time_zone = Time.zone.name
39
+
40
+ Time.zone = 'UTC'
41
+ utc_time = Time.zone.parse(date_string)
42
+ Time.zone = last_time_zone
43
+
44
+ return utc_time
45
+ end
46
+
47
+ def self.parse_timestamp(raw_timestamp, options={})
48
+ options.reverse_merge!(:time_only => false)
49
+
50
+ timestamp = Time.zone.parse(raw_timestamp)
51
+
52
+ return options[:time_only] ? timestamp.strftime("%H:%M:%S") : timestamp
53
+ end
54
+
55
+ def self.round(datetime, seconds=60)
56
+ return Time.at((datetime.to_f / seconds).round * seconds)
57
+ end
58
+
59
+ def self.seconds_to_unit(seconds, options={})
60
+ options.reverse_merge!(:force => nil, :format => :time, :show_unit => true)
61
+ seconds = seconds.to_f
62
+
63
+ result = seconds
64
+ time_table = {'year' => 31536000, 'day' => 86400, 'hour' => 3600, 'minute' => 60, 'second' => 1}
65
+ unit_selected = 'second'
66
+
67
+ time_table.each do |unit, value|
68
+ if (seconds >= value and not options[:force]) or (unit == options[:force])
69
+ unit_selected = unit
70
+ result = seconds / value
71
+ break
72
+ end
73
+ end
74
+
75
+ formatted_result = (options[:format] == :time) ? format_in_time(result) : sprintf("%0.02f", result)
76
+
77
+ if options[:show_unit]
78
+ return formatted_result + " " + I18n.t("datetime.short.#{unit_selected}")
79
+ else
80
+ return formatted_result.to_i
81
+ end
82
+ end
83
+
84
+ def self.start_timer
85
+ @timer = Time.now
86
+ end
87
+
88
+ def self.stop_timer
89
+ Time.now - @timer
90
+ end
91
+
92
+ def self.time_format
93
+ {
94
+ :basic => "%Y%m%dT%H%M%S",
95
+ :shortdate => "%Y-%m-%d",
96
+ :shorttime => "%H:%M:%S",
97
+ :shortdatetime => "%Y-%m-%dT%H:%M:%S"
98
+ }
99
+ end
100
+
101
+ def self.time_process_in_seconds(time)
102
+ time = time.split('.')[0]
103
+ new_time = time.split(':')
104
+
105
+ return (new_time[new_time.length-2].to_i * 60).to_i + new_time.last.to_i
106
+ end
107
+
108
+ def self.datetime_standard
109
+ [ :iso8601 ]
110
+ end
111
+
112
+ def self.timestamp(date=nil)
113
+ date = Time.now.getgm unless date
114
+
115
+ return date.strftime("%Y%m%d%H%M%S")
116
+ end
117
+
118
+ def self.timestamp_db(date=nil)
119
+ date = Time.now unless date
120
+
121
+ return date.strftime("%Y-%m-%d %H:%M:%S")
122
+ end
123
+
124
+ def self.timezone(date, options={})
125
+ options.reverse_merge!(:standard => :iso8601)
126
+ send("timezone_" + options[:standard].to_s, date) if datetime_standard.include?(options[:standard])
127
+ end
128
+
129
+ def self.timezone_iso8601(date)
130
+ if date.utc?
131
+ 'Z'
132
+ else
133
+ off = date.utc_offset
134
+ sign = off < 0 ? '-' : '+'
135
+ sprintf('%s%02d:%02d', sign, *(off.abs / 60).divmod(60))
136
+ end
137
+ end
138
+
139
+ def self.to_iso8601(date, options={})
140
+ options.reverse_merge!(:format => :basic, :utc => false, :timezone => false)
141
+
142
+ date = date.utc if options[:utc]
143
+
144
+ if time_format[options[:format]]
145
+ iso8601_date = date.strftime(time_format[options[:format]])
146
+ iso8601_date = iso8601_date + timezone(date, :standard => :iso8601) if options[:timezone] and options[:format] != :shortdate
147
+ else
148
+ iso8601_date = date.iso8601
149
+ end
150
+
151
+ return iso8601_date
152
+ end
153
+ end
@@ -0,0 +1,111 @@
1
+ require 'active_support/time'
2
+ require 'active_support/core_ext/object'
3
+ require 'tool_pouch/trail'
4
+
5
+ module ToolPouch::Locksmith
6
+ include ToolPouch
7
+
8
+ def self.build_digest_request(url, password, lifespan = 2.minutes)
9
+ if url.blank? || password.blank?
10
+ nil
11
+ else
12
+ nonce = Babel.random_string(15)
13
+ timestamp = (Time.now + lifespan).utc.strftime('%Y-%m-%dT%H:%M:%SZ')
14
+ escaped_timestamp = CGI.escape(timestamp)
15
+
16
+ uri = Addressable::URI.parse url
17
+ params = uri.query_values || {}
18
+ params.merge!({'timestamp' => timestamp, 'nonce' => nonce})
19
+
20
+ digested_url = digest_uri(uri, params).to_s
21
+ params.merge!({
22
+ token: hexdigest([password, nonce, escaped_timestamp].join, digested_url)
23
+ })
24
+ uri.query_values = params
25
+ uri.to_s
26
+ end
27
+ end
28
+
29
+ def self.check_digest_request(url, password)
30
+ if url.blank? || password.blank?
31
+ false
32
+ else
33
+ uri = Addressable::URI.parse url
34
+ params = uri.query_values || {}
35
+
36
+ unescaped_timestamp = CGI.unescape(params['timestamp'].to_s)
37
+ escaped_timestamp = CGI.escape(unescaped_timestamp)
38
+ token_key = [password, params['nonce'].to_s, escaped_timestamp].join
39
+
40
+ digested_url = digest_uri(uri, params).to_s
41
+
42
+ expire_at = Krono.parse_in_utc(unescaped_timestamp)
43
+ compare_hexdigest(token_key, digested_url, params['token']) && expire_at.future?
44
+ end
45
+ end
46
+
47
+ # Harmonize generated URL and request URL so they can be compared.
48
+ # Digest params are added to the query
49
+ # Scheme is ignored (harmonized to http)
50
+ # Note that the gem Adressable do order the query string by key values
51
+ def self.digest_uri(original_uri, params = nil)
52
+ uri = original_uri.dup
53
+ if params
54
+ p = params.dup
55
+ p.delete 'token'
56
+ uri.query_values = p
57
+ end
58
+ uri.scheme = nil
59
+ uri.port = nil
60
+ uri.to_s
61
+ end
62
+
63
+ def self.compare_hexdigest(password, value, target_key)
64
+ password and value and OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), password, value.to_s) == target_key
65
+ end
66
+
67
+ def self.decrypt(value, password)
68
+ decrypted_value = nil
69
+
70
+ if value and password
71
+ c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
72
+ c.decrypt
73
+ c.key = Digest::SHA1.hexdigest(password)
74
+ begin
75
+ decrypted_value = c.update(Base64.decode64(value))
76
+ decrypted_value << c.final
77
+ rescue
78
+ decrypted_value = nil
79
+ end
80
+ end
81
+
82
+ return decrypted_value
83
+ end
84
+
85
+ def self.encrypt(value, password)
86
+ encrypt = nil
87
+
88
+ if value and password
89
+ begin
90
+ c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
91
+ c.encrypt
92
+ c.key = Digest::SHA1.hexdigest(password)
93
+ e = c.update(value)
94
+ e << c.final
95
+ encrypt = Base64.encode64(e).strip
96
+ rescue
97
+ encrypt = nil
98
+ end
99
+ end
100
+
101
+ return encrypt
102
+ end
103
+
104
+ def self.generate_file_checksum(file)
105
+ File.exist?(file) ? Digest::MD5.hexdigest(File.read(file)) : nil
106
+ end
107
+
108
+ def self.hexdigest(password, value)
109
+ (password and value) ? OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), password, value) : nil
110
+ end
111
+ end