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.
- 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
|