antwort 0.0.12 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +12 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +28 -1
  6. data/README.md +1 -1
  7. data/antwort.gemspec +2 -2
  8. data/lib/antwort.rb +5 -4
  9. data/lib/antwort/builder/builder.rb +15 -24
  10. data/lib/antwort/builder/email.rb +47 -22
  11. data/lib/antwort/builder/flattener.rb +8 -9
  12. data/lib/antwort/builder/partial.rb +12 -11
  13. data/lib/antwort/builder/style.rb +26 -27
  14. data/lib/antwort/{builder.rb → builders.rb} +3 -3
  15. data/lib/antwort/cli.rb +0 -1
  16. data/lib/antwort/cli/cli.rb +42 -48
  17. data/lib/antwort/cli/send.rb +11 -14
  18. data/lib/antwort/cli/upload.rb +29 -28
  19. data/lib/antwort/core.rb +4 -0
  20. data/lib/antwort/email/build.rb +41 -0
  21. data/lib/antwort/email/collection.rb +41 -0
  22. data/lib/antwort/email/data.rb +27 -0
  23. data/lib/antwort/email/template.rb +88 -0
  24. data/lib/antwort/helpers/file_helper.rb +27 -0
  25. data/lib/antwort/{helpers.rb → helpers/helper.rb} +2 -2
  26. data/lib/antwort/helpers/logic_helper.rb +81 -0
  27. data/lib/antwort/helpers/markup_helper.rb +37 -0
  28. data/lib/antwort/{builder/helpers/sanitizers.rb → helpers/sanitizers_helper.rb} +2 -4
  29. data/lib/antwort/helpers/server_helper.rb +32 -0
  30. data/lib/antwort/{server.rb → server/server.rb} +26 -23
  31. data/{template/project → lib/antwort/server}/views/404.html.erb +1 -1
  32. data/{template/project → lib/antwort/server}/views/index.html.erb +4 -6
  33. data/{template/project → lib/antwort/server}/views/layout.erb +5 -5
  34. data/{template/project/views/markup/_button.html.erb → lib/antwort/server/views/markup/button.html.erb} +0 -0
  35. data/{template/project/views/markup/_image_tag.html.erb → lib/antwort/server/views/markup/image_tag.html.erb} +0 -0
  36. data/{template/project → lib/antwort/server}/views/server.erb +0 -0
  37. data/lib/antwort/version.rb +1 -1
  38. data/spec/builder/builder_spec.rb +17 -22
  39. data/spec/builder/email_spec.rb +11 -12
  40. data/spec/builder/flattener_spec.rb +20 -21
  41. data/spec/builder/helpers_logic_spec.rb +69 -73
  42. data/spec/builder/partial_spec.rb +42 -51
  43. data/spec/builder/style_spec.rb +25 -34
  44. data/spec/{cli_spec.rb → cli/cli_spec.rb} +8 -9
  45. data/spec/cli/send_spec.rb +80 -0
  46. data/spec/cli/upload_spec.rb +17 -9
  47. data/spec/email/build_spec.rb +13 -0
  48. data/spec/email/collection_spec.rb +56 -0
  49. data/spec/email/data_spec.rb +40 -0
  50. data/spec/email/template_spec.rb +116 -0
  51. data/spec/fixtures/assets/css/demo/include.scss +3 -0
  52. data/spec/fixtures/assets/css/demo/inline.scss +33 -0
  53. data/spec/fixtures/assets/css/no-images/include.scss +1 -0
  54. data/spec/fixtures/assets/css/no-images/inline.scss +2 -0
  55. data/spec/fixtures/assets/css/shared/_base.scss +64 -0
  56. data/spec/fixtures/assets/css/shared/_mixins.scss +25 -0
  57. data/spec/fixtures/assets/css/shared/_reset.scss +59 -0
  58. data/spec/fixtures/assets/css/shared/_vars.scss +12 -0
  59. data/spec/fixtures/assets/css/shared/include.scss +23 -0
  60. data/spec/fixtures/assets/css/shared/inline.scss +9 -0
  61. data/spec/fixtures/build/demo-20160101/demo.html +177 -0
  62. data/spec/fixtures/build/demo-20160101/source/demo.html +118 -0
  63. data/spec/fixtures/build/demo-20160101/source/include.css +58 -0
  64. data/spec/fixtures/build/demo-20160101/source/inline.css +83 -0
  65. data/spec/fixtures/build/demo-20160102/demo.html +177 -0
  66. data/spec/fixtures/build/demo-20160102/source/demo.html +118 -0
  67. data/spec/fixtures/build/demo-20160102/source/include.css +58 -0
  68. data/spec/fixtures/build/demo-20160102/source/inline.css +83 -0
  69. data/{template/project/data/.empty_directory → spec/fixtures/emails/1-demo/_bar.erb} +0 -0
  70. data/spec/fixtures/emails/1-demo/_foo.erb +0 -0
  71. data/spec/fixtures/emails/1-demo/index.html.erb +2 -2
  72. data/spec/fixtures/emails/4-custom-layout/index.html.erb +6 -0
  73. data/spec/fixtures/emails/4-custom-layout/layout.erb +5 -0
  74. data/spec/fixtures/emails/demo/index.html.erb +4 -0
  75. data/spec/fixtures/emails/no-images/index.html.erb +4 -0
  76. data/spec/fixtures/emails/shared/_foo.erb +0 -0
  77. data/spec/fixtures/{views → emails/shared}/layout.erb +0 -0
  78. data/spec/helpers/file_helper_spec.rb +30 -0
  79. data/spec/server/server_spec.rb +57 -0
  80. data/spec/spec_helper.rb +18 -7
  81. data/template/project/Gemfile.tt +1 -7
  82. metadata +92 -42
  83. data/lib/antwort/builder/helpers/logic.rb +0 -82
  84. data/lib/antwort/cli/helpers.rb +0 -44
  85. data/lib/antwort/server/helpers.rb +0 -67
  86. data/lib/antwort/server/markup.rb +0 -39
  87. data/spec/cli/helpers_spec.rb +0 -60
  88. data/spec/fixtures/build/demo-123456/build.html +0 -7
  89. data/spec/fixtures/build/demo-123457/build.html +0 -7
  90. data/spec/fixtures/build/demo-bar-123/build.html +0 -7
  91. data/spec/fixtures/views/404.html.erb +0 -1
  92. data/spec/fixtures/views/index.html.erb +0 -14
  93. data/spec/fixtures/views/server.erb +0 -5
  94. data/spec/server_spec.rb +0 -54
  95. data/template/project/.ruby-version +0 -1
  96. data/template/project/data/config.yml +0 -3
@@ -1,8 +1,8 @@
1
- require 'antwort/builder/helpers/logic'
2
- require 'antwort/builder/helpers/sanitizers'
1
+ require 'antwort/helpers/logic_helper'
2
+ require 'antwort/helpers/sanitizers_helper'
3
+
3
4
  require 'antwort/builder/builder'
4
5
  require 'antwort/builder/email'
5
6
  require 'antwort/builder/partial'
6
7
  require 'antwort/builder/flattener'
7
8
  require 'antwort/builder/style'
8
- require 'antwort/cli/helpers'
data/lib/antwort/cli.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'thor'
3
3
 
4
- require 'antwort/cli/helpers'
5
4
  require 'antwort/cli/cli'
6
5
  require 'antwort/cli/send'
7
6
  require 'antwort/cli/upload'
@@ -1,7 +1,7 @@
1
1
  module Antwort
2
2
  class CLI < Thor
3
3
  include Thor::Actions
4
- include Antwort::CLIHelpers
4
+ include Antwort::FileHelpers
5
5
 
6
6
  class_option :version, type: :boolean
7
7
 
@@ -53,20 +53,19 @@ module Antwort
53
53
  desc 'list', 'Lists all emails in the ./emails directory by id'
54
54
  method_option aliases: 'l'
55
55
  def list
56
- list_folders('./emails').each { |e| puts "- #{e}" }
56
+ EmailCollection.new.list.each do |email_id|
57
+ puts "- #{email_id}"
58
+ end
57
59
  end
58
60
 
59
61
  #-- upload
60
62
 
61
63
  desc 'upload', 'Uploads email assets to AWS S3'
62
- method_option :force,
63
- type: :boolean,
64
- default: false,
65
- aliases: '-f',
66
- desc: 'Overwrites existing files on the server'
67
64
  method_option aliases: 'u'
65
+
68
66
  def upload(email_id)
69
- Upload.new(email_id, options[:force]).upload
67
+ require 'antwort/cli/upload'
68
+ Upload.new(email_id).upload
70
69
  end
71
70
 
72
71
  #-- send
@@ -81,16 +80,17 @@ module Antwort
81
80
  type: :string,
82
81
  default: ENV['SEND_TO'],
83
82
  aliases: '-r',
84
- desc: 'Email address of receipient'
83
+ desc: 'Email address of receipient(s). Split multiple recipients with a comma.'
85
84
  method_option :subject,
86
85
  type: :string,
87
86
  aliases: '-s',
88
87
  desc: 'Email Subject. Defaults to <title> value if blank.'
89
88
  def send(email_id)
89
+ require 'antwort/cli/send'
90
90
  build = last_build_by_id(email_id)
91
91
 
92
92
  if build.nil?
93
- say " warning ", :yellow
93
+ say ' warning ', :yellow
94
94
  say "No build found for '#{email_id}'. Building now..."
95
95
  build(email_id)
96
96
  build = last_build_by_id(email_id)
@@ -131,48 +131,37 @@ module Antwort
131
131
  default: 'expanded',
132
132
  aliases: '-c',
133
133
  desc: 'CSS output style'
134
- def build(email_id='')
135
- require 'antwort'
136
-
137
- emails = options[:all] ? available_emails : Array.new.push(email_id)
138
-
139
- emails.each do |email_id|
140
- attrs = { email: email_id, id: create_id_from_timestamp }.merge(options)
141
- email = Antwort::EmailBuilder.new(attrs)
142
- until email.build
143
- sleep 1
144
- end
145
-
146
- if build_partials?
147
- partials = Antwort::PartialBuilder.new(attrs)
148
- sleep 1 until partials.build
149
- end
134
+ def build(email_id = '')
135
+ if email_id.empty?
136
+ say 'Error: ', :red
137
+ say 'build which email?'
138
+ list
139
+ else
140
+ to_build = options[:all] ? EmailCollection.new.list : email_id
141
+ Build.new(to_build, options).create!
142
+ show_accuracy_warning if options[:partials]
150
143
  end
151
-
152
- show_accuracy_warning if build_partials?
153
-
154
- return true
155
144
  end
156
145
 
157
146
  #-- prune
158
147
 
159
148
  desc 'prune', 'Removes all files in the ./build directory'
160
149
  method_option :force,
161
- type: :boolean,
162
- default: false,
163
- aliases: '-f',
164
- desc: 'Removes all files in the ./build directory'
150
+ type: :boolean,
151
+ default: false,
152
+ aliases: '-f',
153
+ desc: 'Removes all files in the ./build directory'
165
154
  def prune
166
155
  if confirms_prune?
167
156
  build_dir = File.expand_path('./build')
168
157
  Dir.foreach(build_dir) do |f|
169
158
  next if f.to_s[0] == '.'
170
- say " delete ", :red
159
+ say ' delete ', :red
171
160
  say "./build/#{f}/"
172
161
  FileUtils.rm_rf(File.expand_path("./build/#{f}"))
173
162
  end
174
163
  else
175
- say "prune aborted."
164
+ say 'prune aborted.'
176
165
  end
177
166
  end
178
167
 
@@ -180,22 +169,28 @@ module Antwort
180
169
 
181
170
  desc 'remove [email_id]', 'Removes an email, incl. its assets, styles and data'
182
171
  method_option :force,
183
- type: :boolean,
184
- default: false,
185
- aliases: '-f',
186
- desc: 'Removes an email, incl. its assets, styles and data'
172
+ type: :boolean,
173
+ default: false,
174
+ aliases: '-f',
175
+ desc: 'Removes an email, incl. its assets, styles and data'
187
176
  def remove(email_id)
188
177
  @email_id = email_id
178
+
179
+ unless can_remove?
180
+ say "The '#{email_id}' folder is a project dependency and cannot be removed.", :red
181
+ return
182
+ end
183
+
189
184
  if confirms_remove?
190
185
  remove_email
191
186
  else
192
- say "Remove aborted."
187
+ say 'Remove aborted.'
193
188
  end
194
189
  end
195
190
 
196
191
  #-- version
197
192
 
198
- desc 'version','Ouputs version number'
193
+ desc 'version', 'Ouputs version number'
199
194
  def version
200
195
  puts "Version: #{Antwort::VERSION}" if options[:version]
201
196
  end
@@ -205,9 +200,8 @@ module Antwort
205
200
  attr_reader :project_name, :email_id
206
201
 
207
202
  no_commands do
208
-
209
- def build_partials?
210
- options[:partials]
203
+ def can_remove?
204
+ email_id != 'shared'
211
205
  end
212
206
 
213
207
  def confirms_prune?
@@ -223,7 +217,7 @@ module Antwort
223
217
  File.join('assets', 'css', email_directory)
224
218
  directory 'email/images',
225
219
  File.join('assets', 'images', email_directory)
226
- create_file File.join('data', "#{email_id}.yml")
220
+ create_file!(content: '', path: File.join('data', "#{email_id}.yml"))
227
221
  copy_file 'email/email.html.erb',
228
222
  File.join('emails', email_directory, 'index.html.erb')
229
223
  end
@@ -252,11 +246,11 @@ module Antwort
252
246
  end
253
247
 
254
248
  def email_directory
255
- email_id.downcase.gsub(/([^A-Za-z0-9_\/-]+)|(--)/, '')
249
+ email_id.downcase.gsub(%r{([^A-Za-z0-9_/-]+)|(--)}, '')
256
250
  end
257
251
 
258
252
  def project_directory
259
- project_name.downcase.gsub(/([^A-Za-z0-9_\/-]+)|(--)/, '')
253
+ project_name.downcase.gsub(%r{([^A-Za-z0-9_/-]+)|(--)}, '')
260
254
  end
261
255
 
262
256
  def create_id_from_timestamp
@@ -5,7 +5,7 @@ module Antwort
5
5
  class CLI
6
6
  class Send
7
7
  include Thor::Shell
8
- attr_reader :build_id, :sender, :recipient, :subject
8
+ attr_reader :build_id, :sender, :recipient, :subject, :html_body, :mail
9
9
 
10
10
  Mail.defaults do
11
11
  delivery_method :smtp,
@@ -18,15 +18,16 @@ module Antwort
18
18
  return_response: true
19
19
  end
20
20
 
21
- def initialize(build_id, options={})
21
+ def initialize(build_id, options = {})
22
22
  @build_id = build_id
23
- @html_body = File.open("#{build_folder}/#{template_name}.html").read
23
+ @html_body = File.open("build/#{build_id}/#{template_name}.html").read
24
24
 
25
- @recipient = options[:recipient]
25
+ @recipient = (options[:recipient] || ENV['SEND_TO']).split(',')
26
26
  @sender = options[:from] || ENV['SEND_FROM']
27
- @subject = options[:subject] || "[Test] " << extract_title(@html_body)
27
+ @subject = options[:subject] || '[Test] ' << extract_title(@html_body)
28
28
  end
29
29
 
30
+ # rubocop:disable Metrics/MethodLength
30
31
  def send
31
32
  # because scope changes inside mail DSL
32
33
  mail_from = @sender
@@ -34,7 +35,7 @@ module Antwort
34
35
  mail_subject = @subject
35
36
 
36
37
  # setup email
37
- mail = Mail.new do
38
+ @mail = Mail.new do
38
39
  from mail_from
39
40
  to mail_to
40
41
  subject mail_subject
@@ -44,13 +45,13 @@ module Antwort
44
45
  end
45
46
 
46
47
  html_part do
47
- content_type 'text/html; charset=UTF-8'
48
+ content_type 'text/html'
48
49
  body @html_body
49
50
  end
50
51
  end
51
52
 
52
53
  # send email
53
- if mail.deliver!
54
+ if @mail.deliver!
54
55
  say "Sent Email \"#{template_name}\" at #{Time.now.strftime('%d.%m.%Y %H:%M')}", :green
55
56
  say " to: #{@recipient}"
56
57
  say " subject: #{@subject}"
@@ -59,6 +60,7 @@ module Antwort
59
60
  say "Error sending #{build_id}/#{template_name} at #{Time.now.strftime('%d.%m.%Y %H:%M')}", :red
60
61
  end
61
62
  end
63
+ # rubocop:enable Metrics/MethodLength
62
64
 
63
65
  private
64
66
 
@@ -66,14 +68,9 @@ module Antwort
66
68
  @build_id.split('-')[0...-1].join('-') # removes timestamp ID
67
69
  end
68
70
 
69
- def build_folder
70
- "build/#{@build_id}"
71
- end
72
-
73
71
  def extract_title(body = '')
74
72
  body.scan(%r{<title>(.*?)</title>}).first.first
75
73
  end
76
-
77
74
  end
78
75
  end
79
- end
76
+ end
@@ -4,39 +4,37 @@ module Antwort
4
4
  class CLI
5
5
  class Upload < Thor
6
6
  include Thor::Actions
7
- include Antwort::CLIHelpers
8
- attr_reader :email_id
7
+ include Antwort::FileHelpers
8
+
9
+ attr_reader :email_id, :template
10
+
11
+ def initialize(email_id)
12
+ @email_id = email_id
13
+ @template = Antwort::EmailTemplate.new(email_id)
9
14
 
10
- def initialize(email_id, force = false)
11
- @force = force
12
- @email_id = email_id
13
- @images_dir = images_dir(email_id)
14
15
  check_credentials
15
16
  end
16
17
 
17
18
  no_commands do
18
-
19
19
  def check_credentials
20
20
  failed = false
21
- vars = ['ASSET_SERVER', 'AWS_BUCKET', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY']
21
+ vars = %w(ASSET_SERVER AWS_BUCKET AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY)
22
22
  vars.each do |var|
23
- if ENV[var].nil?
24
- say "Error ", :red
25
- say "#{var} not set."
26
- failed = true
27
- end
23
+ next unless ENV[var].nil?
24
+ say 'Error ', :red
25
+ say "#{var} not set."
26
+ failed = true
28
27
  end
29
28
 
30
- if failed
31
- abort "Please fix your .env config file and try again."
32
- end
29
+ abort 'Please fix your .env config file and try again.' if failed
33
30
  end
34
31
 
35
32
  def upload
36
- count = count_files @images_dir
37
- abort "No images in #{@images_dir} to upload." if count === 0
33
+ unless @template.has_images?
34
+ abort "#{@template.name} has no images to upload."
35
+ end
38
36
 
39
- if confirms_upload?(count)
37
+ if confirms_upload?
40
38
  do_upload
41
39
  else
42
40
  say 'Upload aborted. ', :red
@@ -44,21 +42,21 @@ module Antwort
44
42
  end
45
43
  end
46
44
 
47
- def confirms_upload?(count)
48
- yes?("Upload #{count} images and overwrite '#{email_id}' folder on assets server? (y/n)")
45
+ def confirms_upload?
46
+ yes?("Upload #{@template.images.length} images and overwrite '#{@template.name}' folder on assets server? (y/n)")
49
47
  end
50
48
 
51
49
  def do_upload
52
50
  clean_directory!
53
- Dir.foreach(@images_dir) do |f|
54
- next if f.to_s[0] == '.'
51
+
52
+ @template.images.each do |f|
55
53
  directory.files.create(
56
- key: "#{email_id}/#{f}",
57
- body: File.open(File.join(@images_dir, f)),
54
+ key: upload_path(f),
55
+ body: File.open(@template.image_path(f)),
58
56
  public: true
59
57
  )
60
58
  say ' create ', :green
61
- say "#{ENV['ASSET_SERVER']}/#{email_id}/#{f}"
59
+ say "#{ENV['ASSET_SERVER']}/#{upload_path(f)}"
62
60
  end
63
61
  end
64
62
 
@@ -76,13 +74,16 @@ module Antwort
76
74
  return @directory if defined?(@directory)
77
75
 
78
76
  @directory ||=
79
- connection.directories.get(ENV['AWS_BUCKET'], prefix: email_id)
77
+ connection.directories.get(ENV['AWS_BUCKET'], prefix: @template.name)
78
+ end
79
+
80
+ def upload_path(file)
81
+ "#{@template.name}/#{file}"
80
82
  end
81
83
 
82
84
  def clean_directory!
83
85
  directory.files.each(&:destroy)
84
86
  end
85
-
86
87
  end
87
88
  end
88
89
  end
@@ -0,0 +1,4 @@
1
+ require 'antwort/email/build'
2
+ require 'antwort/email/collection'
3
+ require 'antwort/email/template'
4
+ require 'antwort/email/data'
@@ -0,0 +1,41 @@
1
+ require 'antwort/builders'
2
+
3
+ module Antwort
4
+ class Build
5
+ include Antwort::Helpers
6
+ include Antwort::FileHelpers
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(emails, opts = {})
11
+ @id = new_timestamp_id
12
+ @emails = emails.is_a?(Array) ? emails : [].push(emails)
13
+ @options = opts
14
+ end
15
+
16
+ def create!
17
+ @emails.each do |email_id|
18
+ attrs = { email: email_id, id: @id }.merge(@options)
19
+ build_email(attrs)
20
+ build_partials(attrs) if @options[:partials]
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def build_email(attrs)
27
+ email = Antwort::EmailBuilder.new(attrs)
28
+ sleep 1 until email.build!
29
+ end
30
+
31
+ def build_partials(attrs)
32
+ partials = Antwort::PartialBuilder.new(attrs)
33
+ sleep 1 until partials.build!
34
+ end
35
+
36
+ def new_timestamp_id
37
+ stamp = Time.now.to_s
38
+ stamp.split(' ')[0..1].join.gsub(/(-|:)/, '')
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ module Antwort
2
+ class EmailCollection
3
+ include Antwort::Helpers
4
+ include Antwort::FileHelpers
5
+
6
+ attr_reader :templates, :list
7
+
8
+ def initialize
9
+ @templates = []
10
+ @list = []
11
+
12
+ dir = './emails'
13
+ find_templates(dir) if Dir.exist? dir
14
+ end
15
+
16
+ def empty?
17
+ @templates.empty?
18
+ end
19
+
20
+ def total
21
+ @templates.length
22
+ end
23
+
24
+ private
25
+
26
+ def find_templates(dir)
27
+ folders = Dir.entries(dir)
28
+ folders = filter_templates(folders)
29
+
30
+ folders.each do |f|
31
+ @list.push f
32
+ @templates.push Antwort::EmailTemplate.new(f)
33
+ end
34
+ end
35
+
36
+ def filter_templates(arry = [])
37
+ arry.delete_if { |name| name.to_s[0] == '.' }
38
+ arry.delete_if { |name| name.to_s == 'shared' }
39
+ end
40
+ end
41
+ end