mjml-rb 0.4.2 → 0.4.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: 0062fce070623c7a358e1af4811bb928a46d7f0cd4e1320a71e16276ccca1d74
4
- data.tar.gz: a4e81e450419963570486009b46f44962d93c25b41cdbe4df721346db6e79fc8
3
+ metadata.gz: a143101aa0a18f9aa1e77160d0810561f9b5e58fe6dbf8c68d9f809b91421d8d
4
+ data.tar.gz: b3fb93b17518384e6ce03e96162a30ef870ab4534bfc3c0fe62775b5db21c594
5
5
  SHA512:
6
- metadata.gz: 880bfd1a06dfe3fb9e8fe2169fe660554cb4e9ca96ee5d6eff1d2b41f6df748af14b753895d7b58a3106e33335516f6f4caadd2fd01717c597846b6104330fbf
7
- data.tar.gz: 94f84fa6a66210d9177db704c16efa6fce09d983eb138bc5d13fdbf9bf967ac553b867907cfce9190a2bd7e14a6e6108c4ff74eb776d16221cbf77689f59ea85
6
+ metadata.gz: a811a85d468e8332b29c533efe918038c68fccb9f7383d82bfceff42954cbe7c9faeccbe72dde57380b422881b620a358db2d6032bef62928053a6b25c001d71
7
+ data.tar.gz: c04f1e1df5835cbf5d0c2f81835ba942aa6f9d3149e592662cffeec22ece7c2841d89f44d4834e5ba29ef8b2891070c402a9da277b04363a7f59541740f2c785
data/README.md CHANGED
@@ -11,198 +11,98 @@
11
11
  > This is a **fully open source project** — feedback, bug reports, test cases,
12
12
  > and pull requests are welcome!
13
13
 
14
- This gem provides a Ruby-first implementation of the main MJML tooling:
14
+ A pure-Ruby MJML v4 compiler no Node.js required.
15
15
 
16
- - library API compatible with `mjml2html`
17
- - command-line interface (`mjml`)
18
- - validation commands
19
- - pure Ruby parser, AST, validator, and renderer
20
- - no Node.js runtime and no shelling out to the official npm renderer
16
+ - Library API compatible with `mjml2html`
17
+ - Command-line interface (`mjml`)
18
+ - Rails integration (ActionView template handler for `.mjml` views)
19
+ - Validation (soft, strict, skip)
20
+ - Custom component support
21
+ - Pure Ruby parser, AST, validator, and renderer
21
22
 
22
- Remaining parity work is tracked in [npm Ruby Parity Audit](docs/PARITY_AUDIT.md).
23
-
24
- ## Compatibility
25
-
26
- This project targets **MJML v4 only**.
23
+ **[Full Usage Guide](docs/USAGE.md)** API reference, all compiler options, component attribute tables, CLI flags, Rails setup, custom components, and more.
27
24
 
28
- - parsing, validation, and rendering are implemented against the MJML v4 document structure
29
- - component rules and attribute validation follow the MJML v4 model
25
+ ## Installation
30
26
 
31
- ## Quick start
32
-
33
- ```bash
34
- bundle install
35
- bundle exec ruby -Ilib -e 'require "mjml-rb"; puts MjmlRb.mjml2html("<mjml><mj-body><mj-section><mj-column><mj-text>Hello</mj-text></mj-column></mj-section></mj-body></mjml>")[:html]'
27
+ ```ruby
28
+ # Gemfile
29
+ gem "mjml-rb"
36
30
  ```
37
31
 
38
- ## CLI usage
32
+ ## Quick Start
39
33
 
40
- ```bash
41
- bundle exec bin/mjml example.mjml -o output.html
42
- bundle exec bin/mjml --validate example.mjml
34
+ ```ruby
35
+ require "mjml-rb"
36
+
37
+ result = MjmlRb.mjml2html(<<~MJML)
38
+ <mjml>
39
+ <mj-body>
40
+ <mj-section>
41
+ <mj-column>
42
+ <mj-text>Hello World!</mj-text>
43
+ </mj-column>
44
+ </mj-section>
45
+ </mj-body>
46
+ </mjml>
47
+ MJML
48
+
49
+ puts result[:html] # compiled HTML
50
+ puts result[:errors] # validation errors (if any)
43
51
  ```
44
52
 
45
- ## Rails integration
46
-
47
- In a Rails app, requiring the gem registers an `ActionView` template handler for
48
- `.mjml` templates through a `Railtie`.
49
-
50
- By default, `.mjml` files are treated as raw MJML/XML source.
53
+ ## CLI
51
54
 
52
- If you want Slim-backed MJML templates, configure it explicitly:
53
-
54
- ```ruby
55
- config.mjml_rb.rails_template_language = :slim
55
+ ```bash
56
+ mjml email.mjml -o email.html # compile to file
57
+ mjml -r "templates/*.mjml" -o output/ # batch compile
58
+ mjml -v email.mjml # validate only
59
+ mjml -i -s < email.mjml # stdin → stdout
56
60
  ```
57
61
 
58
- Supported values are `:slim` and `:haml`.
62
+ See the [Usage Guide — CLI section](docs/USAGE.md#cli) for all flags and config options.
59
63
 
60
- With a configured `rails_template_language`, `.mjml` templates are rendered
61
- through that template engine first, so partials and embedded Ruby can assemble
62
- MJML before the outer template is compiled to HTML. Without that setting,
63
- non-XML MJML source is rejected instead of being guessed.
64
+ ## Rails
64
65
 
65
- For `:slim` or `:haml`, the matching Rails template handler must already be
66
- registered in `ActionView` by the corresponding gem or integration layer.
66
+ Add the gem to your Gemfile that's it. The `.mjml` template handler is registered automatically.
67
67
 
68
- Create a view such as `app/views/user_mailer/welcome.html.mjml`:
69
-
70
- ```mjml
68
+ ```erb
69
+ <!-- app/views/user_mailer/welcome.html.mjml -->
71
70
  <mjml>
72
71
  <mj-body>
73
72
  <mj-section>
74
73
  <mj-column>
75
- <mj-text>Hello from Rails</mj-text>
74
+ <mj-text>Welcome, <%= @user.name %>!</mj-text>
76
75
  </mj-column>
77
76
  </mj-section>
78
77
  </mj-body>
79
78
  </mjml>
80
79
  ```
81
80
 
82
- Then render it like any other Rails template:
83
-
84
81
  ```ruby
85
82
  class UserMailer < ApplicationMailer
86
- def welcome
87
- mail(to: "user@example.com", subject: "Welcome")
83
+ def welcome(user)
84
+ @user = user
85
+ mail(to: user.email, subject: "Welcome")
88
86
  end
89
87
  end
90
88
  ```
91
89
 
92
- Rails rendering uses strict MJML validation by default. You can override the
93
- compiler options in your application config:
94
-
95
- ```ruby
96
- config.mjml_rb.compiler_options = { validation_level: "soft" }
97
- ```
98
-
99
- ## Custom components
90
+ Supports Slim and Haml via `config.mjml_rb.rails_template_language = :slim`. See the [Usage Guide — Rails section](docs/USAGE.md#rails-integration) for full configuration.
100
91
 
101
- You can register custom MJML components written in Ruby:
102
-
103
- ```ruby
104
- class MjRating < MjmlRb::Components::Base
105
- TAGS = ["mj-rating"].freeze
106
- ALLOWED_ATTRIBUTES = { "stars" => "integer", "color" => "color" }.freeze
107
- DEFAULT_ATTRIBUTES = { "stars" => "5", "color" => "#f4b400" }.freeze
108
-
109
- def render(tag_name:, node:, context:, attrs:, parent:)
110
- stars = (attrs["stars"] || "5").to_i
111
- color = attrs["color"] || "#f4b400"
112
- %(<div style="color:#{escape_attr(color)}">#{"\u2605" * stars}</div>)
113
- end
114
- end
115
-
116
- MjmlRb.register_component(MjRating,
117
- dependencies: { "mj-column" => ["mj-rating"] },
118
- ending_tags: ["mj-rating"]
119
- )
120
- ```
121
-
122
- 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.
123
-
124
- Once registered, the component works in MJML markup and is validated like any built-in component.
125
-
126
- ## `.mjmlrc` config file
127
-
128
- Place a `.mjmlrc` file (JSON) in your project root to auto-register custom components and set default compiler options:
92
+ ## Architecture
129
93
 
130
- ```json
131
- {
132
- "packages": [
133
- "./lib/mjml_components/mj_rating.rb"
134
- ],
135
- "options": {
136
- "beautify": true,
137
- "validation-level": "soft"
138
- }
139
- }
140
94
  ```
141
-
142
- - **`packages`** — Ruby files to `require`. Each file should call `MjmlRb.register_component` to register its components.
143
- - **`options`** — Default compiler options. CLI flags and programmatic options override these.
144
-
145
- The CLI loads `.mjmlrc` automatically from the working directory. For the library API, load it explicitly:
146
-
147
- ```ruby
148
- MjmlRb::ConfigFile.load("/path/to/project")
149
- result = MjmlRb.mjml2html(mjml_string)
95
+ MJML string → Parser → AST → Validator → Renderer → HTML
150
96
  ```
151
97
 
152
- ## Architecture
98
+ 1. **Parser** — normalizes source, expands `mj-include`, builds `AstNode` tree
99
+ 2. **Validator** — checks structure, hierarchy, and attribute types
100
+ 3. **Renderer** — resolves head metadata, applies defaults, emits responsive HTML
153
101
 
154
- The compile pipeline is intentionally simple and fully Ruby-based:
155
-
156
- 1. `MjmlRb.mjml2html` calls `MjmlRb::Compiler`.
157
- 2. `MjmlRb::Parser` normalizes the source, expands `mj-include`, and builds an `AstNode` tree.
158
- 3. `MjmlRb::Validator` checks structural rules and supported attributes.
159
- 4. `MjmlRb::Renderer` resolves head metadata, applies component defaults, and renders HTML.
160
- 5. `MjmlRb::Compiler` post-processes the output and returns a `Result`.
161
-
162
- The key architectural idea is that the project uses a small shared AST plus a component registry:
163
-
164
- - the parser produces generic `AstNode` objects instead of component-specific node types
165
- - structure rules live in `lib/mjml-rb/dependencies.rb`
166
- - rendering logic lives in `lib/mjml-rb/components/*`
167
- - head components populate a shared rendering context
168
- - body components consume that context and emit the final HTML
169
-
170
- That split keeps the compiler pipeline predictable:
171
-
172
- - parsing is responsible for source normalization and include expansion
173
- - validation is responsible for MJML structure and attribute checks
174
- - rendering is responsible for HTML generation and responsive email output
175
-
176
- ## Project structure
177
-
178
- The main files are organized like this:
179
-
180
- ```text
181
- lib/mjml-rb.rb # public gem entry point
182
- lib/mjml-rb/compiler.rb # orchestration: parse -> validate -> render
183
- lib/mjml-rb/parser.rb # MJML/XML normalization, includes, AST building
184
- lib/mjml-rb/ast_node.rb # shared tree representation
185
- lib/mjml-rb/validator.rb # structural and attribute validation
186
- lib/mjml-rb/dependencies.rb # allowed parent/child relationships
187
- lib/mjml-rb/renderer.rb # HTML document assembly and render context
188
- lib/mjml-rb/components/* # per-component rendering and head handling
189
- lib/mjml-rb/result.rb # result object returned by the compiler
190
- lib/mjml-rb/cli.rb # CLI implementation used by bin/mjml
191
- docs/ARCHITECTURE.md # deeper architecture notes
192
- docs/PARITY_AUDIT.md # npm vs Ruby parity tracking
193
- ```
102
+ For the full internal walkthrough, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
194
103
 
195
- If you want the full internal walkthrough, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
104
+ Remaining parity work is tracked in [npm Ruby Parity Audit](docs/PARITY_AUDIT.md).
196
105
 
197
- ## Implementation goal
106
+ ## License
198
107
 
199
- > **Ruby MJML pipeline without the Node.js renderer.**
200
- >
201
- > The npm `mjml` package requires Node.js at build time (or runtime via a child
202
- > process / FFI bridge). This project replaces that entire pipeline with a single
203
- > Ruby library: XML parsing, AST construction, attribute resolution, validation,
204
- > and HTML rendering — all in Ruby, with no Node.js runtime and no need to
205
- > shell out to the official MJML renderer. Drop it into a Rails, Sinatra, or
206
- > plain Ruby project and render MJML templates the same way you render ERB — no
207
- > extra runtime, no
208
- > `package.json`, no `node_modules`.
108
+ MIT
@@ -603,7 +603,7 @@ module MjmlRb
603
603
  "overflow" => (has_border_radius ? "hidden" : nil),
604
604
  "margin" => "0px auto",
605
605
  "margin-top" => wrapper_gap,
606
- "max-width" => (full_width ? nil : "#{container_px}px")
606
+ "max-width" => "#{container_px}px"
607
607
  }.merge(full_width ? {} : background_styles)
608
608
  )
609
609
 
@@ -545,6 +545,12 @@ module MjmlRb
545
545
  end
546
546
 
547
547
  def normalize_background_fallbacks!(node, declarations)
548
+ background_image = declaration_value(declarations["background-image"])
549
+ if background_image && !background_image.empty?
550
+ declarations.delete("background") if syncable_background?(declaration_value(declarations["background"]))
551
+ return
552
+ end
553
+
548
554
  background_color = declaration_value(declarations["background-color"])
549
555
  return if background_color.nil? || background_color.empty?
550
556
 
@@ -1,3 +1,3 @@
1
1
  module MjmlRb
2
- VERSION = "0.4.2".freeze
2
+ VERSION = "0.4.3".freeze
3
3
  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.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Andriichuk