bootstrap-email 0.3.3 → 1.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/bootstrap-email +2 -0
  4. data/core/bootstrap-email.scss +29 -18
  5. data/core/bootstrap-head.scss +143 -0
  6. data/core/layout.html.erb +11 -0
  7. data/core/scss/_colors.scss +161 -0
  8. data/core/scss/_functions.scss +29 -0
  9. data/core/scss/_helper_groups.scss +21 -0
  10. data/core/{sass → scss}/_reboot_email.scss +7 -22
  11. data/core/scss/_selectors_for_utils.scss +124 -0
  12. data/core/scss/_utilities.scss +145 -0
  13. data/core/scss/_variables.scss +52 -0
  14. data/core/{sass → scss/components}/_alert.scss +0 -1
  15. data/core/{sass → scss/components}/_badge.scss +8 -15
  16. data/core/{sass → scss/components}/_button.scss +21 -17
  17. data/core/{sass → scss/components}/_card.scss +3 -2
  18. data/core/{sass → scss/components}/_container.scss +0 -0
  19. data/core/scss/components/_grid.scss +38 -0
  20. data/core/scss/components/_hr.scss +8 -0
  21. data/core/{sass → scss/components}/_image.scss +2 -1
  22. data/core/{sass → scss/components}/_preview.scss +0 -0
  23. data/core/scss/components/_stack.scss +33 -0
  24. data/core/{sass → scss/components}/_table.scss +0 -0
  25. data/core/scss/utilities/_background.scss +5 -0
  26. data/core/scss/utilities/_border-radius.scss +21 -0
  27. data/core/scss/utilities/_border.scss +13 -0
  28. data/core/scss/utilities/_color.scss +9 -0
  29. data/core/scss/utilities/_display.scss +7 -0
  30. data/core/scss/utilities/_sizing.scss +31 -0
  31. data/core/scss/utilities/_spacing.scss +18 -0
  32. data/core/scss/utilities/_text-decoration.scss +9 -0
  33. data/core/scss/utilities/_typography.scss +48 -0
  34. data/core/scss/utilities/_valign.scss +5 -0
  35. data/core/templates/body.html.erb +1 -1
  36. data/core/templates/container.html.erb +3 -3
  37. data/core/templates/table-left.html.erb +1 -1
  38. data/core/templates/table-to-tbody.html.erb +5 -0
  39. data/core/templates/table-to-tr.html.erb +7 -0
  40. data/core/templates/table.html.erb +1 -1
  41. data/core/templates/td.html.erb +3 -0
  42. data/core/templates/tr.html.erb +5 -0
  43. data/lib/bootstrap-email.rb +20 -222
  44. data/lib/bootstrap-email/bootstrap_email_cli.rb +92 -0
  45. data/lib/bootstrap-email/compiler.rb +98 -0
  46. data/lib/bootstrap-email/components/alert.rb +11 -0
  47. data/lib/bootstrap-email/components/align.rb +22 -0
  48. data/lib/bootstrap-email/components/badge.rb +11 -0
  49. data/lib/bootstrap-email/components/base.rb +54 -0
  50. data/lib/bootstrap-email/components/block.rb +13 -0
  51. data/lib/bootstrap-email/components/body.rb +10 -0
  52. data/lib/bootstrap-email/components/button.rb +11 -0
  53. data/lib/bootstrap-email/components/card.rb +14 -0
  54. data/lib/bootstrap-email/components/color.rb +13 -0
  55. data/lib/bootstrap-email/components/container.rb +14 -0
  56. data/lib/bootstrap-email/components/force_encoding.rb +16 -0
  57. data/lib/bootstrap-email/components/grid.rb +14 -0
  58. data/lib/bootstrap-email/components/head_style.rb +33 -0
  59. data/lib/bootstrap-email/components/hr.rb +12 -0
  60. data/lib/bootstrap-email/components/margin.rb +22 -0
  61. data/lib/bootstrap-email/components/padding.rb +16 -0
  62. data/lib/bootstrap-email/components/paragraph.rb +13 -0
  63. data/lib/bootstrap-email/components/preview_text.rb +18 -0
  64. data/lib/bootstrap-email/components/spacer.rb +11 -0
  65. data/lib/bootstrap-email/components/spacing.rb +17 -0
  66. data/lib/bootstrap-email/components/stack.rb +30 -0
  67. data/lib/bootstrap-email/components/table.rb +13 -0
  68. data/lib/bootstrap-email/components/version_comment.rb +15 -0
  69. data/lib/bootstrap-email/erb.rb +9 -0
  70. data/lib/bootstrap-email/initialize.rb +1 -0
  71. data/lib/bootstrap-email/rails/action_mailer.rb +11 -0
  72. data/lib/bootstrap-email/{engine.rb → rails/engine.rb} +0 -0
  73. data/lib/bootstrap-email/sass_cache.rb +67 -0
  74. data/lib/bootstrap-email/version.rb +3 -5
  75. metadata +82 -59
  76. data/core/head.scss +0 -269
  77. data/core/sass/_border.scss +0 -73
  78. data/core/sass/_color.scss +0 -33
  79. data/core/sass/_display.scss +0 -7
  80. data/core/sass/_functions.scss +0 -14
  81. data/core/sass/_grid.scss +0 -131
  82. data/core/sass/_hr.scss +0 -13
  83. data/core/sass/_spacing.scss +0 -100
  84. data/core/sass/_typography.scss +0 -50
  85. data/core/sass/_variables.scss +0 -150
  86. data/core/template.html.erb +0 -11
  87. data/core/templates/align-center.html.erb +0 -9
  88. data/core/templates/align-left.html.erb +0 -9
  89. data/core/templates/align-right.html.erb +0 -9
  90. data/core/templates/col.html.erb +0 -3
  91. data/core/templates/hr.html.erb +0 -9
  92. data/core/templates/row.html.erb +0 -7
  93. data/lib/assets/stylesheets/bootstrap-email.scss +0 -1
  94. data/lib/bootstrap-email/action_mailer.rb +0 -12
  95. data/lib/bootstrap-email/premailer_railtie.rb +0 -5
@@ -0,0 +1,12 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Hr < Base
4
+ def build
5
+ each_node('hr') do |node|
6
+ default_margin = margin?(node) ? '' : 'my-5'
7
+ node.replace(template('table', classes: "#{default_margin} hr #{node['class']}", contents: ''))
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Margin < Base
4
+ def build
5
+ each_node('*[class*=my-], *[class*=mt-], *[class*=mb-]') do |node|
6
+ top_class = node['class'][/m[ty]{1}-(lg-)?(\d+)/]
7
+ bottom_class = node['class'][/m[by]{1}-(lg-)?(\d+)/]
8
+ node['class'] = node['class'].gsub(/(m[tby]{1}-(lg-)?\d+)/, '')
9
+ html = ''
10
+ if top_class
11
+ html += template('div', classes: "s-#{top_class.gsub(/m[ty]{1}-/, '')}", contents: nil)
12
+ end
13
+ html += node.to_html
14
+ if bottom_class
15
+ html += template('div', classes: "s-#{bottom_class.gsub(/m[by]{1}-/, '')}", contents: nil)
16
+ end
17
+ node.replace(html)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Padding < Base
4
+ def build
5
+ each_node('*[class*=p-], *[class*=pt-], *[class*=pr-], *[class*=pb-], *[class*=pl-], *[class*=px-], *[class*=py-]') do |node|
6
+ next if ['table', 'td', 'a'].include?(node.name)
7
+
8
+ padding_regex = /(p[trblxy]?-\d+)/
9
+ classes = node['class'].scan(padding_regex).join(' ')
10
+ node['class'] = node['class'].gsub(padding_regex, '')
11
+ node.replace(template('table', classes: classes, contents: node.to_html))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Paragraph < Base
4
+ def build
5
+ each_node('p') do |node|
6
+ next if margin?(node)
7
+
8
+ add_class(node, 'mb-4')
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class PreviewText < Base
4
+ def build
5
+ preview_node = doc.at_css('preview')
6
+ return if preview_node.nil?
7
+
8
+ # apply spacing after the text max of 100 characters so it doesn't show body text
9
+ preview_node.inner_html += '&nbsp;' * [(100 - preview_node.content.length.to_i), 0].max
10
+ node = template('div', classes: 'preview', contents: preview_node.content)
11
+ preview_node.remove
12
+
13
+ body = doc.at_css('body')
14
+ body.prepend_child(node)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Spacer < Base
4
+ def build
5
+ each_node('*[class*=s-]') do |node|
6
+ node.replace(template('table', classes: "#{node['class']} w-full", contents: '&nbsp;'))
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Spacing < Base
4
+ def build
5
+ each_node('*[class*=space-y-]') do |node|
6
+ spacer = node['class'].scan(/space-y-((lg-)?\d+)/)[0][0]
7
+ # get all direct children except the first
8
+ node.xpath('./*[position() < last()] | ./tbody/tr/td/*[position() < last()]').each do |child|
9
+ next if margin_bottom?(child)
10
+
11
+ add_class(child, "mb-#{spacer}")
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Stack < Base
4
+ def build
5
+ stack_x
6
+ stack_y
7
+ end
8
+
9
+ def stack_x
10
+ each_node('.stack-x') do |node|
11
+ html = ''
12
+ node.xpath('./*').each do |child|
13
+ html += template('td', classes: 'stack-cell', contents: child.to_html)
14
+ end
15
+ node.replace(template('table-to-tr', classes: node['class'], contents: html))
16
+ end
17
+ end
18
+
19
+ def stack_y
20
+ each_node('.stack-y') do |node|
21
+ html = ''
22
+ node.xpath('./*').each do |child|
23
+ html += template('tr', classes: 'stack-cell', contents: child.to_html)
24
+ end
25
+ node.replace(template('table-to-tbody', classes: node['class'], contents: html))
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class Table < Base
4
+ def build
5
+ each_node('table') do |node|
6
+ node['border'] = 0
7
+ node['cellpadding'] = 0
8
+ node['cellspacing'] = 0
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module BootstrapEmail
2
+ module Component
3
+ class VersionComment < Base
4
+ def build
5
+ doc.at_css('head').prepend_child(bootstrap_email_comment)
6
+ end
7
+
8
+ private
9
+
10
+ def bootstrap_email_comment
11
+ "\n <!-- Compiled with Bootstrap Email version: #{BootstrapEmail::VERSION} -->"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module BootstrapEmail
2
+ class Erb
3
+ def self.template(path, locals_hash = {})
4
+ namespace = OpenStruct.new(locals_hash)
5
+ template_html = File.read(path)
6
+ ERB.new(template_html).result(namespace.instance_eval { binding })
7
+ end
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ Premailer::Adapter.use = :nokogiri_fast
@@ -0,0 +1,11 @@
1
+ ActiveSupport.on_load(:action_mailer, {yield: true}) do |action_mailer|
2
+ action_mailer.class_eval do # To support Rails less than 6
3
+ # sit in the middle and compile the html
4
+ def bootstrap_mail(*args, &block)
5
+ bootstrap = BootstrapEmail::Compiler.new(mail(*args, &block), type: :rails)
6
+ bootstrap.perform_full_compile
7
+ end
8
+ alias bootstrap_email bootstrap_mail
9
+ alias make_bootstrap_mail bootstrap_mail
10
+ end
11
+ end
File without changes
@@ -0,0 +1,67 @@
1
+ module BootstrapEmail
2
+ class SassCache
3
+ CACHE_DIR = File.expand_path('../../.sass-cache', __dir__)
4
+ SASS_DIR = File.expand_path('../../core', __dir__)
5
+
6
+ def self.compile(type, config_path: nil, style: :compressed)
7
+ new(type, config_path, style).compile
8
+ end
9
+
10
+ attr_accessor :type, :style, :file_path, :config_file, :checksum
11
+
12
+ def initialize(type, config_path, style)
13
+ self.type = type
14
+ self.style = style
15
+ self.file_path = "#{SASS_DIR}/#{type}"
16
+ self.config_file = load_config(config_path)
17
+ self.checksum = checksum_files
18
+ end
19
+
20
+ def compile
21
+ cache_path = "#{CACHE_DIR}/#{checksum}/#{type}.css"
22
+ unless cached?(cache_path)
23
+ compile_and_cache_scss(cache_path)
24
+ end
25
+ File.read(cache_path)
26
+ end
27
+
28
+ private
29
+
30
+ def load_config(config_path)
31
+ lookup_locations = ["#{type}.config.scss", "app/assets/stylesheets/#{type}.config.scss"]
32
+ locations = lookup_locations.select { |location| File.exist?(File.expand_path(location, Dir.pwd)) }
33
+ if config_path && File.exist?(config_path)
34
+ # check if custom config was passed in
35
+ replace_config(File.read(config_path))
36
+ elsif locations.any?
37
+ # look for common lookup locations of config
38
+ replace_config(File.read(File.expand_path(locations.first, Dir.pwd)))
39
+ end
40
+ end
41
+
42
+ def replace_config(config_file)
43
+ config_file.gsub("//= @import #{type};", "@import '#{file_path}';")
44
+ end
45
+
46
+ def checksum_files
47
+ checksums = config_file.nil? ? [] : [Digest::SHA1.hexdigest(config_file)]
48
+ Dir.glob('../../core/**/*.scss', base: __dir__).each do |path|
49
+ checksums << Digest::SHA1.file(File.expand_path(path, __dir__)).hexdigest
50
+ end
51
+ Digest::SHA1.hexdigest(checksums.join)
52
+ end
53
+
54
+ def cached?(cache_path)
55
+ File.file?(cache_path)
56
+ end
57
+
58
+ def compile_and_cache_scss(cache_path)
59
+ file = config_file || File.read("#{file_path}.scss")
60
+ css = SassC::Engine.new(file, style: style).render
61
+ Dir.mkdir(CACHE_DIR) unless File.directory?(CACHE_DIR)
62
+ Dir.mkdir("#{CACHE_DIR}/#{checksum}") unless File.directory?("#{CACHE_DIR}/#{checksum}")
63
+ File.write(cache_path, css)
64
+ puts "New css file cached for #{type}"
65
+ end
66
+ end
67
+ end
@@ -1,7 +1,5 @@
1
1
  module BootstrapEmail
2
- module Rails
3
- VERSION = File.read(
4
- File.expand_path('../../VERSION', __dir__)
5
- ).strip
6
- end
2
+ VERSION = File.read(
3
+ File.expand_path('../../VERSION', __dir__)
4
+ ).strip.freeze
7
5
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootstrap-email
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 1.0.0.alpha2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stuart Yamartino
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-06 00:00:00.000000000 Z
11
+ date: 2021-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: actionmailer
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '3'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '3'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: nokogiri
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -39,77 +25,113 @@ dependencies:
39
25
  - !ruby/object:Gem::Version
40
26
  version: '1.6'
41
27
  - !ruby/object:Gem::Dependency
42
- name: premailer-rails
28
+ name: premailer
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '1.9'
33
+ version: '1.7'
48
34
  type: :runtime
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
- version: '1.9'
40
+ version: '1.7'
55
41
  - !ruby/object:Gem::Dependency
56
- name: rails
42
+ name: sassc
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - ">="
45
+ - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '3'
47
+ version: '2.0'
62
48
  type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - ">="
52
+ - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '3'
54
+ version: '2.0'
69
55
  description:
70
56
  email: stu@stuyam.com
71
- executables: []
57
+ executables:
58
+ - bootstrap-email
72
59
  extensions: []
73
60
  extra_rdoc_files: []
74
61
  files:
75
62
  - VERSION
63
+ - bin/bootstrap-email
76
64
  - core/bootstrap-email.scss
77
- - core/head.scss
78
- - core/sass/_alert.scss
79
- - core/sass/_badge.scss
80
- - core/sass/_border.scss
81
- - core/sass/_button.scss
82
- - core/sass/_card.scss
83
- - core/sass/_color.scss
84
- - core/sass/_container.scss
85
- - core/sass/_display.scss
86
- - core/sass/_functions.scss
87
- - core/sass/_grid.scss
88
- - core/sass/_hr.scss
89
- - core/sass/_image.scss
90
- - core/sass/_preview.scss
91
- - core/sass/_reboot_email.scss
92
- - core/sass/_spacing.scss
93
- - core/sass/_table.scss
94
- - core/sass/_typography.scss
95
- - core/sass/_variables.scss
96
- - core/template.html.erb
97
- - core/templates/align-center.html.erb
98
- - core/templates/align-left.html.erb
99
- - core/templates/align-right.html.erb
65
+ - core/bootstrap-head.scss
66
+ - core/layout.html.erb
67
+ - core/scss/_colors.scss
68
+ - core/scss/_functions.scss
69
+ - core/scss/_helper_groups.scss
70
+ - core/scss/_reboot_email.scss
71
+ - core/scss/_selectors_for_utils.scss
72
+ - core/scss/_utilities.scss
73
+ - core/scss/_variables.scss
74
+ - core/scss/components/_alert.scss
75
+ - core/scss/components/_badge.scss
76
+ - core/scss/components/_button.scss
77
+ - core/scss/components/_card.scss
78
+ - core/scss/components/_container.scss
79
+ - core/scss/components/_grid.scss
80
+ - core/scss/components/_hr.scss
81
+ - core/scss/components/_image.scss
82
+ - core/scss/components/_preview.scss
83
+ - core/scss/components/_stack.scss
84
+ - core/scss/components/_table.scss
85
+ - core/scss/utilities/_background.scss
86
+ - core/scss/utilities/_border-radius.scss
87
+ - core/scss/utilities/_border.scss
88
+ - core/scss/utilities/_color.scss
89
+ - core/scss/utilities/_display.scss
90
+ - core/scss/utilities/_sizing.scss
91
+ - core/scss/utilities/_spacing.scss
92
+ - core/scss/utilities/_text-decoration.scss
93
+ - core/scss/utilities/_typography.scss
94
+ - core/scss/utilities/_valign.scss
100
95
  - core/templates/body.html.erb
101
- - core/templates/col.html.erb
102
96
  - core/templates/container.html.erb
103
97
  - core/templates/div.html.erb
104
- - core/templates/hr.html.erb
105
- - core/templates/row.html.erb
106
98
  - core/templates/table-left.html.erb
99
+ - core/templates/table-to-tbody.html.erb
100
+ - core/templates/table-to-tr.html.erb
107
101
  - core/templates/table.html.erb
108
- - lib/assets/stylesheets/bootstrap-email.scss
102
+ - core/templates/td.html.erb
103
+ - core/templates/tr.html.erb
109
104
  - lib/bootstrap-email.rb
110
- - lib/bootstrap-email/action_mailer.rb
111
- - lib/bootstrap-email/engine.rb
112
- - lib/bootstrap-email/premailer_railtie.rb
105
+ - lib/bootstrap-email/bootstrap_email_cli.rb
106
+ - lib/bootstrap-email/compiler.rb
107
+ - lib/bootstrap-email/components/alert.rb
108
+ - lib/bootstrap-email/components/align.rb
109
+ - lib/bootstrap-email/components/badge.rb
110
+ - lib/bootstrap-email/components/base.rb
111
+ - lib/bootstrap-email/components/block.rb
112
+ - lib/bootstrap-email/components/body.rb
113
+ - lib/bootstrap-email/components/button.rb
114
+ - lib/bootstrap-email/components/card.rb
115
+ - lib/bootstrap-email/components/color.rb
116
+ - lib/bootstrap-email/components/container.rb
117
+ - lib/bootstrap-email/components/force_encoding.rb
118
+ - lib/bootstrap-email/components/grid.rb
119
+ - lib/bootstrap-email/components/head_style.rb
120
+ - lib/bootstrap-email/components/hr.rb
121
+ - lib/bootstrap-email/components/margin.rb
122
+ - lib/bootstrap-email/components/padding.rb
123
+ - lib/bootstrap-email/components/paragraph.rb
124
+ - lib/bootstrap-email/components/preview_text.rb
125
+ - lib/bootstrap-email/components/spacer.rb
126
+ - lib/bootstrap-email/components/spacing.rb
127
+ - lib/bootstrap-email/components/stack.rb
128
+ - lib/bootstrap-email/components/table.rb
129
+ - lib/bootstrap-email/components/version_comment.rb
130
+ - lib/bootstrap-email/erb.rb
131
+ - lib/bootstrap-email/initialize.rb
132
+ - lib/bootstrap-email/rails/action_mailer.rb
133
+ - lib/bootstrap-email/rails/engine.rb
134
+ - lib/bootstrap-email/sass_cache.rb
113
135
  - lib/bootstrap-email/version.rb
114
136
  homepage: https://bootstrapemail.com
115
137
  licenses:
@@ -126,13 +148,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
148
  version: '2.0'
127
149
  required_rubygems_version: !ruby/object:Gem::Requirement
128
150
  requirements:
129
- - - ">="
151
+ - - ">"
130
152
  - !ruby/object:Gem::Version
131
- version: '0'
153
+ version: 1.3.1
132
154
  requirements: []
133
155
  rubygems_version: 3.0.3
134
156
  signing_key:
135
157
  specification_version: 4
136
- summary: Bootstrap 4 stylesheet, compiler, and inliner for responsive and consistent
137
- emails with the Bootstrap syntax you know and love.
158
+ summary: 'Bootstrap 5 stylesheet, compiler, and inliner for responsive and consistent
159
+ emails with the Bootstrap syntax you know and love. Support: command line, ruby,
160
+ rails'
138
161
  test_files: []