tool_pouch 0.0.34
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +5 -0
- data/README +22 -0
- data/Rakefile +3 -0
- data/data/iso639.yml +412 -0
- data/lib/tool_pouch.rb +43 -0
- data/lib/tool_pouch/babel.rb +225 -0
- data/lib/tool_pouch/krono.rb +153 -0
- data/lib/tool_pouch/locksmith.rb +111 -0
- data/lib/tool_pouch/mathema.rb +27 -0
- data/lib/tool_pouch/trail.rb +279 -0
- data/lib/tool_pouch/transistor.rb +98 -0
- data/lib/tool_pouch/version.rb +3 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tool_pouch/krono_spec.rb +12 -0
- data/spec/tool_pouch/locksmith/digest_spec.rb +105 -0
- metadata +88 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module ToolPouch::Mathema
|
2
|
+
def self.tax_for_amount(amount, region, options={})
|
3
|
+
options.reverse_merge!(:time => Time.now)
|
4
|
+
|
5
|
+
case region
|
6
|
+
when 'quebec' then get_quebec_taxes(amount, options)
|
7
|
+
else []
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.get_quebec_taxes(amount, options={})
|
12
|
+
options.reverse_merge!(:time => Time.now)
|
13
|
+
|
14
|
+
gst = { :id => 'gst', :nature => 'tax', :rate => 0.05 }
|
15
|
+
qst = { :id => 'qst', :nature => 'tax' }
|
16
|
+
|
17
|
+
qst[:rate] = case
|
18
|
+
when options[:time] < Time.parse('2012-01-01') then 0.085
|
19
|
+
else 0.095
|
20
|
+
end
|
21
|
+
|
22
|
+
gst[:amount] = (amount * gst[:rate]).round
|
23
|
+
qst[:amount] = ((amount + gst[:amount]) * qst[:rate]).round
|
24
|
+
|
25
|
+
return [gst, qst]
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
|
3
|
+
module ToolPouch::Trail
|
4
|
+
def self.build_querystring(form_data)
|
5
|
+
query_string = ''
|
6
|
+
if form_data
|
7
|
+
params = []
|
8
|
+
if not form_data.empty?
|
9
|
+
form_data.each do |d|
|
10
|
+
if d[1].is_a?(Hash)
|
11
|
+
d[1].each do |key, value|
|
12
|
+
params << "#{d[0]}[#{key}]=#{CGI.escape(CGI.unescape(value.to_s))}"
|
13
|
+
end
|
14
|
+
else
|
15
|
+
params << "#{d[0]}=#{CGI.escape(CGI.unescape(d[1].to_s))}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
query_string = params.sort.join('&')
|
20
|
+
end
|
21
|
+
return query_string
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.directories(path)
|
25
|
+
directory = ''
|
26
|
+
if path and path.class == String
|
27
|
+
last_slash_index = path.rindex('/')
|
28
|
+
|
29
|
+
if last_slash_index.to_i > 0
|
30
|
+
directory = path[0, last_slash_index]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
return directory
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.extension(path)
|
38
|
+
file_extension = ''
|
39
|
+
if path
|
40
|
+
file_extension_with_dot = File.extname(path)
|
41
|
+
if not file_extension_with_dot.empty?
|
42
|
+
file_extension = file_extension_with_dot[1, file_extension_with_dot.length]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
return file_extension
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.filename_without_extension(path, options={})
|
49
|
+
options.reverse_merge!(:full_path => true)
|
50
|
+
|
51
|
+
filename = options[:full_path] ? path : filename_without_path(path)
|
52
|
+
|
53
|
+
return (filename and filename.rindex('.')) ? filename[0, filename.rindex('.')] : filename
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.filename_without_path(path)
|
57
|
+
filename_without_path = path
|
58
|
+
if path
|
59
|
+
#Handle properly path without filename
|
60
|
+
if trailing_slash?(path)
|
61
|
+
filename_without_path = ''
|
62
|
+
else
|
63
|
+
filename_without_path = File.basename(path)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
return filename_without_path
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.get_domain(url)
|
70
|
+
domain = nil
|
71
|
+
if url
|
72
|
+
# add protocol so it becomes a valid URI
|
73
|
+
url = ('http://' + url) unless url =~ /^https?:\/\//
|
74
|
+
|
75
|
+
# remove %'s, which we don't need and could cause an URL to become invalid
|
76
|
+
url.gsub!('%', '')
|
77
|
+
|
78
|
+
begin
|
79
|
+
# get the host part, and remove the www. if present
|
80
|
+
domain = Addressable::URI.parse(url).host.gsub(/^www\./i, '')
|
81
|
+
rescue
|
82
|
+
""
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return domain
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.get_all_files_with_extension(path, extensions=[])
|
89
|
+
extensions = [extensions] unless extensions.is_a? Array
|
90
|
+
extensions.collect!{|x| ".#{x}"}
|
91
|
+
|
92
|
+
files = []
|
93
|
+
Find.find(path) { |f| files << f if File.file?(f) and extensions.include? File.extname(f) }
|
94
|
+
|
95
|
+
return files
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.get_file(root, filenames)
|
99
|
+
files = get_files(root, filenames)
|
100
|
+
|
101
|
+
return files.empty? ? nil : files.first
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.get_files(root, filenames, options={})
|
105
|
+
options.reverse_merge!(:perfect_match => true)
|
106
|
+
|
107
|
+
filenames = [filenames] unless filenames.is_a? Array
|
108
|
+
root = slice_trailing_slash(root)
|
109
|
+
files = []
|
110
|
+
|
111
|
+
Find.find(root) do |f|
|
112
|
+
if options[:perfect_match]
|
113
|
+
files << f.gsub(root + '/', '') if File.file?(f) and filenames.include?(filename_without_path(f))
|
114
|
+
else
|
115
|
+
found = false
|
116
|
+
filename_only = filename_without_path(f)
|
117
|
+
filenames.each{ |m| found = true if filename_only.match(/#{m}/) }
|
118
|
+
files << f.gsub(root + '/', '') if File.file?(f) and found
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
return files
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.get_first_file_with_extension(path, extensions=[])
|
126
|
+
return get_all_files_with_extension(path, extensions).first
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.get_first_match(path, value)
|
130
|
+
File.open(path) do |io|
|
131
|
+
io.each do |line|
|
132
|
+
line.chomp!
|
133
|
+
return line if line.include? value
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
return nil
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.get_querystring(url)
|
141
|
+
raw = URI.split(url)[7]
|
142
|
+
|
143
|
+
qs = {}
|
144
|
+
|
145
|
+
if raw
|
146
|
+
raw.split('&').each do |full_param|
|
147
|
+
key, value = full_param.split('=')
|
148
|
+
qs[key] = value
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
return qs
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.get_section(url, options={})
|
156
|
+
section = nil
|
157
|
+
if url
|
158
|
+
if options[:base_path]
|
159
|
+
section = url.gsub(options[:base_path] + '/', '').split('/')[0]
|
160
|
+
else
|
161
|
+
section = url.split('/')[-1, 1].to_s
|
162
|
+
end
|
163
|
+
|
164
|
+
# Remove format if it was specified
|
165
|
+
section = File.basename(section, '.*')
|
166
|
+
end
|
167
|
+
|
168
|
+
return section
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.is_file_with_extension?(path)
|
172
|
+
file_with_extension = false
|
173
|
+
if path
|
174
|
+
extension = File.extname(path)
|
175
|
+
if extension and not extension.empty?
|
176
|
+
file_with_extension = true
|
177
|
+
end
|
178
|
+
end
|
179
|
+
return file_with_extension
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.is_url?(path)
|
183
|
+
return (path.to_s.include?('http://') or path.to_s.include?('https://')) ? true : false
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.last_directory(path)
|
187
|
+
path += 'dummystring' if trailing_slash?(path)
|
188
|
+
|
189
|
+
return path.split('/')[-2, 1]
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.open_and_merge(directories, options={})
|
193
|
+
options.reverse_merge!(:extensions => '')
|
194
|
+
|
195
|
+
directories = [directories] if not directories.is_a? Array
|
196
|
+
|
197
|
+
case options[:extensions]
|
198
|
+
when 'yml' then content = HashTree.new
|
199
|
+
else content = []
|
200
|
+
end
|
201
|
+
|
202
|
+
directories.each do |directory|
|
203
|
+
Find.find(directory) do |p|
|
204
|
+
next unless File.file?(p)
|
205
|
+
|
206
|
+
case options[:extensions]
|
207
|
+
when 'yml' then content.merge(yml_content(p))
|
208
|
+
when 'xml' then content << File.open(p, "r").readlines.join
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
case options[:extensions]
|
214
|
+
when 'yml' then content.get
|
215
|
+
else return content.join("\n")
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.order_querystring_alpha(url)
|
220
|
+
ordered_url = url
|
221
|
+
if url and not url.strip.empty?
|
222
|
+
qs = URI.split(url)[7]
|
223
|
+
|
224
|
+
if qs
|
225
|
+
ordered_url = url.split('?')[0] + '?'
|
226
|
+
parameters = qs.split('&')
|
227
|
+
escaped_parameters = []
|
228
|
+
# Escape each parameter to ensure coherence with build_query_string
|
229
|
+
for parameter in parameters
|
230
|
+
name_value = parameter.split('=')
|
231
|
+
if name_value.size == 2
|
232
|
+
escaped_value = CGI.escape(CGI.unescape(name_value[1]))
|
233
|
+
escaped_parameters << "#{name_value[0]}=#{escaped_value}"
|
234
|
+
else
|
235
|
+
escaped_parameters << "#{name_value[0]}"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
ordered_url << escaped_parameters.sort.join('&')
|
239
|
+
return ordered_url
|
240
|
+
end
|
241
|
+
end
|
242
|
+
return url
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.partial_exists?(partial_short_path)
|
246
|
+
short_path = partial_short_path.split('/')
|
247
|
+
short_path[short_path.length-1] = '_' + short_path.last
|
248
|
+
return File.exists?(RAILS_ROOT + '/app/views' + short_path.join('/') + '.html.erb')
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.refresh_file_content(path, content)
|
252
|
+
FileUtils.mkdir_p directories(path)
|
253
|
+
FileUtils.rm(path) if File.exists?(path)
|
254
|
+
File.open(path, 'w') {|f| f.write(content) }
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.slice_trailing_slash(path)
|
258
|
+
return (trailing_slash?(path)) ? path.chop : path
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.strip_scheme(uri)
|
262
|
+
uri.sub(/^#{Addressable::URI.parse(URI.encode(uri)).scheme}:\/\//,"")
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.trailing_slash?(path)
|
266
|
+
trailing_slash = false
|
267
|
+
if path and path.length > 0
|
268
|
+
if path.slice(-1,1) == '/'
|
269
|
+
trailing_slash = true
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
return trailing_slash
|
274
|
+
end
|
275
|
+
|
276
|
+
def self.yml_content(file)
|
277
|
+
return YAML.load_file(file)
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "addressable/uri"
|
2
|
+
|
3
|
+
#*************************************************************************************
|
4
|
+
# TOCOMMENT
|
5
|
+
#*************************************************************************************
|
6
|
+
module ToolPouch::Transistor
|
7
|
+
def self.get(raw_url, options={})
|
8
|
+
raw_url += '?' + ToolPouch::Trail.build_querystring(options.delete(:params)) if options[:params]
|
9
|
+
|
10
|
+
return send_request(:get, raw_url, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_head(url)
|
14
|
+
begin
|
15
|
+
uri = parse_uri(url)
|
16
|
+
res = Net::HTTP.start(uri.host, uri.port)
|
17
|
+
rescue
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
|
21
|
+
return (res and res.head(url) ? res.head(url) : false)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse_uri(uri)
|
25
|
+
parsed_uri = Addressable::URI.parse(uri)
|
26
|
+
parsed_uri.port = 443 if parsed_uri.scheme == 'https'
|
27
|
+
|
28
|
+
return parsed_uri
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.post(raw_url, options={})
|
32
|
+
send_request(:post, raw_url, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.send_request(method, raw_url, options={})
|
36
|
+
options.reverse_merge!(body: nil, content_type: nil, login: nil, params: {}, password: nil, timeout: nil)
|
37
|
+
|
38
|
+
url = parse_uri raw_url
|
39
|
+
|
40
|
+
request = "Net::HTTP::#{method.to_s.camelize}".constantize.new(url.request_uri)
|
41
|
+
request.basic_auth(options[:login], options[:password]) if options[:login]
|
42
|
+
request.body = options[:body] if options[:body]
|
43
|
+
request.content_type = options[:content_type] if options[:content_type]
|
44
|
+
request.set_form_data(options[:params]) if not options[:params].empty?
|
45
|
+
|
46
|
+
return start_net_http(url, request, timeout: options[:timeout])
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.start_net_http(url, request, options={})
|
50
|
+
http = Net::HTTP.new(url.host, url.port)
|
51
|
+
|
52
|
+
if http
|
53
|
+
http.use_ssl = (url.scheme == 'https')
|
54
|
+
http.read_timeout = options[:timeout] if options[:timeout]
|
55
|
+
|
56
|
+
return http.start { |r| r.request(request) }
|
57
|
+
end
|
58
|
+
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
|
62
|
+
#*************************************************************************************
|
63
|
+
# Digest communication module
|
64
|
+
#*************************************************************************************
|
65
|
+
module Digest
|
66
|
+
def self.delete(url, password, form_data={})
|
67
|
+
form_data['_method'] = 'delete'
|
68
|
+
|
69
|
+
return post_request_digest(url, password, form_data)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.get(url, password)
|
73
|
+
url_digest = ToolPouch::Locksmith.build_digest_request(url, password)
|
74
|
+
|
75
|
+
if url_digest
|
76
|
+
uri = parse_uri url_digest
|
77
|
+
|
78
|
+
return Net::HTTP.get_response(uri) if uri
|
79
|
+
end
|
80
|
+
|
81
|
+
return nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.post(url, password, form_data={})
|
85
|
+
url << ('?' + ToolPouch::Trail.build_querystring(form_data)) if not form_data.empty?
|
86
|
+
|
87
|
+
url_digest = ToolPouch::Locksmith.build_digest_request(url, password)
|
88
|
+
|
89
|
+
return Net::HTTP.post_form(parse_uri(url_digest), ToolPouch::Trail.get_querystring(url_digest))
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.put(url, password, form_data={})
|
93
|
+
form_data['_method'] = 'put'
|
94
|
+
|
95
|
+
return post(url, password, form_data)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ToolPouch::Krono do
|
4
|
+
describe '#is_iso8601?' do
|
5
|
+
it "recognizes valid date" do
|
6
|
+
ToolPouch::Krono.is_iso8601?('2000-01-01T00:00:00-05:00').should be_true
|
7
|
+
ToolPouch::Krono.is_iso8601?('1900-01-01T00:00:00-05:00').should be_true
|
8
|
+
ToolPouch::Krono.is_iso8601?('1800-01-01T00:00:00-05:00').should be_true
|
9
|
+
ToolPouch::Krono.is_iso8601?('1700-01-01T00:00:00-05:00').should be_true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require 'addressable/uri'
|
3
|
+
|
4
|
+
describe ToolPouch::Locksmith do
|
5
|
+
let (:password) {'ThiZizAS3cr3T'}
|
6
|
+
|
7
|
+
describe 'digesting an URL' do
|
8
|
+
it "works without an URL authority" do
|
9
|
+
url = "/home"
|
10
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
11
|
+
|
12
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "works without an URL query" do
|
16
|
+
url = "http://example.org"
|
17
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
18
|
+
|
19
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "works regardless of the scheme use (http vs https)" do
|
23
|
+
url = "https://example.org"
|
24
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
25
|
+
digested_url.gsub! /https:\/\//, 'http://'
|
26
|
+
|
27
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allows specifying a lifespan" do
|
31
|
+
url = "http://example.org"
|
32
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password, 30.seconds)
|
33
|
+
|
34
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "works with an URL query" do
|
38
|
+
url = "http://example.org?key=value"
|
39
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
40
|
+
|
41
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it "orders the query parameters" do
|
45
|
+
url = "http://example.org?b=1&c=1&a=1"
|
46
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
47
|
+
|
48
|
+
digested_url.should match /http:\/\/example.org\?a=1&b=1&c=1/
|
49
|
+
end
|
50
|
+
|
51
|
+
it "works regardless of the orders of URL query parameters" do
|
52
|
+
url = "http://example.org?b=1&c=1&a=1"
|
53
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
54
|
+
digested_url_with_different_orders = digested_url.gsub /a=1&b=1&c=1/, 'c=1&a=1&b=1'
|
55
|
+
|
56
|
+
ToolPouch::Locksmith.check_digest_request(digested_url_with_different_orders, password).should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it "works regardless of using percent encoded characters or real characters in the query" do
|
60
|
+
url = "http://example.org?key=%C3%97"
|
61
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
62
|
+
unescaped_digested_url = digested_url.gsub /%C3%97/, '×'
|
63
|
+
ToolPouch::Locksmith.check_digest_request(unescaped_digested_url, password).should be_true
|
64
|
+
|
65
|
+
url = "http://example.org?key=×"
|
66
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
67
|
+
escaped_digested_url = digested_url.gsub /×/, '%C3%97'
|
68
|
+
ToolPouch::Locksmith.check_digest_request(escaped_digested_url, password).should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does not allow token hacking" do
|
72
|
+
url = "http://example.org?key=value"
|
73
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
74
|
+
hacked_url = digested_url.gsub /token=/, 'token=X'
|
75
|
+
|
76
|
+
ToolPouch::Locksmith.check_digest_request(hacked_url, password).should be_false
|
77
|
+
end
|
78
|
+
|
79
|
+
it "does not allow an expired URL" do
|
80
|
+
url = "http://example.org"
|
81
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password, 2.seconds)
|
82
|
+
sleep 3
|
83
|
+
|
84
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_false
|
85
|
+
end
|
86
|
+
|
87
|
+
it "works regardless of the port used" do
|
88
|
+
url = "https://example.org:443"
|
89
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
90
|
+
digested_url.gsub! /:443/, ''
|
91
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_true
|
92
|
+
|
93
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
94
|
+
digested_url.gsub! /:443/, ':80'
|
95
|
+
ToolPouch::Locksmith.check_digest_request(digested_url, password).should be_true
|
96
|
+
end
|
97
|
+
|
98
|
+
it "keeps the port in the generated URL" do
|
99
|
+
url = "https://example.org:443"
|
100
|
+
digested_url = ToolPouch::Locksmith.build_digest_request(url, password)
|
101
|
+
digested_url.should match /:443/
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|