inky-rb 1.3.7.0 → 1.3.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Gemfile.lock +119 -8
  4. data/README.md +27 -2
  5. data/inky.gemspec +6 -2
  6. data/lib/generators/inky/install_generator.rb +3 -2
  7. data/lib/generators/inky/templates/mailer_layout.html.erb +2 -2
  8. data/lib/generators/inky/templates/mailer_layout.html.haml +13 -0
  9. data/lib/generators/inky/templates/mailer_layout.html.slim +15 -0
  10. data/lib/inky.rb +9 -8
  11. data/lib/inky/component_factory.rb +139 -0
  12. data/lib/inky/configuration.rb +31 -0
  13. data/lib/inky/rails/engine.rb +1 -1
  14. data/lib/inky/rails/template_handler.rb +24 -8
  15. data/lib/inky/rails/version.rb +1 -1
  16. data/spec/configuration_spec.rb +38 -0
  17. data/spec/inky_spec.rb +19 -0
  18. data/spec/spec_helper.rb +5 -5
  19. data/spec/test_app/Rakefile +6 -0
  20. data/spec/test_app/app/controllers/application_controller.rb +3 -0
  21. data/spec/test_app/app/controllers/inky_controller.rb +9 -0
  22. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  23. data/spec/test_app/app/mailers/application_mailer.rb +4 -0
  24. data/spec/test_app/app/views/inky/_inky_partial.html.inky +1 -0
  25. data/spec/test_app/app/views/inky/explicit_builder.html.inky-builder +1 -0
  26. data/spec/test_app/app/views/inky/explicit_slim.html.inky-slim +2 -0
  27. data/spec/test_app/app/views/inky/layout.html.erb +1 -0
  28. data/spec/test_app/app/views/inky/non_inky.html.erb +3 -0
  29. data/spec/test_app/app/views/inky/simple.html.inky +1 -0
  30. data/spec/test_app/app/views/inky/slim.html.inky +2 -0
  31. data/spec/test_app/app/views/layouts/application.html.erb +4 -0
  32. data/spec/test_app/app/views/layouts/inky_layout.html.inky +11 -0
  33. data/spec/test_app/config.ru +5 -0
  34. data/spec/test_app/config/application.rb +22 -0
  35. data/spec/test_app/config/boot.rb +5 -0
  36. data/spec/test_app/config/cable.yml +9 -0
  37. data/spec/test_app/config/database.yml +25 -0
  38. data/spec/test_app/config/environment.rb +5 -0
  39. data/spec/test_app/config/environments/development.rb +54 -0
  40. data/spec/test_app/config/environments/production.rb +86 -0
  41. data/spec/test_app/config/environments/test.rb +44 -0
  42. data/spec/test_app/config/initializers/application_controller_renderer.rb +6 -0
  43. data/spec/test_app/config/initializers/assets.rb +11 -0
  44. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  45. data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
  46. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  47. data/spec/test_app/config/initializers/inflections.rb +16 -0
  48. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  49. data/spec/test_app/config/initializers/new_framework_defaults.rb +24 -0
  50. data/spec/test_app/config/initializers/secret_token.rb +1 -0
  51. data/spec/test_app/config/initializers/session_store.rb +3 -0
  52. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  53. data/spec/test_app/config/locales/en.yml +23 -0
  54. data/spec/test_app/config/puma.rb +47 -0
  55. data/spec/test_app/config/routes.rb +3 -0
  56. data/spec/test_app/config/secrets.yml +22 -0
  57. data/spec/test_app/config/spring.rb +6 -0
  58. data/spec/test_app/log/.keep +0 -0
  59. data/spec/test_app/spec/features/inky_spec.rb +100 -0
  60. data/spec/test_app/spec/helper.rb +14 -0
  61. metadata +197 -5
  62. data/lib/component_factory.rb +0 -138
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a4008c29bb92a467214a676eb6dfa34286f7333
4
- data.tar.gz: 2a5e4bf73fe574f8d952aed07ad6324c7aecfdad
3
+ metadata.gz: 77c176107d789eb48bb01a6d4f6d75713f67d062
4
+ data.tar.gz: 3caf4fac84fadda5c06c8bce1a77b01a6cd443aa
5
5
  SHA512:
6
- metadata.gz: 0c67e33eb5035b4e02333642e3781eb70292bb7b1b2dcdf3fc3af6d54c8802f7085f232cbef1bb9198f3a0f048b2b926b510631aa72f8c841d95d594c14a5bef
7
- data.tar.gz: 6f1d96ad26149214322c00e279f8ffbea12c531f9d30848c1d43af5b5beec3d08187580c82e9b90f34dcd8269c945d6090d7a47f66664aa9d01b311639def83b
6
+ metadata.gz: c9d7938d47cbe1cfe38b36a04d27839a4f8af211a3657814d5f71cfd805de0402bd51c13d3a097a4d908b691ec5482da8cf26cd8432daf972243187d3885d7d2
7
+ data.tar.gz: 8cadd7b1c791284697612b8a87e2cb7b88a270f3e407c1cca6273d73c826824e33d9006ae10167438f6a113da79b2c832803ed047940a51da472d1154fb4b258
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
1
  *.swp
2
2
  pkg
3
3
  spec/_cases_output
4
+ spec/test_app/log/*.log
5
+ spec/test_app/tmp/
@@ -1,28 +1,110 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- inky-rb (1.3.7.0)
4
+ inky-rb (1.3.7.1)
5
5
  foundation_emails (~> 2)
6
6
  nokogiri
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
+ actionmailer (4.2.7.1)
12
+ actionpack (= 4.2.7.1)
13
+ actionview (= 4.2.7.1)
14
+ activejob (= 4.2.7.1)
15
+ mail (~> 2.5, >= 2.5.4)
16
+ rails-dom-testing (~> 1.0, >= 1.0.5)
17
+ actionpack (4.2.7.1)
18
+ actionview (= 4.2.7.1)
19
+ activesupport (= 4.2.7.1)
20
+ rack (~> 1.6)
21
+ rack-test (~> 0.6.2)
22
+ rails-dom-testing (~> 1.0, >= 1.0.5)
23
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
24
+ actionview (4.2.7.1)
25
+ activesupport (= 4.2.7.1)
26
+ builder (~> 3.1)
27
+ erubis (~> 2.7.0)
28
+ rails-dom-testing (~> 1.0, >= 1.0.5)
29
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
30
+ activejob (4.2.7.1)
31
+ activesupport (= 4.2.7.1)
32
+ globalid (>= 0.3.0)
33
+ activemodel (4.2.7.1)
34
+ activesupport (= 4.2.7.1)
35
+ builder (~> 3.1)
36
+ activerecord (4.2.7.1)
37
+ activemodel (= 4.2.7.1)
38
+ activesupport (= 4.2.7.1)
39
+ arel (~> 6.0)
40
+ activesupport (4.2.7.1)
41
+ i18n (~> 0.7)
42
+ json (~> 1.7, >= 1.7.7)
43
+ minitest (~> 5.1)
44
+ thread_safe (~> 0.3, >= 0.3.4)
45
+ tzinfo (~> 1.1)
46
+ addressable (2.3.8)
47
+ arel (6.0.3)
11
48
  ast (2.3.0)
49
+ builder (3.2.2)
50
+ capybara (2.7.0)
51
+ addressable
52
+ mime-types (>= 1.16)
53
+ nokogiri (>= 1.3.3)
54
+ rack (>= 1.0.0)
55
+ rack-test (>= 0.5.4)
56
+ xpath (~> 2.0)
57
+ concurrent-ruby (1.0.2)
12
58
  diff-lcs (1.2.5)
59
+ erubis (2.7.0)
13
60
  foundation_emails (2.2.1.0)
61
+ globalid (0.3.7)
62
+ activesupport (>= 4.1.0)
63
+ i18n (0.7.0)
64
+ json (1.8.3)
65
+ loofah (2.0.3)
66
+ nokogiri (>= 1.5.9)
67
+ mail (2.6.4)
68
+ mime-types (>= 1.16, < 4)
69
+ mime-types (3.1)
70
+ mime-types-data (~> 3.2015)
71
+ mime-types-data (3.2016.0521)
14
72
  mini_portile2 (2.1.0)
73
+ minitest (5.9.1)
15
74
  nokogiri (1.6.8.1)
16
75
  mini_portile2 (~> 2.1.0)
17
76
  parser (2.3.1.4)
18
77
  ast (~> 2.2)
19
78
  powerpack (0.1.1)
79
+ rack (1.6.4)
80
+ rack-test (0.6.3)
81
+ rack (>= 1.0)
82
+ rails (4.2.7.1)
83
+ actionmailer (= 4.2.7.1)
84
+ actionpack (= 4.2.7.1)
85
+ actionview (= 4.2.7.1)
86
+ activejob (= 4.2.7.1)
87
+ activemodel (= 4.2.7.1)
88
+ activerecord (= 4.2.7.1)
89
+ activesupport (= 4.2.7.1)
90
+ bundler (>= 1.3.0, < 2.0)
91
+ railties (= 4.2.7.1)
92
+ sprockets-rails
93
+ rails-deprecated_sanitizer (1.0.3)
94
+ activesupport (>= 4.2.0.alpha)
95
+ rails-dom-testing (1.0.7)
96
+ activesupport (>= 4.2.0.beta, < 5.0)
97
+ nokogiri (~> 1.6.0)
98
+ rails-deprecated_sanitizer (>= 1.0.1)
99
+ rails-html-sanitizer (1.0.3)
100
+ loofah (~> 2.0)
101
+ railties (4.2.7.1)
102
+ actionpack (= 4.2.7.1)
103
+ activesupport (= 4.2.7.1)
104
+ rake (>= 0.8.7)
105
+ thor (>= 0.18.1, < 2.0)
20
106
  rainbow (2.1.0)
21
- rake (11.2.2)
22
- rspec (3.5.0)
23
- rspec-core (~> 3.5.0)
24
- rspec-expectations (~> 3.5.0)
25
- rspec-mocks (~> 3.5.0)
107
+ rake (11.3.0)
26
108
  rspec-core (3.5.0)
27
109
  rspec-support (~> 3.5.0)
28
110
  rspec-expectations (3.5.0)
@@ -31,6 +113,14 @@ GEM
31
113
  rspec-mocks (3.5.0)
32
114
  diff-lcs (>= 1.2.0, < 2.0)
33
115
  rspec-support (~> 3.5.0)
116
+ rspec-rails (3.5.2)
117
+ actionpack (>= 3.0)
118
+ activesupport (>= 3.0)
119
+ railties (>= 3.0)
120
+ rspec-core (~> 3.5.0)
121
+ rspec-expectations (~> 3.5.0)
122
+ rspec-mocks (~> 3.5.0)
123
+ rspec-support (~> 3.5.0)
34
124
  rspec-support (3.5.0)
35
125
  rubocop (0.43.0)
36
126
  parser (>= 2.3.1.1, < 3.0)
@@ -39,19 +129,40 @@ GEM
39
129
  ruby-progressbar (~> 1.7)
40
130
  unicode-display_width (~> 1.0, >= 1.0.1)
41
131
  ruby-progressbar (1.7.5)
132
+ slim (3.0.6)
133
+ temple (~> 0.7.3)
134
+ tilt (>= 1.3.3, < 2.1)
135
+ sprockets (3.7.0)
136
+ concurrent-ruby (~> 1.0)
137
+ rack (> 1, < 3)
138
+ sprockets-rails (3.2.0)
139
+ actionpack (>= 4.0)
140
+ activesupport (>= 4.0)
141
+ sprockets (>= 3.0.0)
142
+ temple (0.7.6)
143
+ thor (0.19.1)
144
+ thread_safe (0.3.5)
145
+ tilt (1.4.1)
146
+ tzinfo (1.2.2)
147
+ thread_safe (~> 0.1)
42
148
  unicode-display_width (1.1.1)
149
+ xpath (2.0.0)
150
+ nokogiri (~> 1.3)
43
151
 
44
152
  PLATFORMS
45
153
  ruby
46
154
 
47
155
  DEPENDENCIES
48
156
  bundler (~> 1.6)
157
+ capybara
49
158
  inky-rb!
159
+ rails
50
160
  rake
51
- rspec
52
161
  rspec-core
53
162
  rspec-expectations
163
+ rspec-rails
54
164
  rubocop
165
+ slim
55
166
 
56
167
  BUNDLED WITH
57
- 1.12.5
168
+ 1.13.6
data/README.md CHANGED
@@ -62,19 +62,44 @@ Run the following command to set up the required styles and mailer layout:
62
62
  rails g inky:install
63
63
  ```
64
64
 
65
- (You can specify the generated mailer layout filename like so: `rails g inky:install some_name`)
65
+ (You can specify the generated mailer layout filename like so: `rails g inky:install some_name` and also your prefered
66
+ markup language like: `rails g inky:install mailer_layout slim`)
66
67
 
67
- Rename your email templates to use the `.inky` file extension. Note that you'll still be able to use ERB within the `.inky` templates:
68
+ Rename your email templates to use the `.inky` file extension. Note that you'll still be able to use your default
69
+ template engine within the `.inky` templates:
68
70
 
69
71
  ```
70
72
  welcome.html => welcome.html.inky
71
73
  pw_reset.html.erb => pw_reset.html.inky
72
74
  ```
73
75
 
76
+
74
77
  You're all set!
75
78
 
76
79
  ** The majority of email clients ignore linked stylesheets. By using a CSS inliner like `premailer-rails` or `roadie`, you're able to leave your stylesheets in a separate file, keeping your markup lean.
77
80
 
81
+ ## Alternative template engine
82
+
83
+ If you do not use ERB for your views and layouts but some other markup like Haml or Slim, you can configure Inky to
84
+ use these languages. To do so, just set an initializer:
85
+
86
+ ```ruby
87
+ # config/initializers/inky.rb
88
+ Inky.configure do |config|
89
+ config.template_engine = :slim
90
+ end
91
+ ```
92
+
93
+ Check [lib/generators/inky/templates/mailer_layout.html.slim](lib/generators/inky/templates/mailer_layout.html.slim)
94
+ for a Slim example.
95
+
96
+ You may prefer to specify which template engine to use before inky:
97
+
98
+ ```
99
+ welcome.html.haml => welcome.html.inky-haml
100
+ pw_reset.html.erb => pw_reset.html.inky-erb
101
+ ```
102
+
78
103
  ## Custom Elements
79
104
 
80
105
  Inky simplifies the process of creating HTML emails by expanding out simple tags like `<row>` and `<column>` into full table syntax. The names of the tags can be changed with the `components` setting.
@@ -13,12 +13,16 @@ Gem::Specification.new do |s|
13
13
 
14
14
  s.files = `git ls-files -z`.split("\x0")
15
15
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
- s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+ s.test_files = Dir['spec/**/*']
17
17
 
18
18
  s.add_dependency "foundation_emails", "~> 2"
19
19
  s.add_dependency "nokogiri"
20
20
  s.add_development_dependency "bundler", "~> 1.6"
21
21
  s.add_development_dependency "rake"
22
- s.add_development_dependency "rspec"
23
22
  s.add_development_dependency "rubocop"
23
+ s.add_development_dependency "rspec-rails"
24
+ s.add_development_dependency "capybara"
25
+ s.add_development_dependency "rails"
26
+ s.add_development_dependency "slim"
27
+ # s.add_development_dependency "pry-byebug"
24
28
  end
@@ -6,11 +6,12 @@ module Inky
6
6
  desc 'Install Foundation for Emails'
7
7
  source_root File.join(File.dirname(__FILE__), 'templates')
8
8
  argument :layout_name, type: :string, default: 'mailer', banner: 'layout_name'
9
+ argument :extension, type: :string, default: 'erb', banner: 'extension'
9
10
 
10
11
  def preserve_original_mailer_layout
11
12
  return unless layout_name == 'mailer'
12
13
 
13
- original_mailer = File.join(layouts_base_dir, 'mailer.html.erb')
14
+ original_mailer = File.join(layouts_base_dir, "mailer.html.#{extension}")
14
15
  rename_filename = File.join(layouts_base_dir, "old_mailer_#{Time.now.to_i}.html.erb")
15
16
  File.rename(original_mailer, rename_filename) if File.exist? original_mailer
16
17
  end
@@ -20,7 +21,7 @@ module Inky
20
21
  end
21
22
 
22
23
  def create_mailer_layout
23
- template 'mailer_layout.html.erb', File.join(layouts_base_dir, "#{layout_name.underscore}.html.erb")
24
+ template "mailer_layout.html.#{extension}", File.join(layouts_base_dir, "#{layout_name.underscore}.html.#{extension}")
24
25
  end
25
26
 
26
27
  private
@@ -4,7 +4,7 @@
4
4
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
5
  <meta name="viewport" content="width=device-width" />
6
6
 
7
- <%%= stylesheet_link_tag "foundation_emails" %>
7
+ <%= stylesheet_link_tag "foundation_emails" %>
8
8
  </head>
9
9
 
10
10
  <body>
@@ -12,7 +12,7 @@
12
12
  <tr>
13
13
  <td class="center" align="center" valign="top">
14
14
  <center>
15
- <%%= yield %>
15
+ <%= yield %>
16
16
  </center>
17
17
  </td>
18
18
  </tr>
@@ -0,0 +1,13 @@
1
+ !!! Strict
2
+ %html{:xmlns => "http://www.w3.org/1999/xhtml"}
3
+ %head
4
+ %meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}/
5
+ %meta{:content => "width=device-width", :name => "viewport"}/
6
+ = stylesheet_link_tag "foundation_emails"
7
+ %body
8
+ %table.body{"data-made-with-foundation" => ""}
9
+ %tr
10
+ %td.center{:align => "center", :valign => "top"}
11
+ %center
12
+ %container
13
+ = yield
@@ -0,0 +1,15 @@
1
+ doctype strict
2
+ html[xmlns='http://www.w3.org/1999/xhtml']
3
+ head
4
+ meta http-equiv="Content-Type" content="text/html; charset=utf-8"
5
+ meta name="viewport" content="width=device-width"
6
+
7
+ = stylesheet_link_tag "foundation_emails"
8
+
9
+ body
10
+ table.body data-made-with-foundation=""
11
+ tr
12
+ td.center align="center" valign="top"
13
+ center
14
+ container
15
+ = yield
@@ -1,7 +1,9 @@
1
+ require 'nokogiri'
2
+ require_relative 'inky/configuration'
3
+ require_relative 'inky/component_factory'
4
+
1
5
  module Inky
2
6
  class Core
3
- require 'nokogiri'
4
- require_relative 'component_factory'
5
7
  attr_accessor :components, :column_count, :component_lookup, :component_tags
6
8
 
7
9
  include ComponentFactory
@@ -28,14 +30,13 @@ module Inky
28
30
  self.component_tags = components.values
29
31
  end
30
32
 
31
- def release_the_kraken(xml_string)
32
- xml_string = xml_string.gsub(/doctype/i, 'DOCTYPE')
33
- raws, str = Inky::Core.extract_raws(xml_string)
33
+ def release_the_kraken(html_string)
34
+ html_string.force_encoding('utf-8') # transform_doc barfs if encoding is ASCII-8bit
35
+ html_string = html_string.gsub(/doctype/i, 'DOCTYPE')
36
+ raws, str = Inky::Core.extract_raws(html_string)
34
37
  parse_cmd = str =~ /<html/i ? :parse : :fragment
35
38
  html = Nokogiri::HTML.public_send(parse_cmd, str)
36
- html.elements.each do |elem|
37
- transform_doc(elem)
38
- end
39
+ transform_doc(html)
39
40
  string = html.to_html(encoding: 'US-ASCII')
40
41
  Inky::Core.re_inject_raws(string, raws)
41
42
  end
@@ -0,0 +1,139 @@
1
+ module Inky
2
+ module ComponentFactory
3
+ def component_factory(elem)
4
+ transform_method = :"_transform_#{component_lookup[elem.name]}"
5
+ return unless respond_to?(transform_method)
6
+ inner = elem.children.map(&:to_s).join
7
+ send(transform_method, elem, inner)
8
+ end
9
+
10
+ tags = %w[class id href size large no-expander small target]
11
+ tags = tags.to_set if tags.respond_to? :to_set
12
+ IGNORED_ON_PASSTHROUGH = tags.freeze
13
+
14
+ def _pass_through_attributes(elem)
15
+ elem.attributes.reject { |e| IGNORED_ON_PASSTHROUGH.include?(e.downcase) }.map do |name, value|
16
+ %{#{name}="#{value}" }
17
+ end.join
18
+ end
19
+
20
+ def _has_class(elem, klass)
21
+ elem.attr('class') =~ /(^|\s)#{klass}($|\s)/
22
+ end
23
+
24
+ def _combine_classes(elem, extra_classes)
25
+ [elem['class'], extra_classes].join(' ')
26
+ end
27
+
28
+ def _combine_attributes(elem, extra_classes = nil)
29
+ classes = _combine_classes(elem, extra_classes)
30
+ [_pass_through_attributes(elem), classes && %{class="#{classes}"}].join
31
+ end
32
+
33
+ def _target_attribute(elem)
34
+ elem.attributes['target'] ? %{ target="#{elem.attributes['target']}"} : ''
35
+ end
36
+
37
+ def _transform_button(component, inner)
38
+ expand = _has_class(component, 'expand')
39
+ if component.attr('href')
40
+ target = _target_attribute(component)
41
+ extra = ' align="center" class="float-center"' if expand
42
+ inner = %{<a href="#{component.attr('href')}"#{target}#{extra}>#{inner}</a>}
43
+ end
44
+ inner = "<center>#{inner}</center>" if expand
45
+
46
+ classes = _combine_classes(component, 'button')
47
+ expander = '<td class="expander"></td>' if expand
48
+ %{<table class="#{classes}"><tr><td><table><tr><td>#{inner}</td></tr></table></td>#{expander}</tr></table>}
49
+ end
50
+
51
+ def _transform_menu(component, inner)
52
+ attributes = _combine_attributes(component, 'menu')
53
+ %{<table #{attributes}><tr><td><table><tr>#{inner}</tr></table></td></tr></table>}
54
+ end
55
+
56
+ def _transform_menu_item(component, inner)
57
+ target = _target_attribute(component)
58
+ attributes = _combine_attributes(component, 'menu-item')
59
+ %{<th #{attributes}><a href="#{component.attr('href')}"#{target}>#{inner}</a></th>}
60
+ end
61
+
62
+ def _transform_container(component, inner)
63
+ attributes = _combine_attributes(component, 'container')
64
+ %{<table #{attributes} align="center"><tbody><tr><td>#{inner}</td></tr></tbody></table>}
65
+ end
66
+
67
+ def _transform_row(component, inner)
68
+ attributes = _combine_attributes(component, 'row')
69
+ %{<table #{attributes}><tbody><tr>#{inner}</tr></tbody></table>}
70
+ end
71
+
72
+ # in inky.js this is factored out into makeClumn. TBD if we need that here.
73
+ def _transform_columns(component, inner)
74
+ col_count = component.parent.elements.size
75
+
76
+ small_val = component.attr('small')
77
+ large_val = component.attr('large')
78
+
79
+ small_size = small_val || column_count
80
+ large_size = large_val || small_val || (column_count / col_count).to_i
81
+
82
+ classes = _combine_classes(component, "small-#{small_size} large-#{large_size} columns")
83
+
84
+ classes << ' first' unless component.previous_element
85
+ classes << ' last' unless component.next_element
86
+
87
+ subrows = component.elements.css(".row").to_a.concat(component.elements.css("row").to_a)
88
+ expander = %{<th class="expander"></th>} if large_size.to_i == column_count && subrows.empty?
89
+
90
+ %{<th class="#{classes}" #{_pass_through_attributes(component)}><table><tr><th>#{inner}</th>#{expander}</tr></table></th>}
91
+ end
92
+
93
+ def _transform_block_grid(component, inner)
94
+ classes = _combine_classes(component, "block-grid up-#{component.attr('up')}")
95
+ %{<table class="#{classes}"><tr>#{inner}</tr></table>}
96
+ end
97
+
98
+ def _transform_center(component, _inner)
99
+ # NOTE: Using children instead of elements because elements.to_a
100
+ # sometimes appears to miss elements that show up in size
101
+ component.elements.each do |child|
102
+ child['align'] = 'center'
103
+ child['class'] = _combine_classes(child, 'float-center')
104
+ items = component.elements.css(".menu-item").to_a.concat(component.elements.css("item").to_a)
105
+ items.each do |item|
106
+ item['class'] = _combine_classes(item, 'float-center')
107
+ end
108
+ end
109
+ component.to_s
110
+ end
111
+
112
+ def _transform_callout(component, inner)
113
+ classes = _combine_classes(component, 'callout-inner')
114
+ attributes = _pass_through_attributes(component)
115
+ %{<table #{attributes}class="callout"><tr><th class="#{classes}">#{inner}</th><th class="expander"></th></tr></table>}
116
+ end
117
+
118
+ def _transform_spacer(component, _inner)
119
+ classes = _combine_classes(component, 'spacer')
120
+ build_table = ->(size, extra) { %{<table class="#{classes} #{extra}"><tbody><tr><td height="#{size}px" style="font-size:#{size}px;line-height:#{size}px;">&#xA0;</td></tr></tbody></table>} }
121
+ size = component.attr('size')
122
+ size_sm = component.attr('size-sm')
123
+ size_lg = component.attr('size-lg')
124
+ if size_sm || size_lg
125
+ html = ''
126
+ html << build_table[size_sm, 'hide-for-large'] if size_sm
127
+ html << build_table[size_lg, 'show-for-large'] if size_lg
128
+ html
129
+ else
130
+ build_table[size || 16, nil]
131
+ end
132
+ end
133
+
134
+ def _transform_wrapper(component, inner)
135
+ attributes = _combine_attributes(component, 'wrapper')
136
+ %{<table #{attributes} align="center"><tr><td class="wrapper-inner">#{inner}</td></tr></table>}
137
+ end
138
+ end
139
+ end