mjml-rb 0.2.2 → 0.2.4

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: 8065e65f3045ffc642525cb247d79591a4c1c2cd3d57541ec768dc2d0fec6ef9
4
- data.tar.gz: 304dc0c8474a87438378344264bcc4e56e27052ea9e09d6003352a3c94f3dd4e
3
+ metadata.gz: 4bb6667d87d9376e60199a47e987e10cfb496be78c4bd549f71fa049c2618aff
4
+ data.tar.gz: 98f63a171ea6a2d5b1dd6212c86a13cdd2eabf71c1ab9f12d15f8c83a04e30d2
5
5
  SHA512:
6
- metadata.gz: a9bc2dd17fe59a21d311aca96332ba32a5c09222641bf566518e9aa856f5a9f45121d72ed6b53f74217cf26b61a3378d2f800abe482a772585e0353eb8b5627b
7
- data.tar.gz: 90991a406d6ecc5ed614d0a12a6bba3094bb0c72535407fcf709e5e5d9bceab802d137e85e319ac47aa57ad3c5ab4bf58fddbd8524996468cdeb18ebb521d30e
6
+ metadata.gz: 33c9d050ef8ebd560b92a513391957c9fd2ebea43023d3977c5c06fe154bc7eef1bf6646abbf555dcbf6f53d4900461cceb89daaf0e3b2be36d3e08cec445b38
7
+ data.tar.gz: 839a5ddbe4448b2c86b9b015d59468e8f2553a78432cf6371bdb6d061c850568850bde0316fd9be6ac548a0bc17715900d233cd1538cff03785383f29d960240
data/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
  > and not all components or attributes are fully implemented yet.
9
9
  > **Do not use in production without thorough testing of every template against
10
10
  > the official npm renderer.** API and output format may change without notice.
11
+ > This is a **fully open source project**, and help is welcome:
12
+ > feedback, bug reports, test cases, optimizations, proposals, and pull requests.
11
13
  > No warranty of any kind is provided.
12
14
 
13
15
  This directory contains a Ruby-first implementation of the main MJML user-facing tooling:
@@ -60,9 +62,9 @@ The table below tracks current JS-to-Ruby migration status for MJML components i
60
62
  | `mj-navbar-link` | migrated | Implemented in `navbar.rb` as an ending-tag navbar child component. |
61
63
  | `mj-raw` | migrated | Implemented in `raw.rb`, including head insertion and top-level `position="file-start"` output before the doctype. |
62
64
  | `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. |
65
+ | `mj-attributes` | migrated | Implemented in `attributes.rb`, including npm-style `mj-class` descendant defaults. |
64
66
  | `mj-all` | partial | Supported through `mj-attributes`. |
65
- | `mj-class` | partial | Supported through `mj-attributes`. |
67
+ | `mj-class` | migrated | Supported through `attributes.rb`, including nested per-tag descendant defaults. |
66
68
  | `mj-title` | partial | Supported through head context. |
67
69
  | `mj-preview` | partial | Supported through head context. |
68
70
  | `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:)
@@ -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,6 +1,7 @@
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"
@@ -62,7 +63,9 @@ module MjmlRb
62
63
  fonts: DEFAULT_FONTS.merge(hash_or_empty(options[:fonts])),
63
64
  global_defaults: {},
64
65
  tag_defaults: {},
65
- classes: {}
66
+ classes: {},
67
+ classes_default: {},
68
+ inherited_mj_class: ""
66
69
  }
67
70
 
68
71
  return context unless head
@@ -84,7 +87,11 @@ module MjmlRb
84
87
  width = node.attributes["width"].to_s.strip
85
88
  context[:breakpoint] = width unless width.empty?
86
89
  when "mj-attributes"
87
- 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
88
95
  when "mj-html-attributes"
89
96
  absorb_html_attributes_node(node, context)
90
97
  when "mj-raw"
@@ -173,7 +180,9 @@ module MjmlRb
173
180
  end
174
181
 
175
182
  def render_children(node, context, parent:)
176
- 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
177
186
  end
178
187
 
179
188
  def render_node(node, context, parent:)
@@ -196,10 +205,12 @@ module MjmlRb
196
205
  def render_group(node, context, width_pct = 100)
197
206
  items = node.element_children.select { |e| e.tag_name == "mj-column" }
198
207
  widths = compute_column_widths(items, context)
199
- items.each_with_index.map do |item, i|
200
- context[:_column_width_pct] = widths[i]
201
- render_node(item, context, parent: "mj-group")
202
- 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
203
214
  end
204
215
 
205
216
  def compute_column_widths(columns, context)
@@ -260,14 +271,10 @@ module MjmlRb
260
271
  rules.each do |selector, attrs|
261
272
  next if selector.empty? || attrs.empty?
262
273
 
263
- begin
264
- document.css(selector).each do |node|
265
- attrs.each do |name, value|
266
- node[name] = value.to_s
267
- end
274
+ select_nodes(document, selector).each do |node|
275
+ attrs.each do |name, value|
276
+ node[name] = value.to_s
268
277
  end
269
- rescue Nokogiri::CSS::SyntaxError
270
- next
271
278
  end
272
279
  end
273
280
 
@@ -282,18 +289,53 @@ module MjmlRb
282
289
  parse_inline_css_rules(css_blocks.join("\n")).each do |selector, declarations|
283
290
  next if selector.empty? || declarations.empty?
284
291
 
285
- begin
286
- document.css(selector).each do |node|
287
- merge_inline_style!(node, declarations)
288
- end
289
- rescue Nokogiri::CSS::SyntaxError
290
- next
292
+ select_nodes(document, selector).each do |node|
293
+ merge_inline_style!(node, declarations)
291
294
  end
292
295
  end
293
296
 
294
297
  document.to_html
295
298
  end
296
299
 
300
+ def select_nodes(document, selector)
301
+ document.css(selector)
302
+ rescue Nokogiri::CSS::SyntaxError, Nokogiri::XML::XPath::SyntaxError
303
+ fallback_select_nodes(document, selector)
304
+ end
305
+
306
+ def fallback_select_nodes(document, selector)
307
+ return [] unless selector.include?(":lang(")
308
+
309
+ lang_values = selector.scan(/:lang\(([^)]+)\)/).flatten.map do |value|
310
+ value.to_s.strip.gsub(/\A['"]|['"]\z/, "").downcase
311
+ end.reject(&:empty?)
312
+ return [] if lang_values.empty?
313
+
314
+ base_selector = selector.gsub(/:lang\(([^)]+)\)/, "").strip
315
+ base_selector = "*" if base_selector.empty?
316
+
317
+ document.css(base_selector).select do |node|
318
+ lang_values.all? { |lang| lang_matches?(node, lang) }
319
+ end
320
+ rescue Nokogiri::CSS::SyntaxError, Nokogiri::XML::XPath::SyntaxError
321
+ []
322
+ end
323
+
324
+ def lang_matches?(node, lang)
325
+ current = node
326
+
327
+ while current
328
+ value = current["lang"]
329
+ if value && !value.empty?
330
+ normalized = value.downcase
331
+ return normalized == lang || normalized.start_with?("#{lang}-")
332
+ end
333
+ current = current.parent
334
+ end
335
+
336
+ false
337
+ end
338
+
297
339
  def parse_inline_css_rules(css)
298
340
  stripped_css = strip_css_comments(css.to_s)
299
341
  plain_css = strip_css_at_rules(stripped_css)
@@ -391,6 +433,7 @@ module MjmlRb
391
433
  registry = {}
392
434
  # Register component classes here as they are implemented.
393
435
  register_component(registry, Components::Body.new(self))
436
+ register_component(registry, Components::Attributes.new(self))
394
437
  register_component(registry, Components::Breakpoint.new(self))
395
438
  register_component(registry, Components::Accordion.new(self))
396
439
  register_component(registry, Components::Button.new(self))
@@ -419,8 +462,18 @@ module MjmlRb
419
462
  attrs.merge!(context[:tag_defaults][node.tag_name] || {})
420
463
 
421
464
  node_classes = node.attributes["mj-class"].to_s.split(/\s+/).reject(&:empty?)
422
- node_classes.each do |klass|
423
- attrs.merge!(context[:classes][klass] || {})
465
+ class_attrs = node_classes.each_with_object({}) do |klass, memo|
466
+ mj_class_attrs = (context[:classes] || {})[klass] || {}
467
+ if memo["css-class"] && mj_class_attrs["css-class"]
468
+ memo["css-class"] = "#{memo["css-class"]} #{mj_class_attrs["css-class"]}"
469
+ end
470
+ memo.merge!(mj_class_attrs)
471
+ end
472
+ attrs.merge!(class_attrs)
473
+
474
+ inherited_classes = context[:inherited_mj_class].to_s.split(/\s+/).reject(&:empty?)
475
+ inherited_classes.each do |klass|
476
+ attrs.merge!(((context[:classes_default] || {})[klass] || {})[node.tag_name] || {})
424
477
  end
425
478
 
426
479
  attrs.merge!(node.attributes)
@@ -482,6 +535,15 @@ module MjmlRb
482
535
  node.element_children.find { |child| child.tag_name == tag_name }
483
536
  end
484
537
 
538
+ def with_inherited_mj_class(context, node)
539
+ previous = context[:inherited_mj_class]
540
+ current = node.attributes["mj-class"]
541
+ context[:inherited_mj_class] = (current && !current.empty?) ? current : previous
542
+ yield
543
+ ensure
544
+ context[:inherited_mj_class] = previous
545
+ end
546
+
485
547
  def root_file_start_raw(document)
486
548
  document.element_children.filter_map do |child|
487
549
  next unless child.tag_name == "mj-raw"
@@ -1,3 +1,3 @@
1
1
  module MjmlRb
2
- VERSION = "0.2.2".freeze
2
+ VERSION = "0.2.4".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.2
4
+ version: 0.2.4
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
@@ -78,7 +79,7 @@ files:
78
79
  - lib/mjml-rb/validator.rb
79
80
  - lib/mjml-rb/version.rb
80
81
  - mjml-rb.gemspec
81
- homepage: https://github.com/faraquet/mjml-rails
82
+ homepage: https://github.com/faraquet/mjml-rb
82
83
  licenses:
83
84
  - MIT
84
85
  metadata: {}