tzispa_helpers 0.1.3
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/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: []
|