mjml-rb 0.2.1 → 0.2.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: 8c04515032bc1da0a7fcbbad05d5d2b8bb10f684cb34e3b25920f96e10e63207
4
- data.tar.gz: 778c683334aecab61498fde20ea06ba55e417c5080fd1776dacf5668f609988e
3
+ metadata.gz: 4c0d1fcf5bc3d3da63f1e883fbf9fc1c5d596f23e2d1845b52fac3a2795c15ec
4
+ data.tar.gz: 96945245aea91b2d4cb94336a2d29a58eb6c5cbf332ac76e2828f581f4e31a4f
5
5
  SHA512:
6
- metadata.gz: 3c2b46d74bb5804fd9db901c529aec0fd2a4d9cb9b8b97872ce5d7bef2fc22077cfa1b0471f368ab268b5c3d8b231e0cbc13f5b565da937692689233b98a55ae
7
- data.tar.gz: 37e1b8ce0fa0134c5cbe41e8977fb3f0722dcbcea69728f2b810a1bf615a81691f27fa55805ccd52f37b23c078d957ed3fbc388f9898a9cad4fd667773fe2781
6
+ metadata.gz: 2ab219c36a7dfb49bc89ce1f7b6da2dc8d5089e60bfb640b2f2606783d8f27ee5560133274935d0f6aee96ac076cdfac346c4d85775452a2933f2a47ff9d87e0
7
+ data.tar.gz: d3fea2c9c4cd07a967d62dc07ff16328c0b61cc7f20b108209f21c7d5f8eb061040eaf5085875c0bb09a81d56607e08e9efa3cfd83d147f296a5a8308687cca9
data/README.md CHANGED
@@ -58,11 +58,11 @@ The table below tracks current JS-to-Ruby migration status for MJML components i
58
58
  | `mj-hero` | migrated | Implemented in `hero.rb` with fixed/fluid modes, inner content wrapper, and Outlook VML background fallback. |
59
59
  | `mj-navbar` | migrated | Implemented in `navbar.rb`, including `base-url` propagation and breakpoint-aware hamburger CSS. |
60
60
  | `mj-navbar-link` | migrated | Implemented in `navbar.rb` as an ending-tag navbar child component. |
61
- | `mj-raw` | partial | Supported as passthrough content, but not as a dedicated migrated component. |
61
+ | `mj-raw` | migrated | Implemented in `raw.rb`, including head insertion and top-level `position="file-start"` output before the doctype. |
62
62
  | `mj-head` | partial | Core tags such as `mj-title`, `mj-preview`, `mj-style`, `mj-font`, and `mj-attributes` are supported. |
63
- | `mj-attributes` | partial | `mj-all`, `mj-class`, and per-tag defaults are supported. |
63
+ | `mj-attributes` | migrated | Implemented in `attributes.rb`, including npm-style `mj-class` descendant defaults. |
64
64
  | `mj-all` | partial | Supported through `mj-attributes`. |
65
- | `mj-class` | partial | Supported through `mj-attributes`. |
65
+ | `mj-class` | migrated | Supported through `attributes.rb`, including nested per-tag descendant defaults. |
66
66
  | `mj-title` | partial | Supported through head context. |
67
67
  | `mj-preview` | partial | Supported through head context. |
68
68
  | `mj-style` | partial | Supported, including inline CSS application. |
@@ -94,16 +94,18 @@ module MjmlRb
94
94
  "border-bottom" => "none",
95
95
  "font-family" => accordion_attrs["font-family"]
96
96
  )
97
- inner = node.element_children.map do |child|
98
- case child.tag_name
99
- when "mj-accordion-element"
100
- render_accordion_element(child, context, accordion_attrs)
101
- when "mj-raw"
102
- raw_inner(child)
103
- else
104
- render_node(child, context, parent: "mj-accordion")
105
- end
106
- end.join
97
+ inner = with_inherited_mj_class(context, node) do
98
+ node.element_children.map do |child|
99
+ case child.tag_name
100
+ when "mj-accordion-element"
101
+ render_accordion_element(child, context, accordion_attrs)
102
+ when "mj-raw"
103
+ raw_inner(child)
104
+ else
105
+ render_node(child, context, parent: "mj-accordion")
106
+ end
107
+ end.join
108
+ end
107
109
 
108
110
  %(<tr><td style="#{outer_style}"><table role="presentation" width="100%" cellspacing="0" cellpadding="0" class="mj-accordion" style="#{table_style}"><tbody>#{inner}</tbody></table></td></tr>)
109
111
  end
@@ -125,16 +127,18 @@ module MjmlRb
125
127
  content = []
126
128
  content << render_accordion_title(nil, attrs) unless has_title
127
129
 
128
- children.each do |child|
129
- case child.tag_name
130
- when "mj-accordion-title"
131
- child_attrs = attrs.merge(resolved_attributes(child, context))
132
- content << render_accordion_title(child, child_attrs)
133
- when "mj-accordion-text"
134
- child_attrs = attrs.merge(resolved_attributes(child, context))
135
- content << render_accordion_text(child, child_attrs)
136
- when "mj-raw"
137
- content << raw_inner(child)
130
+ with_inherited_mj_class(context, node) do
131
+ children.each do |child|
132
+ case child.tag_name
133
+ when "mj-accordion-title"
134
+ child_attrs = attrs.merge(resolved_attributes(child, context))
135
+ content << render_accordion_title(child, child_attrs)
136
+ when "mj-accordion-text"
137
+ child_attrs = attrs.merge(resolved_attributes(child, context))
138
+ content << render_accordion_text(child, child_attrs)
139
+ when "mj-raw"
140
+ content << raw_inner(child)
141
+ end
138
142
  end
139
143
  end
140
144
  content << render_accordion_text(nil, attrs) unless has_text
@@ -0,0 +1,53 @@
1
+ require_relative "base"
2
+
3
+ module MjmlRb
4
+ module Components
5
+ class Attributes < Base
6
+ TAGS = %w[mj-attributes mj-all mj-class].freeze
7
+
8
+ class << self
9
+ def allowed_attributes_for(tag_name)
10
+ {}
11
+ end
12
+
13
+ def allowed_attributes
14
+ {}
15
+ end
16
+ end
17
+
18
+ def tags
19
+ TAGS
20
+ end
21
+
22
+ def render(tag_name:, node:, context:, attrs:, parent:)
23
+ ""
24
+ end
25
+
26
+ def handle_head(attributes_node, context)
27
+ attributes_node.element_children.each do |child|
28
+ case child.tag_name
29
+ when "mj-all"
30
+ context[:global_defaults].merge!(child.attributes)
31
+ when "mj-class"
32
+ name = child.attributes["name"]
33
+ next unless name
34
+
35
+ context[:classes][name] ||= {}
36
+ context[:classes][name].merge!(child.attributes.reject { |key, _| key == "name" })
37
+
38
+ defaults = child.element_children.each_with_object({}) do |class_child, memo|
39
+ memo[class_child.tag_name] = class_child.attributes
40
+ end
41
+ next if defaults.empty?
42
+
43
+ context[:classes_default][name] ||= {}
44
+ context[:classes_default][name].merge!(defaults)
45
+ else
46
+ context[:tag_defaults][child.tag_name] ||= {}
47
+ context[:tag_defaults][child.tag_name].merge!(child.attributes)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -45,6 +45,10 @@ module MjmlRb
45
45
  renderer.send(:resolved_attributes, node, context)
46
46
  end
47
47
 
48
+ def with_inherited_mj_class(context, node, &block)
49
+ renderer.send(:with_inherited_mj_class, context, node, &block)
50
+ end
51
+
48
52
  def raw_inner(node)
49
53
  renderer.send(:raw_inner, node)
50
54
  end
@@ -164,14 +164,16 @@ module MjmlRb
164
164
  end
165
165
 
166
166
  def render_navbar_children(node, context)
167
- node.element_children.map do |child|
168
- case child.tag_name
169
- when "mj-navbar-link", "mj-raw"
170
- render_node(child, context, parent: "mj-navbar")
171
- else
172
- ""
173
- end
174
- end.join
167
+ with_inherited_mj_class(context, node) do
168
+ node.element_children.map do |child|
169
+ case child.tag_name
170
+ when "mj-navbar-link", "mj-raw"
171
+ render_node(child, context, parent: "mj-navbar")
172
+ else
173
+ ""
174
+ end
175
+ end.join
176
+ end
175
177
  end
176
178
 
177
179
  def render_navbar_link(node, context, attrs, parent:)
@@ -0,0 +1,21 @@
1
+ require_relative "base"
2
+
3
+ module MjmlRb
4
+ module Components
5
+ class Raw < Base
6
+ TAGS = ["mj-raw"].freeze
7
+
8
+ ALLOWED_ATTRIBUTES = {
9
+ "position" => "enum(file-start)"
10
+ }.freeze
11
+
12
+ def tags
13
+ TAGS
14
+ end
15
+
16
+ def render(tag_name:, node:, context:, attrs:, parent:)
17
+ raw_inner(node)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -224,22 +224,24 @@ module MjmlRb
224
224
  close_tr = %(<!--[if mso | IE]></tr><![endif]-->)
225
225
  close_table = %(<!--[if mso | IE]></table><![endif]-->)
226
226
 
227
- col_parts = columns.each_with_index.map do |col, i|
228
- col_attrs = resolved_attributes(col, context)
229
- v_align = col_attrs["vertical-align"] || "top"
230
- col_px = (box_width.to_f * widths[i] / 100.0).round
231
-
232
- td_open = %(<!--[if mso | IE]><td class="" style="vertical-align:#{v_align};width:#{col_px}px;" ><![endif]-->)
233
- td_close = %(<!--[if mso | IE]></td><![endif]-->)
234
-
235
- col_html = if col.tag_name == "mj-group"
236
- renderer.send(:render_group, col, context, widths[i])
237
- else
238
- context[:_column_width_pct] = widths[i]
239
- render_node(col, context, parent: "mj-section")
240
- end
241
-
242
- "#{td_open}\n#{col_html}\n#{td_close}"
227
+ col_parts = with_inherited_mj_class(context, node) do
228
+ columns.each_with_index.map do |col, i|
229
+ col_attrs = resolved_attributes(col, context)
230
+ v_align = col_attrs["vertical-align"] || "top"
231
+ col_px = (box_width.to_f * widths[i] / 100.0).round
232
+
233
+ td_open = %(<!--[if mso | IE]><td class="" style="vertical-align:#{v_align};width:#{col_px}px;" ><![endif]-->)
234
+ td_close = %(<!--[if mso | IE]></td><![endif]-->)
235
+
236
+ col_html = if col.tag_name == "mj-group"
237
+ renderer.send(:render_group, col, context, widths[i])
238
+ else
239
+ context[:_column_width_pct] = widths[i]
240
+ render_node(col, context, parent: "mj-section")
241
+ end
242
+
243
+ "#{td_open}\n#{col_html}\n#{td_close}"
244
+ end
243
245
  end
244
246
 
245
247
  ([open_table, open_tr] + col_parts + [close_tr, close_table]).join("\n")
@@ -317,11 +319,13 @@ module MjmlRb
317
319
  close_tr = %(<!--[if mso | IE]></tr><![endif]-->)
318
320
  close_table = %(<!--[if mso | IE]></table><![endif]-->)
319
321
 
320
- section_parts = children.map do |child|
321
- td_open = %(<!--[if mso | IE]><td class="" width="#{container_px}px" ><![endif]-->)
322
- td_close = %(<!--[if mso | IE]></td><![endif]-->)
323
- child_html = render_node(child, context, parent: "mj-wrapper")
324
- "#{td_open}\n#{child_html}\n#{td_close}"
322
+ section_parts = with_inherited_mj_class(context, node) do
323
+ children.map do |child|
324
+ td_open = %(<!--[if mso | IE]><td class="" width="#{container_px}px" ><![endif]-->)
325
+ td_close = %(<!--[if mso | IE]></td><![endif]-->)
326
+ child_html = render_node(child, context, parent: "mj-wrapper")
327
+ "#{td_open}\n#{child_html}\n#{td_close}"
328
+ end
325
329
  end
326
330
 
327
331
  ([open_table, open_tr] + section_parts + [close_tr, close_table]).join("\n")
@@ -139,14 +139,16 @@ module MjmlRb
139
139
  outlook_open = %(<!--[if mso | IE]><table align="#{escape_attr(align)}" border="0" cellpadding="0" cellspacing="0" role="presentation" ><tr>)
140
140
  outlook_close = %(</tr></table><![endif]-->)
141
141
 
142
- children_html = elements.map.with_index do |child, idx|
143
- child_attrs = resolved_attributes(child, context)
144
- merged_attrs = ELEMENT_DEFAULTS.merge(inherited).merge(child_attrs)
145
- el_html = render_social_element(child, merged_attrs)
146
-
147
- outlook_td_open = idx == 0 ? "<td>" : "</td><td>"
148
- %(#{outlook_td_open}<![endif]--><table align="#{escape_attr(align)}" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tbody>#{el_html}</tbody></table><!--[if mso | IE]>)
149
- end.join
142
+ children_html = with_inherited_mj_class(context, node) do
143
+ elements.map.with_index do |child, idx|
144
+ child_attrs = resolved_attributes(child, context)
145
+ merged_attrs = ELEMENT_DEFAULTS.merge(inherited).merge(child_attrs)
146
+ el_html = render_social_element(child, merged_attrs)
147
+
148
+ outlook_td_open = idx == 0 ? "<td>" : "</td><td>"
149
+ %(#{outlook_td_open}<![endif]--><table align="#{escape_attr(align)}" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tbody>#{el_html}</tbody></table><!--[if mso | IE]>)
150
+ end.join
151
+ end
150
152
 
151
153
  %(#{outlook_open}#{children_html}</td>#{outlook_close})
152
154
  end
@@ -155,11 +157,13 @@ module MjmlRb
155
157
  inherited = inherited_attrs(social_attrs)
156
158
  elements = social_element_children(node)
157
159
 
158
- children_html = elements.map do |child|
159
- child_attrs = resolved_attributes(child, context)
160
- merged_attrs = ELEMENT_DEFAULTS.merge(inherited).merge(child_attrs)
161
- render_social_element(child, merged_attrs)
162
- end.join
160
+ children_html = with_inherited_mj_class(context, node) do
161
+ elements.map do |child|
162
+ child_attrs = resolved_attributes(child, context)
163
+ merged_attrs = ELEMENT_DEFAULTS.merge(inherited).merge(child_attrs)
164
+ render_social_element(child, merged_attrs)
165
+ end.join
166
+ end
163
167
 
164
168
  %(<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0px;"><tbody>#{children_html}</tbody></table>)
165
169
  end
@@ -20,6 +20,7 @@ module MjmlRb
20
20
  def parse(mjml, options = {})
21
21
  opts = normalize_options(options)
22
22
  xml = apply_preprocessors(mjml.to_s, opts[:preprocessors])
23
+ xml = wrap_raw_tags_in_cdata(xml)
23
24
  xml = normalize_html_void_tags(xml)
24
25
  xml = expand_includes(xml, opts) unless opts[:ignore_includes]
25
26
 
@@ -48,6 +49,7 @@ module MjmlRb
48
49
  end
49
50
 
50
51
  def expand_includes(xml, options)
52
+ xml = wrap_raw_tags_in_cdata(xml)
51
53
  xml = normalize_html_void_tags(xml)
52
54
  doc = Document.new(sanitize_bare_ampersands(xml))
53
55
  includes = XPath.match(doc, "//mj-include")
@@ -64,7 +66,7 @@ module MjmlRb
64
66
  replacement = if include_type == "html"
65
67
  %(<mj-raw><![CDATA[#{escape_cdata(include_content)}]]></mj-raw>)
66
68
  else
67
- normalize_html_void_tags(strip_xml_declaration(include_content))
69
+ wrap_raw_tags_in_cdata(normalize_html_void_tags(strip_xml_declaration(include_content)))
68
70
  end
69
71
 
70
72
  fragment = Document.new(sanitize_bare_ampersands("<include-root>#{replacement}</include-root>"))
@@ -101,6 +103,18 @@ module MjmlRb
101
103
  end
102
104
  end
103
105
 
106
+ def wrap_raw_tags_in_cdata(content)
107
+ content.gsub(/<mj-raw(\s[^<>]*?)?>(.*?)<\/mj-raw>/mi) do
108
+ attrs = Regexp.last_match(1).to_s
109
+ inner = Regexp.last_match(2).to_s
110
+ if inner.include?("<![CDATA[")
111
+ "<mj-raw#{attrs}>#{inner}</mj-raw>"
112
+ else
113
+ "<mj-raw#{attrs}><![CDATA[#{escape_cdata(inner)}]]></mj-raw>"
114
+ end
115
+ end
116
+ end
117
+
104
118
  def escape_cdata(content)
105
119
  content.to_s.gsub("]]>", "]]]]><![CDATA[>")
106
120
  end
@@ -1,12 +1,14 @@
1
1
  require "cgi"
2
2
  require "nokogiri"
3
3
  require_relative "components/accordion"
4
+ require_relative "components/attributes"
4
5
  require_relative "components/body"
5
6
  require_relative "components/breakpoint"
6
7
  require_relative "components/button"
7
8
  require_relative "components/hero"
8
9
  require_relative "components/image"
9
10
  require_relative "components/navbar"
11
+ require_relative "components/raw"
10
12
  require_relative "components/text"
11
13
  require_relative "components/divider"
12
14
  require_relative "components/html_attributes"
@@ -36,6 +38,7 @@ module MjmlRb
36
38
  raise ArgumentError, "Missing <mj-body>" unless body
37
39
 
38
40
  context = build_context(head, options)
41
+ context[:before_doctype] = root_file_start_raw(document)
39
42
  context[:lang] = options[:lang] || document.attributes["lang"] || "und"
40
43
  context[:dir] = options[:dir] || document.attributes["dir"] || "auto"
41
44
  context[:column_widths] = {}
@@ -52,14 +55,17 @@ module MjmlRb
52
55
  title: "",
53
56
  preview: "",
54
57
  breakpoint: "480px",
58
+ before_doctype: "",
59
+ head_raw: [],
55
60
  head_styles: [],
56
61
  inline_styles: [],
57
- body_styles: [],
58
62
  html_attributes: {},
59
63
  fonts: DEFAULT_FONTS.merge(hash_or_empty(options[:fonts])),
60
64
  global_defaults: {},
61
65
  tag_defaults: {},
62
- classes: {}
66
+ classes: {},
67
+ classes_default: {},
68
+ inherited_mj_class: ""
63
69
  }
64
70
 
65
71
  return context unless head
@@ -81,11 +87,15 @@ module MjmlRb
81
87
  width = node.attributes["width"].to_s.strip
82
88
  context[:breakpoint] = width unless width.empty?
83
89
  when "mj-attributes"
84
- absorb_attribute_node(node, context)
90
+ if (component = component_for(node.tag_name)) && component.respond_to?(:handle_head)
91
+ component.handle_head(node, context)
92
+ else
93
+ absorb_attribute_node(node, context)
94
+ end
85
95
  when "mj-html-attributes"
86
96
  absorb_html_attributes_node(node, context)
87
97
  when "mj-raw"
88
- context[:body_styles] << raw_inner(node)
98
+ context[:head_raw] << raw_inner(node)
89
99
  end
90
100
  end
91
101
 
@@ -135,6 +145,8 @@ module MjmlRb
135
145
  title = context[:title].to_s
136
146
  preview = context[:preview]
137
147
  head_styles = ([DOCUMENT_RESET_CSS] + unique_strings(context[:head_styles])).join("\n")
148
+ head_raw = Array(context[:head_raw]).join("\n")
149
+ before_doctype = context[:before_doctype].to_s
138
150
  font_links = context[:fonts].values.uniq.map { |href| %(<link href="#{escape_attr(href)}" rel="stylesheet" type="text/css">) }.join("\n")
139
151
  preview_block = preview.empty? ? "" : %(<div style="display:none;max-height:0;overflow:hidden;opacity:0;">#{escape_html(preview)}</div>)
140
152
  html_attributes = { "lang" => context[:lang], "dir" => context[:dir] }
@@ -153,6 +165,7 @@ module MjmlRb
153
165
  <title>#{escape_html(title)}</title>
154
166
  #{font_links}
155
167
  <style type="text/css">#{head_styles}</style>
168
+ #{head_raw}
156
169
  </head>
157
170
  <body style="#{body_style}">
158
171
  #{preview_block}
@@ -162,11 +175,14 @@ module MjmlRb
162
175
  HTML
163
176
 
164
177
  html = apply_html_attributes(html, context)
165
- apply_inline_styles(html, context)
178
+ html = apply_inline_styles(html, context)
179
+ before_doctype.empty? ? html : "#{before_doctype}\n#{html}"
166
180
  end
167
181
 
168
182
  def render_children(node, context, parent:)
169
- node.children.map { |child| render_node(child, context, parent: parent) }.join("\n")
183
+ with_inherited_mj_class(context, node) do
184
+ node.children.map { |child| render_node(child, context, parent: parent) }.join("\n")
185
+ end
170
186
  end
171
187
 
172
188
  def render_node(node, context, parent:)
@@ -181,8 +197,6 @@ module MjmlRb
181
197
  case node.tag_name
182
198
  when "mj-group"
183
199
  render_group(node, context)
184
- when "mj-raw"
185
- raw_inner(node)
186
200
  else
187
201
  render_children(node, context, parent: node.tag_name)
188
202
  end
@@ -191,10 +205,12 @@ module MjmlRb
191
205
  def render_group(node, context, width_pct = 100)
192
206
  items = node.element_children.select { |e| e.tag_name == "mj-column" }
193
207
  widths = compute_column_widths(items, context)
194
- items.each_with_index.map do |item, i|
195
- context[:_column_width_pct] = widths[i]
196
- render_node(item, context, parent: "mj-group")
197
- end.join("\n")
208
+ with_inherited_mj_class(context, node) do
209
+ items.each_with_index.map do |item, i|
210
+ context[:_column_width_pct] = widths[i]
211
+ render_node(item, context, parent: "mj-group")
212
+ end.join("\n")
213
+ end
198
214
  end
199
215
 
200
216
  def compute_column_widths(columns, context)
@@ -386,12 +402,14 @@ module MjmlRb
386
402
  registry = {}
387
403
  # Register component classes here as they are implemented.
388
404
  register_component(registry, Components::Body.new(self))
405
+ register_component(registry, Components::Attributes.new(self))
389
406
  register_component(registry, Components::Breakpoint.new(self))
390
407
  register_component(registry, Components::Accordion.new(self))
391
408
  register_component(registry, Components::Button.new(self))
392
409
  register_component(registry, Components::Hero.new(self))
393
410
  register_component(registry, Components::Image.new(self))
394
411
  register_component(registry, Components::Navbar.new(self))
412
+ register_component(registry, Components::Raw.new(self))
395
413
  register_component(registry, Components::Text.new(self))
396
414
  register_component(registry, Components::Divider.new(self))
397
415
  register_component(registry, Components::Table.new(self))
@@ -413,8 +431,18 @@ module MjmlRb
413
431
  attrs.merge!(context[:tag_defaults][node.tag_name] || {})
414
432
 
415
433
  node_classes = node.attributes["mj-class"].to_s.split(/\s+/).reject(&:empty?)
416
- node_classes.each do |klass|
417
- attrs.merge!(context[:classes][klass] || {})
434
+ class_attrs = node_classes.each_with_object({}) do |klass, memo|
435
+ mj_class_attrs = (context[:classes] || {})[klass] || {}
436
+ if memo["css-class"] && mj_class_attrs["css-class"]
437
+ memo["css-class"] = "#{memo["css-class"]} #{mj_class_attrs["css-class"]}"
438
+ end
439
+ memo.merge!(mj_class_attrs)
440
+ end
441
+ attrs.merge!(class_attrs)
442
+
443
+ inherited_classes = context[:inherited_mj_class].to_s.split(/\s+/).reject(&:empty?)
444
+ inherited_classes.each do |klass|
445
+ attrs.merge!(((context[:classes_default] || {})[klass] || {})[node.tag_name] || {})
418
446
  end
419
447
 
420
448
  attrs.merge!(node.attributes)
@@ -476,6 +504,24 @@ module MjmlRb
476
504
  node.element_children.find { |child| child.tag_name == tag_name }
477
505
  end
478
506
 
507
+ def with_inherited_mj_class(context, node)
508
+ previous = context[:inherited_mj_class]
509
+ current = node.attributes["mj-class"]
510
+ context[:inherited_mj_class] = (current && !current.empty?) ? current : previous
511
+ yield
512
+ ensure
513
+ context[:inherited_mj_class] = previous
514
+ end
515
+
516
+ def root_file_start_raw(document)
517
+ document.element_children.filter_map do |child|
518
+ next unless child.tag_name == "mj-raw"
519
+ next unless child.attributes["position"] == "file-start"
520
+
521
+ raw_inner(child)
522
+ end.join("\n")
523
+ end
524
+
479
525
  def contains_tag?(node, tag_name)
480
526
  return false unless node.respond_to?(:tag_name)
481
527
  return true if node.tag_name == tag_name
@@ -1,3 +1,3 @@
1
1
  module MjmlRb
2
- VERSION = "0.2.1".freeze
2
+ VERSION = "0.2.3".freeze
3
3
  end
data/mjml-rb.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.license = "MIT"
12
12
  spec.required_ruby_version = ">= 3.0"
13
13
 
14
- spec.homepage = "https://github.com/faraquet/mjml-rails"
14
+ spec.homepage = "https://github.com/faraquet/mjml-rb"
15
15
  spec.files = Dir.chdir(__dir__) do
16
16
  Dir.glob("{bin,lib}/**/*") + ["Gemfile", "LICENSE", "mjml-rb.gemspec", "README.md"]
17
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mjml-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Andriichuk
@@ -54,6 +54,7 @@ files:
54
54
  - lib/mjml-rb/cli.rb
55
55
  - lib/mjml-rb/compiler.rb
56
56
  - lib/mjml-rb/components/accordion.rb
57
+ - lib/mjml-rb/components/attributes.rb
57
58
  - lib/mjml-rb/components/base.rb
58
59
  - lib/mjml-rb/components/body.rb
59
60
  - lib/mjml-rb/components/breakpoint.rb
@@ -64,6 +65,7 @@ files:
64
65
  - lib/mjml-rb/components/html_attributes.rb
65
66
  - lib/mjml-rb/components/image.rb
66
67
  - lib/mjml-rb/components/navbar.rb
68
+ - lib/mjml-rb/components/raw.rb
67
69
  - lib/mjml-rb/components/section.rb
68
70
  - lib/mjml-rb/components/social.rb
69
71
  - lib/mjml-rb/components/spacer.rb
@@ -77,7 +79,7 @@ files:
77
79
  - lib/mjml-rb/validator.rb
78
80
  - lib/mjml-rb/version.rb
79
81
  - mjml-rb.gemspec
80
- homepage: https://github.com/faraquet/mjml-rails
82
+ homepage: https://github.com/faraquet/mjml-rb
81
83
  licenses:
82
84
  - MIT
83
85
  metadata: {}