tzispa_helpers 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +16 -0
- data/README.md +3 -0
- data/lib/tzispa/helpers/crawler.rb +60 -0
- data/lib/tzispa/helpers/html.rb +149 -0
- data/lib/tzispa/helpers/mail.rb +68 -0
- data/lib/tzispa/helpers/mime.rb +23 -0
- data/lib/tzispa/helpers/pattern.rb +56 -0
- data/lib/tzispa/helpers/request.rb +64 -0
- data/lib/tzispa/helpers/response.rb +197 -0
- data/lib/tzispa/helpers/security.rb +81 -0
- data/lib/tzispa/helpers/text.rb +140 -0
- data/lib/tzispa/helpers/version.rb +11 -0
- data/lib/tzispa/helpers.rb +16 -0
- data/lib/tzispa_helpers.rb +7 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 87b4a937a075322e0d9ad944994d1a0562fbb390
|
4
|
+
data.tar.gz: 8ebb31c207fd19d5322097b6b11225aa52b22e80
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2464f13b0caa59b90b74a3b0d30a68b784c420b2fba0f17b89ea7e9b4a33e8a4e5f50b1f54bf8dfe9e4c5f68a2190143545ce741f00fb7f80d8be5fd973179d7
|
7
|
+
data.tar.gz: b3af1a958c90c2881b842277b708c5d5490bb8f4704699ce002f1e64cb7f1105e57669702fbd737c35c80c5afa59a01249261448df7fb9c64b94afc1e7a0215b
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Tzispa Helpers
|
2
|
+
|
3
|
+
## v0.1.3
|
4
|
+
- Added mime module, code moved partially from response
|
5
|
+
- Added 2 crawler functions: crawler_to_markdown, crawler_table_to_dl
|
6
|
+
|
7
|
+
## v0.1.2
|
8
|
+
- Added amount formatter in text helper with i18n support
|
9
|
+
|
10
|
+
## v0.1.1
|
11
|
+
- Added exception rescue in Mail.send_smtp_mail
|
12
|
+
- Added pattern Helper
|
13
|
+
- Added web crawler helper
|
14
|
+
|
15
|
+
## v0.1.0
|
16
|
+
- Initial release: code split from tzispa main gem
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'htmlentities'
|
6
|
+
require 'reverse_markdown'
|
7
|
+
require 'unicode_utils'
|
8
|
+
require 'redcarpet'
|
9
|
+
|
10
|
+
module Tzispa
|
11
|
+
module Helpers
|
12
|
+
module Crawler
|
13
|
+
|
14
|
+
|
15
|
+
def crawler_save_file(url, dest_file)
|
16
|
+
File.open("#{dest_file}", 'wb') do |fo|
|
17
|
+
fo.write open(url).read
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def crawler_to_markdown(source)
|
22
|
+
begin
|
23
|
+
source = source.read if source.respond_to? :read
|
24
|
+
htmee = HTMLEntities.new
|
25
|
+
ReverseMarkdown.convert(htmee.decode(source).strip, unknown_tags: :bypass)
|
26
|
+
rescue Encoding::UndefinedConversionError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def crawler_table_to_dl(source, table_path, columns=2, excluded_terms=[])
|
31
|
+
String.new.tap { |content|
|
32
|
+
dt, dd = Array.new, Array.new
|
33
|
+
htmee = HTMLEntities.new
|
34
|
+
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new)
|
35
|
+
Nokogiri::HTML(source)&.xpath(table_path).collect { |row|
|
36
|
+
dterm = htmee.decode(row.at_xpath('td[1]')&.content).strip
|
37
|
+
if dterm.length > 0 && !excluded_terms.include?(UnicodeUtils.downcase dterm)
|
38
|
+
dt << dterm
|
39
|
+
dd << (2..columns).map { |i|
|
40
|
+
ReverseMarkdown.convert(htmee.decode(row.at_xpath("td[#{i}]")&.children&.to_s || row.at_xpath("td[#{i}]")&.to_s).strip, unknown_tags: :bypass)
|
41
|
+
}.join('\n')
|
42
|
+
end
|
43
|
+
}
|
44
|
+
if dt.length > 0
|
45
|
+
content << '<dl>'
|
46
|
+
dt.zip(dd).sort.each do |idt,idd|
|
47
|
+
if idt.length > 0 && !excluded_terms.include?(UnicodeUtils.downcase idt)
|
48
|
+
content << "<dt>#{idt}</dt>"
|
49
|
+
content << "<dd>#{markdown.render idd}</dd>"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
content << "</dl>"
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi/util'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Tzispa
|
7
|
+
module Helpers
|
8
|
+
module Html
|
9
|
+
|
10
|
+
def html_checked(value)
|
11
|
+
value ? ' checked="checked"' : String.new.freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
def html_selected(value)
|
15
|
+
value ? ' selected="selected"' : String.new.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
def html_escape(str)
|
19
|
+
CGI::escapeHTML str
|
20
|
+
end
|
21
|
+
|
22
|
+
def html_unescape(str)
|
23
|
+
CGI::unescapeHTML str
|
24
|
+
end
|
25
|
+
|
26
|
+
def html_urlencode(str)
|
27
|
+
URI.escape str
|
28
|
+
end
|
29
|
+
|
30
|
+
def html_urldecode(str)
|
31
|
+
URI.unescape str
|
32
|
+
end
|
33
|
+
|
34
|
+
def html_strip(source)
|
35
|
+
source.gsub(/<\/?[^>]*>/, '')
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# $Id: sanitize.rb 3 2005-04-05 12:51:14Z dwight $
|
41
|
+
#
|
42
|
+
# Copyright (c) 2005 Dwight Shih
|
43
|
+
# A derived work of the Perl version:
|
44
|
+
# Copyright (c) 2002 Brad Choate, bradchoate.com
|
45
|
+
#
|
46
|
+
# Permission is hereby granted, free of charge, to
|
47
|
+
# any person obtaining a copy of this software and
|
48
|
+
# associated documentation files (the "Software"), to
|
49
|
+
# deal in the Software without restriction, including
|
50
|
+
# without limitation the rights to use, copy, modify,
|
51
|
+
# merge, publish, distribute, sublicense, and/or sell
|
52
|
+
# copies of the Software, and to permit persons to
|
53
|
+
# whom the Software is furnished to do so, subject to
|
54
|
+
# the following conditions:
|
55
|
+
#
|
56
|
+
# The above copyright notice and this permission
|
57
|
+
# notice shall be included in all copies or
|
58
|
+
# substantial portions of the Software.
|
59
|
+
#
|
60
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
|
61
|
+
# OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
62
|
+
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
63
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND
|
64
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
65
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
66
|
+
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
67
|
+
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
68
|
+
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
69
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
70
|
+
#
|
71
|
+
def html_sanitize( html, okTags='a href, b, br, i, p' )
|
72
|
+
# no closing tag necessary for these
|
73
|
+
soloTags = ["br","hr"]
|
74
|
+
|
75
|
+
# Build hash of allowed tags with allowed attributes
|
76
|
+
tags = okTags.downcase().split(',').collect!{ |s| s.split(' ') }
|
77
|
+
allowed = Hash.new
|
78
|
+
tags.each do |s|
|
79
|
+
key = s.shift
|
80
|
+
allowed[key] = s
|
81
|
+
end
|
82
|
+
|
83
|
+
# Analyze all <> elements
|
84
|
+
stack = Array.new
|
85
|
+
result = html.gsub( /(<.*?>)/m ) do | element |
|
86
|
+
if element =~ /\A<\/(\w+)/ then
|
87
|
+
# </tag>
|
88
|
+
tag = $1.downcase
|
89
|
+
if allowed.include?(tag) && stack.include?(tag) then
|
90
|
+
# If allowed and on the stack
|
91
|
+
# Then pop down the stack
|
92
|
+
top = stack.pop
|
93
|
+
out = "</#{top}>"
|
94
|
+
until top == tag do
|
95
|
+
top = stack.pop
|
96
|
+
out << "</#{top}>"
|
97
|
+
end
|
98
|
+
out
|
99
|
+
end
|
100
|
+
elsif element =~ /\A<(\w+)\s*\/>/
|
101
|
+
# <tag />
|
102
|
+
tag = $1.downcase
|
103
|
+
if allowed.include?(tag) then
|
104
|
+
"<#{tag} />"
|
105
|
+
end
|
106
|
+
elsif element =~ /\A<(\w+)/ then
|
107
|
+
# <tag ...>
|
108
|
+
tag = $1.downcase
|
109
|
+
if allowed.include?(tag) then
|
110
|
+
if ! soloTags.include?(tag) then
|
111
|
+
stack.push(tag)
|
112
|
+
end
|
113
|
+
if allowed[tag].length == 0 then
|
114
|
+
# no allowed attributes
|
115
|
+
"<#{tag}>"
|
116
|
+
else
|
117
|
+
# allowed attributes?
|
118
|
+
out = "<#{tag}"
|
119
|
+
while ( $' =~ /(\w+)=("[^"]+")/ )
|
120
|
+
attr = $1.downcase
|
121
|
+
valu = $2
|
122
|
+
if allowed[tag].include?(attr) then
|
123
|
+
out << " #{attr}=#{valu}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
out << ">"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# eat up unmatched leading >
|
133
|
+
while result.sub!(/\A([^<]*)>/m) { $1 } do end
|
134
|
+
|
135
|
+
# eat up unmatched trailing <
|
136
|
+
while result.sub!(/<([^>]*)\Z/m) { $1 } do end
|
137
|
+
|
138
|
+
# clean up the stack
|
139
|
+
if stack.length > 0 then
|
140
|
+
result << "</#{stack.reverse.join('></')}>"
|
141
|
+
end
|
142
|
+
|
143
|
+
result
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mail'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Helpers
|
7
|
+
module Mail
|
8
|
+
|
9
|
+
def send_smtp_mail(from:, to:, subject:, body:, config:, cc: nil, html: false)
|
10
|
+
begin
|
11
|
+
smtp_configuration config
|
12
|
+
mail = ::Mail.new
|
13
|
+
mail.from = from
|
14
|
+
if !to.empty?
|
15
|
+
to_addrs = to.split(';')
|
16
|
+
mail.to = to_addrs.pop
|
17
|
+
to_addrs.each { |email|
|
18
|
+
mail.to << email
|
19
|
+
}
|
20
|
+
if cc
|
21
|
+
cc_addrs = cc.split(';')
|
22
|
+
mail.cc = cc_addrs.pop
|
23
|
+
cc_addrs.each { |email|
|
24
|
+
mail.cc << email
|
25
|
+
}
|
26
|
+
end
|
27
|
+
mail.subject = subject
|
28
|
+
if html
|
29
|
+
mail.html_part do
|
30
|
+
content_type 'text/html; charset=UTF-8'
|
31
|
+
body = body
|
32
|
+
end
|
33
|
+
else
|
34
|
+
mail.body = body
|
35
|
+
end
|
36
|
+
mail.deliver
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
rescue
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def smtp_configuration(config)
|
48
|
+
if config.smtp_auth
|
49
|
+
::Mail.defaults do
|
50
|
+
delivery_method :smtp, address: config.host, domain: config.domain,
|
51
|
+
port: config.port, authentication: config.authentication,
|
52
|
+
openssl_verify_mode: config.openssl_verify, enable_starttls_auto: config.starttls_auto,
|
53
|
+
user_name: config.user_name, password: config.password
|
54
|
+
end
|
55
|
+
else
|
56
|
+
::Mail.defaults do
|
57
|
+
delivery_method :smtp, address: config.host, domain: config.domain,
|
58
|
+
port: config.port, openssl_verify_mode: config.openssl_verify,
|
59
|
+
enable_starttls_auto: config.starttls_auto
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/mime'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Helpers
|
7
|
+
module Mime
|
8
|
+
|
9
|
+
def mime_type(type, value = nil)
|
10
|
+
return type if type.nil?
|
11
|
+
return type.to_s if type.to_s.include?('/')
|
12
|
+
type = ".#{type}" unless type.to_s[0] == ?.
|
13
|
+
return Rack::Mime.mime_type(type, nil) unless value
|
14
|
+
Rack::Mime::MIME_TYPES[type] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def mime_extension(type)
|
18
|
+
Rack::Mime::MIME_TYPES.invert[type]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'html'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Helpers
|
7
|
+
module Pattern
|
8
|
+
|
9
|
+
include Tzispa::Helpers::Html
|
10
|
+
|
11
|
+
def pattern_select_hours(selected=nil)
|
12
|
+
selected &&= selected.to_i
|
13
|
+
Proc.new {
|
14
|
+
(0..23).map { |hour|
|
15
|
+
loop_item(
|
16
|
+
value: hour,
|
17
|
+
text: hour.to_s.rjust(2, '0'),
|
18
|
+
selected: html_selected( selected == hour )
|
19
|
+
)
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def pattern_select_minutes(selected=nil)
|
25
|
+
selected &&= selected.to_i
|
26
|
+
Proc.new {
|
27
|
+
(0..59).map { |minute|
|
28
|
+
loop_item(
|
29
|
+
value: minute,
|
30
|
+
text: minute.to_s.rjust(2, '0'),
|
31
|
+
selected: html_selected( selected == minute )
|
32
|
+
)
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def pattern_select_year(first, last, selected=nil, reverse=true)
|
38
|
+
selected &&= selected.to_i
|
39
|
+
Proc.new {
|
40
|
+
ryear = (first..last)
|
41
|
+
enum_year = first > last ? (ryear.first).downto(ryear.last) : (ryear.first).to(ryear.last)
|
42
|
+
enum_year.map { |year|
|
43
|
+
loop_item(
|
44
|
+
value: year,
|
45
|
+
text: year,
|
46
|
+
selected: html_selected( selected == year )
|
47
|
+
)
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tzispa
|
4
|
+
module Helpers
|
5
|
+
module Request
|
6
|
+
|
7
|
+
def request_data(fields)
|
8
|
+
Hash.new.tap { |data|
|
9
|
+
fields.each { |name|
|
10
|
+
macro_field = name.split('@:')
|
11
|
+
macro = macro_field.first.to_sym if macro_field.length == 2
|
12
|
+
field = macro_field.length == 2 ? macro_field.last : macro_field.first
|
13
|
+
field.split(':').tap { |fld|
|
14
|
+
src = fld.first
|
15
|
+
dest = fld.last
|
16
|
+
data[dest.to_sym] = macro ? send(macro, context.request[src]) : context.request[src]
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_data_object(data_object:, fields:)
|
23
|
+
data_object.tap { |data|
|
24
|
+
fields.each { |name|
|
25
|
+
macro_field = name.split('@:')
|
26
|
+
macro = macro_field.first.to_sym if macro_field.length == 2
|
27
|
+
field = macro_field.length == 2 ? macro_field.last : macro_field.first
|
28
|
+
field.split(':').tap { |fld|
|
29
|
+
src = fld.first
|
30
|
+
dest = fld.last
|
31
|
+
data.send "#{dest}=".to_sym, macro ? send(macro, context.request[src]) : context.request[src]
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def request_file_upload(request_file:, destination_path:, save_as: nil, keep_ext: true)
|
38
|
+
if request_file
|
39
|
+
fileext = File.extname(request_file[:filename]).downcase
|
40
|
+
filetype = request_file[:type]
|
41
|
+
save_ext = fileext if keep_ext
|
42
|
+
filename = (save_as ? "#{save_as}#{save_ext}" : request_file[:filename])
|
43
|
+
tempfile = request_file[:tempfile]
|
44
|
+
begin
|
45
|
+
dest_file = "#{destination_path}/#{filename}"
|
46
|
+
FileUtils.cp tempfile.path, dest_file
|
47
|
+
result = { name: filename, ext: fileext, path: dest_file, size: ::File.size(dest_file), type: filetype }
|
48
|
+
rescue => err
|
49
|
+
context.logger.fatal(err) if context
|
50
|
+
result = nil
|
51
|
+
ensure
|
52
|
+
tempfile.close
|
53
|
+
tempfile.unlink
|
54
|
+
end
|
55
|
+
else
|
56
|
+
result = nil
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require_relative 'mime'
|
5
|
+
|
6
|
+
module Tzispa
|
7
|
+
module Helpers
|
8
|
+
module Response
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.class_eval do
|
12
|
+
include Tzispa::Helpers::Mime
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set or retrieve the response status code.
|
17
|
+
def status(value = nil)
|
18
|
+
response.status = value if value
|
19
|
+
response.status
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set or retrieve the response body. When a block is given,
|
23
|
+
# evaluation is deferred until the body is read with #each.
|
24
|
+
def body(value = nil, &block)
|
25
|
+
if block_given?
|
26
|
+
def block.each; yield(call) end
|
27
|
+
response.body = block
|
28
|
+
elsif value
|
29
|
+
headers.delete 'Content-Length' unless request.head? || value.is_a?(Rack::File) || value.is_a?(Stream)
|
30
|
+
response.body = value
|
31
|
+
else
|
32
|
+
response.body
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Exit the current block, halts any further processing
|
37
|
+
# of the request, and returns the specified response.
|
38
|
+
def halt(*response)
|
39
|
+
response = response.first if response.length == 1
|
40
|
+
throw :halt, response
|
41
|
+
end
|
42
|
+
|
43
|
+
# Halt processing and redirect to the URI provided.
|
44
|
+
def redirect(uri, *args)
|
45
|
+
if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
|
46
|
+
status 303
|
47
|
+
else
|
48
|
+
status 302
|
49
|
+
end
|
50
|
+
|
51
|
+
# According to RFC 2616 section 14.30, "the field value consists of a
|
52
|
+
# single absolute URI"
|
53
|
+
response['Location'] = uri(uri.to_s, config.absolute_redirects?)
|
54
|
+
halt(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Generates the absolute URI for a given path in the app.
|
58
|
+
# Takes Rack routers and reverse proxies into account.
|
59
|
+
def uri(addr = nil, absolute = true)
|
60
|
+
return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
|
61
|
+
uri = [host = String.new]
|
62
|
+
if absolute
|
63
|
+
host << "http#{'s' if request.secure?}://"
|
64
|
+
if request.forwarded? or request.port != (request.secure? ? 443 : 80)
|
65
|
+
host << request.host_with_port
|
66
|
+
else
|
67
|
+
host << request.host
|
68
|
+
end
|
69
|
+
end
|
70
|
+
uri << (addr ? addr : request.path_info).to_s
|
71
|
+
File.join uri
|
72
|
+
end
|
73
|
+
|
74
|
+
# Halt processing and return the error status provided.
|
75
|
+
def error(code, body = nil)
|
76
|
+
code, body = 500, code.to_str if code.respond_to? :to_str
|
77
|
+
response.body = body unless body.nil?
|
78
|
+
halt code
|
79
|
+
end
|
80
|
+
|
81
|
+
# Halt processing and return a 404 Not Found
|
82
|
+
def not_found(body = nil)
|
83
|
+
error 404, body
|
84
|
+
end
|
85
|
+
|
86
|
+
# Halt processing and return a 401 Unauthorized
|
87
|
+
def unauthorized(body = nil)
|
88
|
+
error 401, body
|
89
|
+
end
|
90
|
+
|
91
|
+
# Set multiple response headers with Hash.
|
92
|
+
def headers(hash = nil)
|
93
|
+
response.headers.merge! hash if hash
|
94
|
+
response.headers
|
95
|
+
end
|
96
|
+
|
97
|
+
# Set the Content-Type of the response body given a media type or file
|
98
|
+
# extension.
|
99
|
+
def content_type(type = nil, params = {})
|
100
|
+
return response['Content-Type'] unless type
|
101
|
+
default = params.delete :default
|
102
|
+
mime_type = mime_type(type) || default
|
103
|
+
fail "Unknown media type: %p" % type if mime_type.nil?
|
104
|
+
mime_type = mime_type.dup
|
105
|
+
unless params.include? :charset
|
106
|
+
params[:charset] = params.delete('charset') || config.default_encoding
|
107
|
+
end
|
108
|
+
params.delete :charset if mime_type.include? 'charset'
|
109
|
+
unless params.empty?
|
110
|
+
mime_type << (mime_type.include?(';') ? ', ' : ';')
|
111
|
+
mime_type << params.map do |key, val|
|
112
|
+
val = val.inspect if val =~ /[";,]/
|
113
|
+
"#{key}=#{val}"
|
114
|
+
end.join(', ')
|
115
|
+
end
|
116
|
+
response['Content-Type'] = mime_type
|
117
|
+
end
|
118
|
+
|
119
|
+
def attachment!(filename = nil, disposition = 'attachment')
|
120
|
+
content_disposition = disposition.to_s
|
121
|
+
content_disposition += "; filename=\"#{filename}\"; filename*=UTF-8''#{URI.escape(filename)}" if !filename.nil?
|
122
|
+
response['Content-Disposition'] = content_disposition
|
123
|
+
end
|
124
|
+
|
125
|
+
# Use the contents of the file at +path+ as the response body.
|
126
|
+
def send_file(path, opts = {})
|
127
|
+
begin
|
128
|
+
if opts[:type] or not response['Content-Type']
|
129
|
+
content_type opts[:type] || opts[:extension], :default => 'application/octet-stream'
|
130
|
+
end
|
131
|
+
|
132
|
+
disposition = opts[:disposition]
|
133
|
+
filename = opts[:filename]
|
134
|
+
disposition = 'attachment' if disposition.nil? and filename
|
135
|
+
filename = path if filename.nil?
|
136
|
+
|
137
|
+
attachment! filename, disposition
|
138
|
+
last_modified opts[:last_modified] if opts[:last_modified]
|
139
|
+
|
140
|
+
file = Rack::File.new nil
|
141
|
+
file.path = path
|
142
|
+
result = file.serving context.env
|
143
|
+
result[1].each { |k,v| response.headers[k] ||= v }
|
144
|
+
response.headers['Content-Length'] = result[1]['Content-Length']
|
145
|
+
#opts[:status] &&= Integer(opts[:status])
|
146
|
+
#halt opts[:status] || result[0], result[2]
|
147
|
+
response.status = result[0]
|
148
|
+
response.body = result[2]
|
149
|
+
rescue
|
150
|
+
not_found 'Fichero no encontrado'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Sugar for redirect (example: redirect back)
|
155
|
+
def back
|
156
|
+
request.referer
|
157
|
+
end
|
158
|
+
|
159
|
+
# whether or not the status is set to 1xx
|
160
|
+
def informational?
|
161
|
+
response.status.between? 100, 199
|
162
|
+
end
|
163
|
+
|
164
|
+
# whether or not the status is set to 2xx
|
165
|
+
def success?
|
166
|
+
response.status.between? 200, 299
|
167
|
+
end
|
168
|
+
|
169
|
+
# whether or not the status is set to 3xx
|
170
|
+
def redirect?
|
171
|
+
response.status.between? 300, 399
|
172
|
+
end
|
173
|
+
|
174
|
+
# whether or not the status is set to 4xx
|
175
|
+
def client_error?
|
176
|
+
response.status.between? 400, 499
|
177
|
+
end
|
178
|
+
|
179
|
+
# whether or not the status is set to 5xx
|
180
|
+
def server_error?
|
181
|
+
response.status.between? 500, 599
|
182
|
+
end
|
183
|
+
|
184
|
+
# whether or not the status is set to 404
|
185
|
+
def not_found?
|
186
|
+
response.status == 404
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
class NotFound < NameError #:nodoc:
|
191
|
+
def http_status; 404 end
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Helpers
|
7
|
+
module Security
|
8
|
+
|
9
|
+
|
10
|
+
def secret(length)
|
11
|
+
alfanb = ['!', '$', '%', '&', '@', '#', '=', '_' '+', '-', '*', '/', '?', ':', ';', '.', ',',
|
12
|
+
('a'..'z'), ('0'..'9'), ('A'..'Z')].map { |i| i.to_a }.flatten
|
13
|
+
(0...length-1).map { alfanb[rand(alfanb.length)] }.join
|
14
|
+
end
|
15
|
+
|
16
|
+
def uuid
|
17
|
+
sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
18
|
+
# 32 bits for "time_low"
|
19
|
+
rand(0..0xffff), rand(0..0xffff),
|
20
|
+
|
21
|
+
# 16 bits for "time_mid"
|
22
|
+
rand(0..0xffff),
|
23
|
+
|
24
|
+
# 16 bits for "time_hi_and_version",
|
25
|
+
#3 four most significant bits holds version number 4
|
26
|
+
rand(0..0x0fff) | 0x4000,
|
27
|
+
|
28
|
+
# 16 bits, 8 bits for "clk_seq_hi_res",
|
29
|
+
# 8 bits for "clk_seq_low",
|
30
|
+
# two most significant bits holds zero and one for variant DCE1.1
|
31
|
+
rand(0..0x3fff) | 0x8000,
|
32
|
+
|
33
|
+
# 48 bits for "node"
|
34
|
+
rand(0..0xffff), rand(0..0xffff), rand(0..0xffff)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def sign_array(astr, salt=nil)
|
39
|
+
sign, i = String.new, 0
|
40
|
+
astr.each { |s|
|
41
|
+
i = i + 1
|
42
|
+
sign << "#{"_"*i}#{s}"
|
43
|
+
}
|
44
|
+
sign << "**#{salt}"
|
45
|
+
Digest::SHA1.hexdigest sign
|
46
|
+
end
|
47
|
+
|
48
|
+
def hash_password(password, salt)
|
49
|
+
Digest::MD5::hexdigest "#{password}::#{salt}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def hash_password?(hashed, pwd, salt)
|
53
|
+
hashed == hash_password(pwd, salt)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
class Identity
|
58
|
+
|
59
|
+
attr_reader :id, :token
|
60
|
+
|
61
|
+
def initialize(id, secret)
|
62
|
+
@id = id
|
63
|
+
@token = generate_token id, secret
|
64
|
+
end
|
65
|
+
|
66
|
+
def valid?(secret)
|
67
|
+
@token == Identity.generate_token(@id, secret)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def self.generate_token(value, salt)
|
73
|
+
Digest::SHA1.hexdigest "___#{value}_#{salt}__token__"
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'bigdecimal'
|
5
|
+
require 'i18n'
|
6
|
+
require "unicode_utils"
|
7
|
+
|
8
|
+
module Tzispa
|
9
|
+
module Helpers
|
10
|
+
module Text
|
11
|
+
|
12
|
+
def remove_words(sentence, removable)
|
13
|
+
sentence.split.delete_if{|x| removable.include?(UnicodeUtils.downcase x)}.join(' ')
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_phrases(text, removable)
|
17
|
+
removable.each { |phrase|
|
18
|
+
text.slice! phrase
|
19
|
+
}
|
20
|
+
text
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_parenthesized_text(text)
|
24
|
+
text.gsub(/\([^\)]*\)/, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
def synonymize(sentence, synonyms)
|
28
|
+
sentence.gsub(/[[:alnum:]]+/) {|word|
|
29
|
+
dwword = UnicodeUtils.downcase word
|
30
|
+
synonyms.has_key?(dwword) ? synonyms[dwword] : word
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def strip_to_nil(str, transform = nil)
|
35
|
+
sstr = str.strip if str and not str.strip.empty?
|
36
|
+
case transform
|
37
|
+
when :upcase then
|
38
|
+
UnicodeUtils.upcase(sstr)
|
39
|
+
when :downcase then
|
40
|
+
UnicodeUtils.downcase(sstr)
|
41
|
+
when :titlecase then
|
42
|
+
UnicodeUtils.titlecase(sstr)
|
43
|
+
else sstr
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def split_to_array(str, separator=';')
|
48
|
+
str.split(separator) if str
|
49
|
+
end
|
50
|
+
|
51
|
+
def join_to_nil(ary, separator)
|
52
|
+
ary.join(separator) if ary and not ary.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def str_to_bool(str, strue=nil)
|
56
|
+
strue ? (strue == str) : (str == 'yes' || str == 'true')
|
57
|
+
end
|
58
|
+
|
59
|
+
def str_to_date(str, format=nil)
|
60
|
+
begin
|
61
|
+
Date.strptime(str, format || I18n.t('date.formats.default')) if str
|
62
|
+
rescue
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def str_to_datetime(str, format=nil)
|
68
|
+
begin
|
69
|
+
result = DateTime.strptime(str, format || I18n.t('time.formats.default'))
|
70
|
+
rescue
|
71
|
+
result = nil
|
72
|
+
end
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
def amount(number, options = {})
|
77
|
+
if number.nil? && options[:nil_as_dash] != false
|
78
|
+
'–'
|
79
|
+
elsif number.zero? && options[:zero_as_dash]
|
80
|
+
'–'
|
81
|
+
else
|
82
|
+
precision = options[:precision]
|
83
|
+
if precision
|
84
|
+
options[:minimum_precision] = precision
|
85
|
+
options[:maximum_precision] = precision
|
86
|
+
end
|
87
|
+
|
88
|
+
number = number.round if options[:round]
|
89
|
+
separator = options.fetch(:separator, I18n.t('number.currency.format.separator'))
|
90
|
+
delimiter = options.fetch(:delimiter, I18n.t('number.currency.format.delimiter')).to_s
|
91
|
+
minimum_precision = options[:minimum_precision] || 0
|
92
|
+
str = number.is_a?(BigDecimal) ? number.to_s('F').sub(/\.0+\z/, "") : number.to_s.sub(/\.0+\z/, "")
|
93
|
+
str =~ /\A(\-?)(\d+)(?:\.(\d+))?\z/ or raise "Could not parse number: #{number}"
|
94
|
+
sign = $1
|
95
|
+
integer = $2
|
96
|
+
fraction = ($3 || '')
|
97
|
+
|
98
|
+
if options[:maximum_precision]
|
99
|
+
fraction = fraction[0, options[:maximum_precision]] if options[:maximum_precision]
|
100
|
+
end
|
101
|
+
|
102
|
+
if minimum_precision > 0
|
103
|
+
if fraction.length > 0 || minimum_precision > 0
|
104
|
+
fraction = "#{fraction}#{'0' * [0, minimum_precision - fraction.length].max}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# the following two lines appear to be the most performant way to add a delimiter to every thousands place in the number
|
109
|
+
integer_size = integer.size
|
110
|
+
(1..((integer_size-1) / 3)).each {|x| integer[integer_size-x*3,0] = delimiter}
|
111
|
+
str = integer.chomp(delimiter)
|
112
|
+
|
113
|
+
# add fraction
|
114
|
+
str << "#{separator}#{fraction}" if fraction.length > 0
|
115
|
+
|
116
|
+
# restore sign
|
117
|
+
str = "#{sign}#{str}"
|
118
|
+
# add unit if given
|
119
|
+
if options[:unit]
|
120
|
+
unless options[:unit_separator] == false
|
121
|
+
str << options.fetch(:unit_separator, ' ')
|
122
|
+
end
|
123
|
+
str << options[:unit]
|
124
|
+
end
|
125
|
+
str
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def money_amount(number, options = {})
|
130
|
+
amount(number, options.merge(:unit => I18n.t('number.currency.format.unit'), :nil_as_dash => true, :precision => I18n.t('number.currency.format.precision')))
|
131
|
+
end
|
132
|
+
|
133
|
+
def price_amount(number, options = {})
|
134
|
+
amount(number, options.merge(:nil_as_dash => true, :precision => I18n.t('number.currency.format.precision')))
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Tzispa
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
require 'tzispa/helpers/version'
|
5
|
+
require 'tzispa/helpers/crawler'
|
6
|
+
require 'tzispa/helpers/html'
|
7
|
+
require 'tzispa/helpers/mail'
|
8
|
+
require 'tzispa/helpers/mime'
|
9
|
+
require 'tzispa/helpers/pattern'
|
10
|
+
require 'tzispa/helpers/request'
|
11
|
+
require 'tzispa/helpers/response'
|
12
|
+
require 'tzispa/helpers/security'
|
13
|
+
require 'tzispa/helpers/text'
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tzispa_helpers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Juan Antonio Piñero
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mail
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: i18n
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: unicode_utils
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: htmlentities
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: reverse_markdown
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: redcarpet
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.3'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.3'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: nokogiri
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.6'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.6'
|
111
|
+
description: Module Helpers for Tzispa framework
|
112
|
+
email:
|
113
|
+
- japinero@area-integral.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- CHANGELOG.md
|
119
|
+
- README.md
|
120
|
+
- lib/tzispa/helpers.rb
|
121
|
+
- lib/tzispa/helpers/crawler.rb
|
122
|
+
- lib/tzispa/helpers/html.rb
|
123
|
+
- lib/tzispa/helpers/mail.rb
|
124
|
+
- lib/tzispa/helpers/mime.rb
|
125
|
+
- lib/tzispa/helpers/pattern.rb
|
126
|
+
- lib/tzispa/helpers/request.rb
|
127
|
+
- lib/tzispa/helpers/response.rb
|
128
|
+
- lib/tzispa/helpers/security.rb
|
129
|
+
- lib/tzispa/helpers/text.rb
|
130
|
+
- lib/tzispa/helpers/version.rb
|
131
|
+
- lib/tzispa_helpers.rb
|
132
|
+
homepage: https://www.area-integral.com
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - "~>"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '2.3'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.5.1
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Helpers for Tzispa
|
156
|
+
test_files: []
|