snails 0.3.6 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33aac0b5a88a59ea685e3fc896dbcdc7ee369b486b3802ceccd3b1e860d38db1
4
- data.tar.gz: 6b6612a3054b980f46c0375967ee02204c11982d11179a4ef500bd0e1b94aeda
3
+ metadata.gz: 483517cd1b55bc5273ebb004bb9ef69a8cd6191de4be897c3d9f2c168ea13f4f
4
+ data.tar.gz: 1730ce09810faafc5802833ab4f38fb103e8dae059b9cfb51b85335893fb7f09
5
5
  SHA512:
6
- metadata.gz: e7b51335c3a06e4a9d98bd6fc519a7ed72137c6249d940a5394acb9c10e4e167a02b6f9b8c09cdb756b8d0e4263602dd60e7b925c266993cac8d1fdbd2af825e
7
- data.tar.gz: f60ec8d7471561c0f7b0db13a8ad9eef8def39cc285475bd907714a638614cdc615977042fa416121be61fb99403e4b1a956ec45cf6ce7f0de917d986602d15c
6
+ metadata.gz: e104410a7d4f8eea1370a583d13e4f23cefd043bc78f12027b5fea7063a87285c2cbeb89a1ab8c9a43e7a90d582b3b397528f1c483df2db60cf7dc654f66aba7
7
+ data.tar.gz: dfe0ae0b0116f29c71907e35a38548d3d28af693e7dbf824b29f6e467ddab841d588c6f573b238c3e50d420bae649f81fca9efb73cb3709ac158eb121dae45e3
@@ -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
- puts "Loaded #{Snails.env} environment."
48
+ unless ENV['SILENT']
49
+ puts "Loaded #{Snails.env} environment. (#{Time.now.to_s})"
50
+ end
@@ -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, true
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, false
432
+ set :dump_errors, true
424
433
  set :show_exceptions, true
425
434
  set :log_level, Logger::DEBUG
426
435
  end
@@ -12,32 +12,28 @@ module Snails
12
12
 
13
13
  @queue = :emails
14
14
 
15
- def initialize(opts)
16
- mail_config = (opts[:smtp] || opts[:mail]) or raise ":smtp options missing"
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
- Tuktuk.options = mail_config
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(view)
85
- template = File.read(File.join(@views, "#{view}.erb"))
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 partial_name
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: nil, to:, subject:, body: nil, template: nil, html_body: nil, html_template: nil, message_id: nil, return_path: nil, list_unsubscribe: nil)
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 || @from_email,
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
- debug = @debug.nil? ? !Snails.env.production? : @debug # if debug isn't set, determine based on env
121
- resp, email = Tuktuk.deliver(message, debug: debug)
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
- if resp.is_a?(Tuktuk::Bounce)
124
- logger.info "[#{to}] Email bounced! [#{resp.code}] #{resp.message}"
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
+
@@ -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
@@ -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 'menos de un min'
10
- when 2..4 then 'menos de 5 min'
11
- when 5..14 then 'menos de 15 min'
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} minutos"
14
- when 60..119 then '1 hora'
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
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "snails"
6
- s.version = '0.3.6'
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.6
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: 2019-02-10 00:00:00.000000000 Z
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