mjml-rb 0.3.0 → 0.3.2
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 +4 -4
- data/README.md +53 -0
- data/lib/mjml-rb/cli.rb +5 -0
- data/lib/mjml-rb/component_registry.rb +65 -0
- data/lib/mjml-rb/components/column.rb +18 -1
- data/lib/mjml-rb/components/group.rb +1 -0
- data/lib/mjml-rb/components/hero.rb +13 -2
- data/lib/mjml-rb/components/section.rb +14 -4
- data/lib/mjml-rb/config_file.rb +34 -0
- data/lib/mjml-rb/renderer.rb +21 -6
- data/lib/mjml-rb/validator.rb +4 -9
- data/lib/mjml-rb/version.rb +1 -1
- data/lib/mjml-rb.rb +10 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2a961ff4aa96b2bc79520f261eb233c0e8b77f363afa3d7e0c670fa622359ab7
|
|
4
|
+
data.tar.gz: 295a1289237c408dbcd2752c26bc45c9b16dd80edfe00933bcec864bf250bebf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e544b134c2db7296c58cd9a5c6235b5780448a3673618ae5392ca6eeada84cafa67d88ad7cf1e8aab99aa41ab92feabcc3255ec7eb30ccdbd386b52cc73515ba
|
|
7
|
+
data.tar.gz: 3751e0e61fcfd95378c03d1024caa4dcc4ccc0e7dd1f92a08108afe98c5858cff3d2c31e8a551185a145c44e36e96d7d934fcfc4cd86168fad8ec74650192661
|
data/README.md
CHANGED
|
@@ -95,6 +95,59 @@ compiler options in your application config:
|
|
|
95
95
|
config.mjml_rb.compiler_options = { validation_level: "soft" }
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
## Custom components
|
|
99
|
+
|
|
100
|
+
You can register custom MJML components written in Ruby:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
class MjRating < MjmlRb::Components::Base
|
|
104
|
+
TAGS = ["mj-rating"].freeze
|
|
105
|
+
ALLOWED_ATTRIBUTES = { "stars" => "integer", "color" => "color" }.freeze
|
|
106
|
+
DEFAULT_ATTRIBUTES = { "stars" => "5", "color" => "#f4b400" }.freeze
|
|
107
|
+
|
|
108
|
+
def render(tag_name:, node:, context:, attrs:, parent:)
|
|
109
|
+
stars = (attrs["stars"] || "5").to_i
|
|
110
|
+
color = attrs["color"] || "#f4b400"
|
|
111
|
+
%(<div style="color:#{escape_attr(color)}">#{"\u2605" * stars}</div>)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
MjmlRb.register_component(MjRating,
|
|
116
|
+
dependencies: { "mj-column" => ["mj-rating"] },
|
|
117
|
+
ending_tags: ["mj-rating"]
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The `dependencies` hash declares which parent tags accept the new component as a child. The `ending_tags` list tells the parser to treat content as raw HTML (like `mj-text`). Both are optional.
|
|
122
|
+
|
|
123
|
+
Once registered, the component works in MJML markup and is validated like any built-in component.
|
|
124
|
+
|
|
125
|
+
## `.mjmlrc` config file
|
|
126
|
+
|
|
127
|
+
Place a `.mjmlrc` file (JSON) in your project root to auto-register custom components and set default compiler options:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"packages": [
|
|
132
|
+
"./lib/mjml_components/mj_rating.rb"
|
|
133
|
+
],
|
|
134
|
+
"options": {
|
|
135
|
+
"beautify": true,
|
|
136
|
+
"validation-level": "soft"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- **`packages`** — Ruby files to `require`. Each file should call `MjmlRb.register_component` to register its components.
|
|
142
|
+
- **`options`** — Default compiler options. CLI flags and programmatic options override these.
|
|
143
|
+
|
|
144
|
+
The CLI loads `.mjmlrc` automatically from the working directory. For the library API, load it explicitly:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
MjmlRb::ConfigFile.load("/path/to/project")
|
|
148
|
+
result = MjmlRb.mjml2html(mjml_string)
|
|
149
|
+
```
|
|
150
|
+
|
|
98
151
|
## Architecture
|
|
99
152
|
|
|
100
153
|
The compile pipeline is intentionally simple and fully Ruby-based:
|
data/lib/mjml-rb/cli.rb
CHANGED
|
@@ -24,6 +24,11 @@ module MjmlRb
|
|
|
24
24
|
parser.parse!(argv)
|
|
25
25
|
options[:positional] = argv
|
|
26
26
|
|
|
27
|
+
rc_config = ConfigFile.load
|
|
28
|
+
if rc_config[:options]
|
|
29
|
+
options[:config] = rc_config[:options].merge(options[:config])
|
|
30
|
+
end
|
|
31
|
+
|
|
27
32
|
input_mode, input_values = resolve_input(options)
|
|
28
33
|
output_mode = resolve_output(options)
|
|
29
34
|
config = options[:config]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
|
|
3
|
+
module MjmlRb
|
|
4
|
+
class ComponentRegistry
|
|
5
|
+
attr_reader :custom_components, :custom_dependencies, :custom_ending_tags
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@custom_components = []
|
|
9
|
+
@custom_dependencies = {}
|
|
10
|
+
@custom_ending_tags = Set.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def register(klass, dependencies: {}, ending_tags: [])
|
|
14
|
+
validate_component!(klass)
|
|
15
|
+
@custom_components << klass unless @custom_components.include?(klass)
|
|
16
|
+
dependencies.each do |parent, children|
|
|
17
|
+
@custom_dependencies[parent] = ((@custom_dependencies[parent] || []) + Array(children)).uniq
|
|
18
|
+
end
|
|
19
|
+
@custom_ending_tags.merge(Array(ending_tags))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def component_class_for_tag(tag_name)
|
|
23
|
+
all_component_classes.find { |klass| klass.tags.include?(tag_name) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def dependency_rules
|
|
27
|
+
merged = {}
|
|
28
|
+
Dependencies::RULES.each { |k, v| merged[k] = v.dup }
|
|
29
|
+
@custom_dependencies.each do |parent, children|
|
|
30
|
+
merged[parent] = ((merged[parent] || []) + Array(children)).uniq
|
|
31
|
+
end
|
|
32
|
+
merged
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ending_tags
|
|
36
|
+
Dependencies::ENDING_TAGS | @custom_ending_tags
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def reset!
|
|
40
|
+
@custom_components.clear
|
|
41
|
+
@custom_dependencies.clear
|
|
42
|
+
@custom_ending_tags.clear
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def all_component_classes
|
|
48
|
+
builtin = MjmlRb::Components.constants.filter_map do |name|
|
|
49
|
+
value = MjmlRb::Components.const_get(name)
|
|
50
|
+
value if value.is_a?(Class) && value < MjmlRb::Components::Base
|
|
51
|
+
rescue NameError
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
(builtin + @custom_components).uniq
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def validate_component!(klass)
|
|
58
|
+
raise ArgumentError, "Expected a Class, got #{klass.class}" unless klass.is_a?(Class)
|
|
59
|
+
unless klass.respond_to?(:tags) && klass.respond_to?(:allowed_attributes)
|
|
60
|
+
raise ArgumentError, "Component class must respond to .tags and .allowed_attributes (inherit from MjmlRb::Components::Base)"
|
|
61
|
+
end
|
|
62
|
+
raise ArgumentError, "Component must define at least one tag via TAGS" if klass.tags.empty?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -39,6 +39,7 @@ module MjmlRb
|
|
|
39
39
|
|
|
40
40
|
def render(tag_name:, node:, context:, attrs:, parent:)
|
|
41
41
|
width_pct = context.delete(:_column_width_pct) || 100.0
|
|
42
|
+
mobile_width = context.delete(:_column_mobile_width)
|
|
42
43
|
css_class = attrs["css-class"]
|
|
43
44
|
a = self.class.default_attributes.merge(attrs)
|
|
44
45
|
|
|
@@ -63,7 +64,7 @@ module MjmlRb
|
|
|
63
64
|
"direction" => a["direction"],
|
|
64
65
|
"display" => "inline-block",
|
|
65
66
|
"vertical-align" => vertical_align,
|
|
66
|
-
"width" => "100%"
|
|
67
|
+
"width" => (mobile_width ? mobile_width_value(a, width_pct, context[:container_width]) : "100%")
|
|
67
68
|
)
|
|
68
69
|
|
|
69
70
|
column_markup =
|
|
@@ -168,6 +169,22 @@ module MjmlRb
|
|
|
168
169
|
value && !value.empty?
|
|
169
170
|
end
|
|
170
171
|
|
|
172
|
+
def mobile_width_value(attrs, width_pct, parent_width)
|
|
173
|
+
width = attrs["width"]
|
|
174
|
+
|
|
175
|
+
if present_attr?(width) && width.end_with?("%")
|
|
176
|
+
width
|
|
177
|
+
elsif present_attr?(width) && width.end_with?("px")
|
|
178
|
+
parent_width_px = parse_pixel_value(parent_width || "600px")
|
|
179
|
+
return "100%" if parent_width_px.zero?
|
|
180
|
+
|
|
181
|
+
percentage = (parse_pixel_value(width) / parent_width_px) * 100
|
|
182
|
+
"#{percentage.to_s.sub(/\.?0+$/, "")}%"
|
|
183
|
+
else
|
|
184
|
+
"#{width_pct.to_f.to_s.sub(/\.?0+$/, "")}%"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
171
188
|
def with_child_container_width(context, attrs, width_pct)
|
|
172
189
|
previous_container_width = context[:container_width]
|
|
173
190
|
context[:container_width] = child_container_width(context, attrs, width_pct)
|
|
@@ -77,6 +77,7 @@ module MjmlRb
|
|
|
77
77
|
width_pct = widths[column_index] || 100.0
|
|
78
78
|
column_index += 1
|
|
79
79
|
context[:_column_width_pct] = width_pct
|
|
80
|
+
context[:_column_mobile_width] = true
|
|
80
81
|
td_style = style_join(
|
|
81
82
|
"vertical-align" => resolved_attributes(child, context)["vertical-align"] || "top",
|
|
82
83
|
"width" => "#{(group_width_px * width_pct / 100.0).round}px"
|
|
@@ -238,11 +238,11 @@ module MjmlRb
|
|
|
238
238
|
def hero_container_width(attrs, container_width)
|
|
239
239
|
width = parse_unit_value(container_width)
|
|
240
240
|
content_width = width - padding_side(attrs, "left") - padding_side(attrs, "right")
|
|
241
|
-
|
|
241
|
+
px_length([content_width, 0].max)
|
|
242
242
|
end
|
|
243
243
|
|
|
244
244
|
def normalize_container_width(value)
|
|
245
|
-
|
|
245
|
+
px_length(parse_unit_value(value))
|
|
246
246
|
end
|
|
247
247
|
|
|
248
248
|
def padding_side(attrs, side)
|
|
@@ -280,6 +280,17 @@ module MjmlRb
|
|
|
280
280
|
def blank?(value)
|
|
281
281
|
value.nil? || value.to_s.empty?
|
|
282
282
|
end
|
|
283
|
+
|
|
284
|
+
def px_length(value)
|
|
285
|
+
"#{format_number(value)}px"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def format_number(value)
|
|
289
|
+
number = value.to_f
|
|
290
|
+
return number.to_i.to_s if number == number.to_i
|
|
291
|
+
|
|
292
|
+
number.to_s.sub(/\.?0+$/, "")
|
|
293
|
+
end
|
|
283
294
|
end
|
|
284
295
|
end
|
|
285
296
|
end
|
|
@@ -64,7 +64,7 @@ module MjmlRb
|
|
|
64
64
|
render_wrapper(node, context, attrs)
|
|
65
65
|
else
|
|
66
66
|
a = self.class.default_attributes.merge(attrs)
|
|
67
|
-
if a["full-width"] == "full-width"
|
|
67
|
+
if a["full-width"] == "full-width" && !context[:_inside_full_width_wrapper]
|
|
68
68
|
render_full_width_section(node, context, a)
|
|
69
69
|
else
|
|
70
70
|
render_section(node, context, a)
|
|
@@ -254,8 +254,8 @@ module MjmlRb
|
|
|
254
254
|
|
|
255
255
|
# Build v:fill attributes
|
|
256
256
|
fill_pairs = [
|
|
257
|
-
["origin", "#{v_origin_x}, #{v_origin_y}"],
|
|
258
|
-
["position", "#{v_pos_x}, #{v_pos_y}"],
|
|
257
|
+
["origin", "#{format_vml_number(v_origin_x)}, #{format_vml_number(v_origin_y)}"],
|
|
258
|
+
["position", "#{format_vml_number(v_pos_x)}, #{format_vml_number(v_pos_y)}"],
|
|
259
259
|
["src", bg_url],
|
|
260
260
|
["color", bg_color],
|
|
261
261
|
["type", vml_type]
|
|
@@ -287,6 +287,13 @@ module MjmlRb
|
|
|
287
287
|
end
|
|
288
288
|
end
|
|
289
289
|
|
|
290
|
+
def format_vml_number(value)
|
|
291
|
+
number = value.to_f
|
|
292
|
+
return number.to_i.to_s if number == number.to_i
|
|
293
|
+
|
|
294
|
+
number.to_s.sub(/\.?0+$/, "")
|
|
295
|
+
end
|
|
296
|
+
|
|
290
297
|
def vml_size_attributes(bg_size)
|
|
291
298
|
case bg_size
|
|
292
299
|
when "cover"
|
|
@@ -639,7 +646,9 @@ module MjmlRb
|
|
|
639
646
|
box_width = container_px - pad_left - pad_right - border_left - border_right
|
|
640
647
|
|
|
641
648
|
previous_container_width = context[:container_width]
|
|
649
|
+
previous_full_width_wrapper = context[:_inside_full_width_wrapper]
|
|
642
650
|
context[:container_width] = "#{box_width}px"
|
|
651
|
+
context[:_inside_full_width_wrapper] = full_width
|
|
643
652
|
|
|
644
653
|
div_attrs = {"class" => (full_width ? nil : css_class), "style" => div_style}
|
|
645
654
|
inner = merge_outlook_conditionals(render_wrapped_children_wrapper(node, context, container_px, a["gap"]))
|
|
@@ -686,6 +695,7 @@ module MjmlRb
|
|
|
686
695
|
end
|
|
687
696
|
ensure
|
|
688
697
|
context[:container_width] = previous_container_width if previous_container_width
|
|
698
|
+
context[:_inside_full_width_wrapper] = previous_full_width_wrapper
|
|
689
699
|
end
|
|
690
700
|
|
|
691
701
|
# Wrap each child mj-section/mj-wrapper in an Outlook conditional <tr><td>.
|
|
@@ -703,7 +713,7 @@ module MjmlRb
|
|
|
703
713
|
child_attrs = resolved_attributes(child, context)
|
|
704
714
|
child_css = child_attrs["css-class"]
|
|
705
715
|
outlook_class = suffix_css_classes(child_css)
|
|
706
|
-
td_open = %(<!--[if mso | IE]><tr><td class="#{escape_attr(outlook_class)}" width="#{container_px}
|
|
716
|
+
td_open = %(<!--[if mso | IE]><tr><td class="#{escape_attr(outlook_class)}" width="#{container_px}" ><![endif]-->)
|
|
707
717
|
td_close = %(<!--[if mso | IE]></td></tr><![endif]-->)
|
|
708
718
|
child_html = with_wrapper_child_gap(context, index.zero? ? nil : gap) do
|
|
709
719
|
render_node(child, context, parent: "mj-wrapper")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module MjmlRb
|
|
4
|
+
class ConfigFile
|
|
5
|
+
DEFAULT_NAME = ".mjmlrc"
|
|
6
|
+
|
|
7
|
+
def self.load(dir = Dir.pwd)
|
|
8
|
+
path = File.join(dir, DEFAULT_NAME)
|
|
9
|
+
return {} unless File.exist?(path)
|
|
10
|
+
|
|
11
|
+
raw = JSON.parse(File.read(path))
|
|
12
|
+
config = {}
|
|
13
|
+
|
|
14
|
+
if raw["packages"].is_a?(Array)
|
|
15
|
+
raw["packages"].each do |pkg_path|
|
|
16
|
+
resolved = File.expand_path(pkg_path, dir)
|
|
17
|
+
require resolved
|
|
18
|
+
end
|
|
19
|
+
config[:packages_loaded] = raw["packages"]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if raw["options"].is_a?(Hash)
|
|
23
|
+
config[:options] = raw["options"].each_with_object({}) do |(k, v), memo|
|
|
24
|
+
memo[k.to_s.tr("-", "_").to_sym] = v
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
config
|
|
29
|
+
rescue JSON::ParserError => e
|
|
30
|
+
warn "WARNING: Failed to parse #{path}: #{e.message}"
|
|
31
|
+
{}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/mjml-rb/renderer.rb
CHANGED
|
@@ -113,6 +113,7 @@ module MjmlRb
|
|
|
113
113
|
|
|
114
114
|
def build_html_document(content, context)
|
|
115
115
|
content = minify_outlook_conditionals(content)
|
|
116
|
+
content = apply_html_attributes_to_content(content, context)
|
|
116
117
|
title = context[:title].to_s
|
|
117
118
|
preview = context[:preview]
|
|
118
119
|
head_raw = Array(context[:head_raw]).join("\n")
|
|
@@ -165,7 +166,6 @@ module MjmlRb
|
|
|
165
166
|
</html>
|
|
166
167
|
HTML
|
|
167
168
|
|
|
168
|
-
html = apply_html_attributes(html, context)
|
|
169
169
|
html = apply_inline_styles(html, context)
|
|
170
170
|
html = merge_outlook_conditionals(html)
|
|
171
171
|
before_doctype.empty? ? html : "#{before_doctype}\n#{html}"
|
|
@@ -307,23 +307,33 @@ module MjmlRb
|
|
|
307
307
|
end
|
|
308
308
|
end
|
|
309
309
|
|
|
310
|
-
def
|
|
310
|
+
def apply_html_attributes_to_content(content, context)
|
|
311
311
|
rules = context[:html_attributes] || {}
|
|
312
|
-
return
|
|
312
|
+
return content if rules.empty?
|
|
313
313
|
|
|
314
|
-
|
|
314
|
+
root = html_attributes_fragment_root(content, context)
|
|
315
315
|
|
|
316
316
|
rules.each do |selector, attrs|
|
|
317
317
|
next if selector.empty? || attrs.empty?
|
|
318
318
|
|
|
319
|
-
select_nodes(
|
|
319
|
+
select_nodes(root, selector).each do |node|
|
|
320
320
|
attrs.each do |name, value|
|
|
321
321
|
node[name] = value.to_s
|
|
322
322
|
end
|
|
323
323
|
end
|
|
324
324
|
end
|
|
325
325
|
|
|
326
|
-
|
|
326
|
+
root.inner_html
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def html_attributes_fragment_root(content, context)
|
|
330
|
+
wrapper_attrs = {
|
|
331
|
+
"data-mjml-body-root" => "true",
|
|
332
|
+
"lang" => context[:lang],
|
|
333
|
+
"dir" => context[:dir]
|
|
334
|
+
}
|
|
335
|
+
fragment = Nokogiri::HTML::DocumentFragment.parse("<div#{html_attrs(wrapper_attrs)}>#{content}</div>")
|
|
336
|
+
fragment.at_css("div[data-mjml-body-root='true']")
|
|
327
337
|
end
|
|
328
338
|
|
|
329
339
|
def apply_inline_styles(html, context)
|
|
@@ -638,6 +648,11 @@ module MjmlRb
|
|
|
638
648
|
register_component(registry, Components::Section.new(self))
|
|
639
649
|
register_component(registry, Components::Column.new(self))
|
|
640
650
|
register_component(registry, Components::Spacer.new(self))
|
|
651
|
+
|
|
652
|
+
MjmlRb.component_registry.custom_components.each do |klass|
|
|
653
|
+
register_component(registry, klass.new(self))
|
|
654
|
+
end
|
|
655
|
+
|
|
641
656
|
registry
|
|
642
657
|
end
|
|
643
658
|
end
|
data/lib/mjml-rb/validator.rb
CHANGED
|
@@ -49,7 +49,7 @@ module MjmlRb
|
|
|
49
49
|
validate_supported_attributes(node, errors)
|
|
50
50
|
validate_attribute_types(node, errors)
|
|
51
51
|
|
|
52
|
-
return if
|
|
52
|
+
return if MjmlRb.component_registry.ending_tags.include?(node.tag_name)
|
|
53
53
|
|
|
54
54
|
node.element_children.each { |child| walk(child, errors) }
|
|
55
55
|
end
|
|
@@ -66,9 +66,9 @@ module MjmlRb
|
|
|
66
66
|
def validate_allowed_children(node, errors)
|
|
67
67
|
# Ending-tag components treat content as raw HTML; REXML still parses
|
|
68
68
|
# children structurally, so skip child validation for those tags.
|
|
69
|
-
return if
|
|
69
|
+
return if MjmlRb.component_registry.ending_tags.include?(node.tag_name)
|
|
70
70
|
|
|
71
|
-
allowed =
|
|
71
|
+
allowed = MjmlRb.component_registry.dependency_rules[node.tag_name]
|
|
72
72
|
return unless allowed
|
|
73
73
|
|
|
74
74
|
node.element_children.each do |child|
|
|
@@ -134,12 +134,7 @@ module MjmlRb
|
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
def component_class_for_tag(tag_name)
|
|
137
|
-
MjmlRb
|
|
138
|
-
value = MjmlRb::Components.const_get(name)
|
|
139
|
-
value if value.is_a?(Class) && value < MjmlRb::Components::Base
|
|
140
|
-
rescue NameError
|
|
141
|
-
nil
|
|
142
|
-
end.find { |klass| klass.tags.include?(tag_name) }
|
|
137
|
+
MjmlRb.component_registry.component_class_for_tag(tag_name)
|
|
143
138
|
end
|
|
144
139
|
|
|
145
140
|
def known_tag?(tag_name)
|
data/lib/mjml-rb/version.rb
CHANGED
data/lib/mjml-rb.rb
CHANGED
|
@@ -2,6 +2,8 @@ require_relative "mjml-rb/version"
|
|
|
2
2
|
require_relative "mjml-rb/result"
|
|
3
3
|
require_relative "mjml-rb/ast_node"
|
|
4
4
|
require_relative "mjml-rb/dependencies"
|
|
5
|
+
require_relative "mjml-rb/component_registry"
|
|
6
|
+
require_relative "mjml-rb/config_file"
|
|
5
7
|
require_relative "mjml-rb/parser"
|
|
6
8
|
require_relative "mjml-rb/renderer"
|
|
7
9
|
require_relative "mjml-rb/compiler"
|
|
@@ -43,6 +45,14 @@ module MjmlRb
|
|
|
43
45
|
ActionView::Template.register_template_handler(:mjml, TemplateHandler.new)
|
|
44
46
|
end
|
|
45
47
|
|
|
48
|
+
def register_component(klass, dependencies: {}, ending_tags: [])
|
|
49
|
+
component_registry.register(klass, dependencies: dependencies, ending_tags: ending_tags)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def component_registry
|
|
53
|
+
@component_registry ||= ComponentRegistry.new
|
|
54
|
+
end
|
|
55
|
+
|
|
46
56
|
def mjml2html(mjml, options = {})
|
|
47
57
|
Compiler.new(options).compile(mjml).to_h
|
|
48
58
|
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.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Andriichuk
|
|
@@ -53,6 +53,7 @@ files:
|
|
|
53
53
|
- lib/mjml-rb/ast_node.rb
|
|
54
54
|
- lib/mjml-rb/cli.rb
|
|
55
55
|
- lib/mjml-rb/compiler.rb
|
|
56
|
+
- lib/mjml-rb/component_registry.rb
|
|
56
57
|
- lib/mjml-rb/components/accordion.rb
|
|
57
58
|
- lib/mjml-rb/components/attributes.rb
|
|
58
59
|
- lib/mjml-rb/components/base.rb
|
|
@@ -75,6 +76,7 @@ files:
|
|
|
75
76
|
- lib/mjml-rb/components/spacer.rb
|
|
76
77
|
- lib/mjml-rb/components/table.rb
|
|
77
78
|
- lib/mjml-rb/components/text.rb
|
|
79
|
+
- lib/mjml-rb/config_file.rb
|
|
78
80
|
- lib/mjml-rb/dependencies.rb
|
|
79
81
|
- lib/mjml-rb/parser.rb
|
|
80
82
|
- lib/mjml-rb/railtie.rb
|