snails 0.3.6 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/snails.rb +6 -1
- data/lib/snails/app.rb +17 -8
- data/lib/snails/mailer.rb +100 -27
- data/lib/snails/markdown.rb +53 -0
- data/lib/snails/tasks.rb +2 -4
- data/lib/snails/util.rb +6 -10
- data/snails.gemspec +2 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 483517cd1b55bc5273ebb004bb9ef69a8cd6191de4be897c3d9f2c168ea13f4f
|
4
|
+
data.tar.gz: 1730ce09810faafc5802833ab4f38fb103e8dae059b9cfb51b85335893fb7f09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e104410a7d4f8eea1370a583d13e4f23cefd043bc78f12027b5fea7063a87285c2cbeb89a1ab8c9a43e7a90d582b3b397528f1c483df2db60cf7dc654f66aba7
|
7
|
+
data.tar.gz: dfe0ae0b0116f29c71907e35a38548d3d28af693e7dbf824b29f6e467ddab841d588c6f573b238c3e50d420bae649f81fca9efb73cb3709ac158eb121dae45e3
|
data/lib/snails.rb
CHANGED
@@ -41,5 +41,10 @@ module Snails
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
# sinatra/activerecord only 'reads' RACK_ENV, so make sure it is set
|
45
|
+
ENV['RACK_ENV'] ||= ENV['RAILS_ENV']
|
46
|
+
|
44
47
|
require 'snails/app' unless Snails.env.test?
|
45
|
-
|
48
|
+
unless ENV['SILENT']
|
49
|
+
puts "Loaded #{Snails.env} environment. (#{Time.now.to_s})"
|
50
|
+
end
|
data/lib/snails/app.rb
CHANGED
@@ -13,7 +13,7 @@ module Snails
|
|
13
13
|
module All
|
14
14
|
|
15
15
|
# usage:
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# class App < Snails::App
|
18
18
|
# register Snails::All
|
19
19
|
# end
|
@@ -99,7 +99,7 @@ module Snails
|
|
99
99
|
|
100
100
|
cwd = Pathname.new(Dir.pwd)
|
101
101
|
app.set :sprockets, Sprockets::Environment.new(cwd)
|
102
|
-
app.set :digest_assets, false
|
102
|
+
app.set :digest_assets, false
|
103
103
|
app.set :assets_prefix, app.setting(:assets_prefix, '/assets') # URL
|
104
104
|
app.set :assets_paths, app.setting(:assets_paths, %w(assets)) # source files
|
105
105
|
app.set :assets_public_path, app.setting(:assets_public_path, cwd.join('public', 'assets')) # output dir
|
@@ -308,8 +308,8 @@ module Snails
|
|
308
308
|
label = options[:label] || field.to_s.gsub('_', ' ').capitalize
|
309
309
|
example = options[:example] ? "<small class='example'>#{options[:example]}</small>" : ''
|
310
310
|
|
311
|
-
id = "#{get_model_name(object).downcase}_#{options[:key] || field}"
|
312
|
-
name = "#{get_model_name(object).downcase}[#{field}]"
|
311
|
+
id = options[:id] || "#{get_model_name(object).downcase}_#{options[:key] || field}"
|
312
|
+
name = options[:name] || "#{get_model_name(object).downcase}[#{field}]"
|
313
313
|
label = options[:label] == false ? '' : "<label for='#{id}'>#{label}#{example}</label>\n"
|
314
314
|
|
315
315
|
@tabindex = @tabindex ? @tabindex + 1 : 1
|
@@ -353,6 +353,14 @@ module Snails
|
|
353
353
|
erb(view_name.to_sym, { layout: layout }.merge(opts))
|
354
354
|
end
|
355
355
|
|
356
|
+
def show_date(date)
|
357
|
+
date ? date.strftime("%d de %b, %Y") : ''
|
358
|
+
end
|
359
|
+
|
360
|
+
def show_time(time)
|
361
|
+
time ? time.strftime("%d de %b, %Y a las %H:%M") : ''
|
362
|
+
end
|
363
|
+
|
356
364
|
#########################################
|
357
365
|
# pagination
|
358
366
|
|
@@ -381,7 +389,7 @@ module Snails
|
|
381
389
|
|
382
390
|
def self.registered(app)
|
383
391
|
app.set :sessions, {
|
384
|
-
key: app.setting(:session_key, 'snails.session'),
|
392
|
+
key: app.setting(:session_key, 'snails.session'),
|
385
393
|
path: app.setting(:session_path, '/'),
|
386
394
|
expire_after: app.setting(:session_expire_after, 2592000), # one month in seconds
|
387
395
|
secret: app.session_secret # uses previous set :session_secret or generates one
|
@@ -411,16 +419,17 @@ module Snails
|
|
411
419
|
enable :logging
|
412
420
|
|
413
421
|
use Rack::CommonLogger, Snails.logger
|
414
|
-
use Rack::Static, urls: static_paths, root: 'public' unless Snails.env.production?
|
422
|
+
use Rack::Static, urls: static_paths, root: 'public' unless Snails.env.production?
|
415
423
|
|
416
424
|
configure :production, :staging do
|
417
|
-
set :raise_errors,
|
425
|
+
set :raise_errors, false
|
418
426
|
set :dump_errors, false
|
427
|
+
set :show_exceptions, false
|
419
428
|
end
|
420
429
|
|
421
430
|
configure :development do
|
422
431
|
set :raise_errors, true
|
423
|
-
set :dump_errors,
|
432
|
+
set :dump_errors, true
|
424
433
|
set :show_exceptions, true
|
425
434
|
set :log_level, Logger::DEBUG
|
426
435
|
end
|
data/lib/snails/mailer.rb
CHANGED
@@ -12,32 +12,28 @@ module Snails
|
|
12
12
|
|
13
13
|
@queue = :emails
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
if key = mail_config.dig(:dkim, :private_key) and File.exist?(key)
|
19
|
-
mail_config[:dkim][:private_key] = IO.read(key)
|
20
|
-
elsif mail_config[:dkim]
|
21
|
-
puts "Private key for DKIM not found! Disabling..."
|
22
|
-
mail_config.delete(:dkim)
|
23
|
-
end
|
15
|
+
# class Bounce < StandardError; end
|
16
|
+
# class SoftBounce < Bounce; end
|
17
|
+
# class HardBounce < Bounce; end
|
24
18
|
|
25
|
-
|
26
|
-
@debug = mail_config[:debug]
|
19
|
+
def initialize(opts)
|
27
20
|
@from_email = opts[:from] or raise ":from required"
|
28
21
|
@base_subject = opts[:base_subject] || ''
|
29
22
|
@views = opts[:views] || Snails.root.join('lib', 'views')
|
30
23
|
@logfile = opts[:logfile] # || Snails.root.join('log', 'mailer.log')
|
24
|
+
|
25
|
+
backend_name = opts[:backend_name] || 'TestBackend'
|
26
|
+
@backend = self.class.backends[backend_name].new(opts[:backend_options])
|
31
27
|
end
|
32
28
|
|
33
29
|
def email(name, &block)
|
34
30
|
define_singleton_method(name) do |*args|
|
35
|
-
instance_exec(*args, &block)
|
31
|
+
instance_exec(*args, &block)
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
39
35
|
def helpers(&block)
|
40
|
-
instance_eval(&block)
|
36
|
+
instance_eval(&block)
|
41
37
|
end
|
42
38
|
|
43
39
|
def perform(method, *args)
|
@@ -81,50 +77,127 @@ A <%= @exception.class %> occurred in <%= @url %>:
|
|
81
77
|
|
82
78
|
private
|
83
79
|
|
84
|
-
def render(
|
85
|
-
|
80
|
+
def render(view_path, layout_lath = nil)
|
81
|
+
view = File.read(File.join(@views, "#{view_path}.erb"))
|
82
|
+
if layout_path
|
83
|
+
layout = File.read(File.join(@views, "#{layout_path}.erb"))
|
84
|
+
render_erb(layout) { render_erb(view) }
|
85
|
+
else
|
86
|
+
render_erb(view)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def render_erb(template)
|
86
91
|
ERB.new(template).result(binding)
|
87
92
|
end
|
88
93
|
|
89
94
|
def partial(name)
|
90
95
|
partial_name = name.to_s["/"] ? name.to_s.reverse.sub("/", "_/").reverse : "_#{name}"
|
91
|
-
render
|
96
|
+
render(partial_name)
|
92
97
|
end
|
93
98
|
|
94
99
|
def logger
|
95
100
|
@logger ||= @logfile ? Logger.new(@logfile) : Snails.logger
|
96
101
|
end
|
97
102
|
|
98
|
-
def send_email(from:
|
103
|
+
def send_email(from: @from_email, to:, subject:, body: nil, template: nil, layout: nil, html_body: nil, html_template: nil, html_layout: nil, message_id: nil, return_path: nil, list_unsubscribe: nil)
|
99
104
|
raise "No recipient given for mail: #{subject}!" if to.blank?
|
100
105
|
|
101
106
|
message = {
|
102
107
|
to: to,
|
103
|
-
from: from
|
104
|
-
subject: @base_subject + subject
|
108
|
+
from: from,
|
109
|
+
subject: @base_subject + subject
|
105
110
|
}
|
106
111
|
|
112
|
+
raise "Blank from" if message[:from].blank?
|
113
|
+
raise "Blank to" if message[:to].blank?
|
114
|
+
|
107
115
|
if body or template
|
108
|
-
message[:body] = template ? render(template) : body
|
116
|
+
message[:body] = template ? render(template, layout) : body
|
109
117
|
end
|
110
118
|
|
111
119
|
if html_body or html_template
|
112
|
-
message[:html_body] = html_template ? render(html_template) : html_body
|
120
|
+
message[:html_body] = html_template ? render(html_template, html_layout) : html_body
|
113
121
|
end
|
114
122
|
|
115
123
|
message[:message_id] = message_id if message_id
|
116
124
|
message[:return_path] = return_path if return_path
|
117
125
|
message[:list_unsubscribe] = list_unsubscribe if list_unsubscribe
|
118
126
|
|
119
|
-
logger.info "[#{to}] Delivering: #{subject}"
|
120
|
-
|
121
|
-
|
127
|
+
logger.info "[#{to}] Delivering: #{subject}"
|
128
|
+
@backend.deliver(message)
|
129
|
+
end
|
130
|
+
|
131
|
+
alias_method :deliver, :send_email
|
132
|
+
|
133
|
+
class Backend
|
134
|
+
def deliver(email, options = {})
|
135
|
+
raise "Please redefine in subclass"
|
136
|
+
end
|
137
|
+
|
138
|
+
def deliver_many(emails, options = {})
|
139
|
+
emails.map { |e| deliver(e, options) }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class Tuktuk < Backend
|
144
|
+
def initialize(config = {})
|
145
|
+
@debug = config[:debug]
|
146
|
+
|
147
|
+
if key = config.dig(:dkim, :private_key) and File.exist?(key)
|
148
|
+
config[:dkim][:private_key] = IO.read(key)
|
149
|
+
elsif config[:dkim]
|
150
|
+
puts "Private key for DKIM not found! Disabling..."
|
151
|
+
config.delete(:dkim)
|
152
|
+
end
|
153
|
+
|
154
|
+
Tuktuk.options = config
|
155
|
+
end
|
156
|
+
|
157
|
+
def deliver(email, options = {})
|
158
|
+
debug = @debug.nil? ? !Snails.env.production? : @debug # if debug isn't set, determine based on env
|
159
|
+
resp, email = Tuktuk.deliver(email, debug: debug)
|
160
|
+
|
161
|
+
if resp.is_a?(Tuktuk::Bounce)
|
162
|
+
logger.info "[#{to}] Email bounced! [#{resp.code}] #{resp.message}"
|
163
|
+
end
|
164
|
+
|
165
|
+
return resp, email
|
166
|
+
end
|
167
|
+
|
168
|
+
def deliver_many(emails, options = {})
|
169
|
+
Tuktuk.deliver_many(emails, options)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Mailgun < Backend
|
174
|
+
|
175
|
+
def initialize(api_key:, domain_name:)
|
176
|
+
@key = api_key
|
177
|
+
@url = "https://api.mailgun.net/v3/#{domain_name}/messages"
|
178
|
+
end
|
179
|
+
|
180
|
+
def deliver(email, options = {})
|
181
|
+
raise "No body!" if email[:body].nil?
|
182
|
+
|
183
|
+
data = {
|
184
|
+
from: email[:from],
|
185
|
+
to: email[:to],
|
186
|
+
subject: email[:subject],
|
187
|
+
}
|
188
|
+
|
189
|
+
data[:text] = email[:body] if email[:body]
|
190
|
+
data[:html] = email[:html_body] if email[:html_body]
|
191
|
+
|
192
|
+
if data[:text].blank? && data[:html].blank?
|
193
|
+
raise ArgumentError, "Either text or html required"
|
194
|
+
end
|
122
195
|
|
123
|
-
|
124
|
-
|
196
|
+
opts = { username: 'api', password: @key }
|
197
|
+
resp = Dagger.post(@url, data, opts)
|
198
|
+
resp.ok? ? [resp.data, data[:to]] : nil
|
125
199
|
end
|
126
200
|
|
127
|
-
return resp, email
|
128
201
|
end
|
129
202
|
|
130
203
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
begin
|
2
|
+
require 'rinku'
|
3
|
+
require 'kramdown'
|
4
|
+
require 'sanitize'
|
5
|
+
|
6
|
+
module Snails
|
7
|
+
module Markdown
|
8
|
+
|
9
|
+
MARKDOWN_OPTS = {
|
10
|
+
input: 'GFM',
|
11
|
+
enable_coderay: true,
|
12
|
+
parse_block_html: false,
|
13
|
+
line_width: 72,
|
14
|
+
coderay_tab_width: 2,
|
15
|
+
coderay_line_numbers: nil,
|
16
|
+
coderay_default_lang: 'bash',
|
17
|
+
coderay_css: :class
|
18
|
+
}
|
19
|
+
|
20
|
+
SANITIZE_OPTS = Sanitize::Config::RELAXED
|
21
|
+
|
22
|
+
SANITIZE_OPTS[:elements] = %w(
|
23
|
+
p br a span sub sup strong em div hr abbr
|
24
|
+
ul ol li
|
25
|
+
blockquote pre code kbd
|
26
|
+
h1 h2 h3 h4 h5 h6
|
27
|
+
img object param del
|
28
|
+
)
|
29
|
+
|
30
|
+
SANITIZE_OPTS[:attributes] = {
|
31
|
+
:all => ['class', 'style', 'title', 'id'],
|
32
|
+
'a' => ['href', 'rel', 'name'],
|
33
|
+
'img' => ['src', 'title', 'alt', 'width', 'height']
|
34
|
+
}
|
35
|
+
|
36
|
+
SANITIZE_OPTS[:add_attributes] = { 'a' => {'rel' => 'nofollow', 'target' => '_blank'} }
|
37
|
+
|
38
|
+
def render_markdown(str, options = {})
|
39
|
+
html = Kramdown::Document.new(str, MARKDOWN_OPTS).to_html
|
40
|
+
html = Rinku.auto_link(html, :urls)
|
41
|
+
Sanitize.clean(html, SANITIZE_OPTS)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
rescue LoadError => e
|
49
|
+
puts "Cannot load Snails's markdown lib. (#{e.class})"
|
50
|
+
puts "You need the `kramdown`, `rinku` and `sanitize` gems for this."
|
51
|
+
end
|
52
|
+
|
53
|
+
|
data/lib/snails/tasks.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'snails'
|
2
2
|
|
3
|
-
if defined?(Sinatra::ActiveRecordHelper)
|
3
|
+
if defined?(Sinatra::ActiveRecordHelper) or File.exist?(FileUtils.pwd + '/db/schema.rb')
|
4
4
|
require 'sinatra/activerecord/rake'
|
5
5
|
end
|
6
6
|
|
7
7
|
namespace :assets do
|
8
|
-
|
9
8
|
desc 'Precompiles assets'
|
10
9
|
task :precompile do
|
11
10
|
if Snails.apps.empty?
|
@@ -16,5 +15,4 @@ namespace :assets do
|
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|
19
|
-
|
20
|
-
end
|
18
|
+
end
|
data/lib/snails/util.rb
CHANGED
@@ -6,16 +6,12 @@ module Snails
|
|
6
6
|
|
7
7
|
def self.minutes_in_words(min)
|
8
8
|
case min.to_i
|
9
|
-
when 0..1 then '
|
10
|
-
when 2..4 then '
|
11
|
-
when 5..14 then '
|
9
|
+
when 0..1 then 'un minuto'
|
10
|
+
when 2..4 then '5 minutos'
|
11
|
+
when 5..14 then '15 minutos'
|
12
12
|
when 15..29 then "media hora"
|
13
|
-
when 30..59 then "#{min}
|
14
|
-
when 60..
|
15
|
-
when 120..239 then '2 horas'
|
16
|
-
when 240..479 then '4 horas'
|
17
|
-
when 480..719 then '8 horas'
|
18
|
-
when 720..1439 then '12 horas'
|
13
|
+
when 30..59 then "#{min} min"
|
14
|
+
when 60..1439 then "#{(min/60).floor} horas"
|
19
15
|
when 1440..11519 then "#{(min/1440).floor} días"
|
20
16
|
when 11520..43199 then "#{(min/11520).floor} semanas"
|
21
17
|
when 43200..525599 then "#{(min/43200).floor} meses"
|
@@ -34,7 +30,7 @@ module Snails
|
|
34
30
|
if Time.now < self
|
35
31
|
# "#{str} más"
|
36
32
|
"en #{str}"
|
37
|
-
else
|
33
|
+
else
|
38
34
|
"hace #{str}"
|
39
35
|
end
|
40
36
|
end
|
data/snails.gemspec
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "snails"
|
6
|
-
s.version = '0.3
|
6
|
+
s.version = '0.4.3'
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.authors = ['Tomás Pollak']
|
9
9
|
s.email = ['tomas@forkhq.com']
|
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.add_development_dependency "fuubar", '>= 2.3.2'
|
18
18
|
|
19
19
|
s.add_runtime_dependency "i18n", ">= 1.0.1"
|
20
|
+
s.add_runtime_dependency "activerecord", "< 6.0"
|
20
21
|
s.add_runtime_dependency "sinatra-contrib", ">= 2.0.3"
|
21
22
|
s.add_runtime_dependency "sinatra-activerecord", ">= 2.0.13"
|
22
23
|
s.add_runtime_dependency "sinatra-flash", ">= 0.3.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomás Pollak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,6 +72,20 @@ dependencies:
|
|
72
72
|
- - ">="
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: 1.0.1
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: activerecord
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "<"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '6.0'
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "<"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '6.0'
|
75
89
|
- !ruby/object:Gem::Dependency
|
76
90
|
name: sinatra-contrib
|
77
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -159,6 +173,7 @@ files:
|
|
159
173
|
- lib/snails.rb
|
160
174
|
- lib/snails/app.rb
|
161
175
|
- lib/snails/mailer.rb
|
176
|
+
- lib/snails/markdown.rb
|
162
177
|
- lib/snails/rspec.rb
|
163
178
|
- lib/snails/tasks.rb
|
164
179
|
- lib/snails/util.rb
|