portable_text 0.1.0
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 +7 -0
- data/.rubocop.yml +28 -0
- data/.rubocop_todo.yml +85 -0
- data/LICENSE +21 -0
- data/README.md +357 -0
- data/Rakefile +15 -0
- data/lib/portable_text/block_types/base.rb +19 -0
- data/lib/portable_text/block_types/block.rb +6 -0
- data/lib/portable_text/block_types/image.rb +6 -0
- data/lib/portable_text/block_types/list.rb +64 -0
- data/lib/portable_text/block_types/null.rb +6 -0
- data/lib/portable_text/block_types/span.rb +12 -0
- data/lib/portable_text/config.rb +27 -0
- data/lib/portable_text/errors/unimplemented_error.rb +9 -0
- data/lib/portable_text/errors/unknown_serializer_error.rb +9 -0
- data/lib/portable_text/html/base_component.rb +19 -0
- data/lib/portable_text/html/block_types/block.rb +32 -0
- data/lib/portable_text/html/block_types/image.rb +17 -0
- data/lib/portable_text/html/block_types/list.rb +29 -0
- data/lib/portable_text/html/block_types/null.rb +13 -0
- data/lib/portable_text/html/block_types/span.rb +84 -0
- data/lib/portable_text/html/config.rb +48 -0
- data/lib/portable_text/html/configured.rb +11 -0
- data/lib/portable_text/html/mark_defs/base.rb +14 -0
- data/lib/portable_text/html/mark_defs/link.rb +13 -0
- data/lib/portable_text/html/mark_defs/null.rb +13 -0
- data/lib/portable_text/html/rendering.rb +7 -0
- data/lib/portable_text/html/serializer.rb +17 -0
- data/lib/portable_text/mark_defs/base.rb +10 -0
- data/lib/portable_text/mark_defs/link.rb +7 -0
- data/lib/portable_text/mark_defs/null.rb +6 -0
- data/lib/portable_text/plain/serializer.rb +34 -0
- data/lib/portable_text/serializer.rb +97 -0
- data/lib/portable_text/version.rb +5 -0
- data/lib/portable_text.rb +20 -0
- data/sig/portable_text.rbs +4 -0
- metadata +196 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 86981539153c16fb832e1221ed7d068fbb7fe986f807963c78cc0091f1921970
|
4
|
+
data.tar.gz: 65547bf22449bc8318e89f5cc462bed3a8bd48f490913aebeaece3a14fcac329
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d04924353a52afe867c49c270cb9c5ed86b1313ac106a4b25200f03e7c87452d30212fa9d2e6da531a2dc7834f6ada62df69d2a40e91ad62824ff9e8ddd9e91f
|
7
|
+
data.tar.gz: 7d41d5138c9d7b830e7d2d9af3d7ad391a0de562b00c1df62d8d04031c1742df5e650f1d8ba2d21f16e50705506c029adec5f83502d37db51d2316cc83833e20
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
TargetRubyVersion: 3.0
|
5
|
+
|
6
|
+
Style/StringLiterals:
|
7
|
+
EnforcedStyle: double_quotes
|
8
|
+
|
9
|
+
Style/StringLiteralsInInterpolation:
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Style/FrozenStringLiteralComment:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/Documentation:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/ClassAndModuleChildren:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Metrics/AbcSize:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/WordArray:
|
28
|
+
Enabled: false
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2024-04-11 12:20:23 UTC using RuboCop version 1.63.1.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
# This cop supports safe autocorrection (--autocorrect).
|
11
|
+
# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.
|
12
|
+
# SupportedStylesForExponentOperator: space, no_space
|
13
|
+
# SupportedStylesForRationalLiterals: space, no_space
|
14
|
+
Layout/SpaceAroundOperators:
|
15
|
+
Exclude:
|
16
|
+
- 'portable_text.gemspec'
|
17
|
+
|
18
|
+
# Offense count: 1
|
19
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
20
|
+
Metrics/AbcSize:
|
21
|
+
Max: 21
|
22
|
+
|
23
|
+
# Offense count: 1
|
24
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
25
|
+
Metrics/MethodLength:
|
26
|
+
Max: 13
|
27
|
+
|
28
|
+
# Offense count: 14
|
29
|
+
# Configuration parameters: AllowedConstants.
|
30
|
+
Style/Documentation:
|
31
|
+
Exclude:
|
32
|
+
- 'spec/**/*'
|
33
|
+
- 'test/**/*'
|
34
|
+
- 'lib/portable_text.rb'
|
35
|
+
- 'lib/portable_text/block_types/base.rb'
|
36
|
+
- 'lib/portable_text/block_types/list.rb'
|
37
|
+
- 'lib/portable_text/block_types/span.rb'
|
38
|
+
- 'lib/portable_text/config.rb'
|
39
|
+
- 'lib/portable_text/errors/unimpleted_error.rb'
|
40
|
+
- 'lib/portable_text/html/configured.rb'
|
41
|
+
- 'lib/portable_text/html/content/image.rb'
|
42
|
+
- 'lib/portable_text/html/content/list.rb'
|
43
|
+
- 'lib/portable_text/html/mark_def/base.rb'
|
44
|
+
- 'lib/portable_text/html/serializer.rb'
|
45
|
+
- 'lib/portable_text/mark_defs/base.rb'
|
46
|
+
- 'lib/portable_text/mark_defs/link.rb'
|
47
|
+
- 'lib/portable_text/renderer.rb'
|
48
|
+
|
49
|
+
# Offense count: 15
|
50
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
51
|
+
# Configuration parameters: EnforcedStyle.
|
52
|
+
# SupportedStyles: always, always_true, never
|
53
|
+
Style/FrozenStringLiteralComment:
|
54
|
+
Exclude:
|
55
|
+
- 'lib/portable_text/block_types/base.rb'
|
56
|
+
- 'lib/portable_text/block_types/block.rb'
|
57
|
+
- 'lib/portable_text/block_types/image.rb'
|
58
|
+
- 'lib/portable_text/block_types/list.rb'
|
59
|
+
- 'lib/portable_text/block_types/span.rb'
|
60
|
+
- 'lib/portable_text/config.rb'
|
61
|
+
- 'lib/portable_text/errors/unimpleted_error.rb'
|
62
|
+
- 'lib/portable_text/html/configured.rb'
|
63
|
+
- 'lib/portable_text/html/content/image.rb'
|
64
|
+
- 'lib/portable_text/html/content/list.rb'
|
65
|
+
- 'lib/portable_text/html/mark_def/base.rb'
|
66
|
+
- 'lib/portable_text/html/serializer.rb'
|
67
|
+
- 'lib/portable_text/mark_defs/base.rb'
|
68
|
+
- 'lib/portable_text/mark_defs/link.rb'
|
69
|
+
- 'lib/portable_text/renderer.rb'
|
70
|
+
|
71
|
+
# Offense count: 2
|
72
|
+
# This cop supports safe autocorrection (--autocorrect).
|
73
|
+
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
|
74
|
+
# SupportedStyles: single_quotes, double_quotes
|
75
|
+
Style/StringLiterals:
|
76
|
+
Exclude:
|
77
|
+
- 'lib/portable_text/block_types/list.rb'
|
78
|
+
- 'lib/portable_text/html/content/image.rb'
|
79
|
+
|
80
|
+
# Offense count: 1
|
81
|
+
# This cop supports safe autocorrection (--autocorrect).
|
82
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
83
|
+
# URISchemes: http, https
|
84
|
+
Layout/LineLength:
|
85
|
+
Max: 126
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Maxime Souillat
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
# Ruby Portable Text
|
2
|
+
|
3
|
+
A ruby library to render [Portable text](https://www.sanity.io/docs/presenting-block-text)
|
4
|
+
|
5
|
+
|
6
|
+
This gem is meant to be easy to use but is also highly configurable and extensible to match many use cases. By default, it can serialize Portable Text to HTML.
|
7
|
+
|
8
|
+
You can:
|
9
|
+
|
10
|
+
- easily render default PortableText blocks in html without any configuration
|
11
|
+
- create custom block types, mark_defs. Add them or replace existing ones.
|
12
|
+
- create custom HTML serializers for each block type or mark def. Add them or replace existing ones.
|
13
|
+
- customize each HTML node with custom attributes
|
14
|
+
- create a new serializer
|
15
|
+
|
16
|
+
This is a very early release so please open issues if something doesn't work as intended.
|
17
|
+
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
```other
|
22
|
+
gem install portable_text
|
23
|
+
```
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
See [Rails usage](#rails-usage) for usage in rails
|
30
|
+
|
31
|
+
`PortableText::Serializer` takes 2 parameters:
|
32
|
+
|
33
|
+
- `content:` , the portable text Array
|
34
|
+
- `to:` , the rendering format. It defaults to: `:html`
|
35
|
+
|
36
|
+
> You can also use the `:plain` rendering format to show the text without any formatting. The plain serializer is very basic and does not support any configuration, but it can be used as a starting point to create a new serializer.
|
37
|
+
|
38
|
+
PortableText accepts 2 methods, `render` and `convert!`.
|
39
|
+
- `render` renders the content to the specified format defined in the `to` parameter. See [How to render html ?](#how-to-render-html) for more information.
|
40
|
+
- `convert!` converts the content to be used by the library.
|
41
|
+
- It is useful for debugging purposes.
|
42
|
+
- It transforms the keys to ruby format.
|
43
|
+
- It creates the block types and mark definitions as objects, along with their children and marks, and creates a new data structure for list items.
|
44
|
+
|
45
|
+
### How to render html?
|
46
|
+
|
47
|
+
Under the hood, the html renderer uses [Phlex](https://www.phlex.fun), a templating language which allows to create html in plain ruby.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
content = [
|
51
|
+
{
|
52
|
+
"_key": "12345ffxx",
|
53
|
+
"_type": "block",
|
54
|
+
"children": [{
|
55
|
+
"_key": "78910xxyy",
|
56
|
+
"_type": "span",
|
57
|
+
"marks": [],
|
58
|
+
"text": "Hello world!"
|
59
|
+
}],
|
60
|
+
"markDefs": [],
|
61
|
+
"style": "h1"
|
62
|
+
}
|
63
|
+
]
|
64
|
+
|
65
|
+
portable_text = PortableText::Serializer.new(content: content, to: :html)
|
66
|
+
|
67
|
+
# Since the HTML renderer uses Phlex, you can either include the rendering module
|
68
|
+
# and use the render method...
|
69
|
+
include PortableText::Html::Rendering
|
70
|
+
render portable_text.render
|
71
|
+
# => <h1>Hello world!</h1>
|
72
|
+
|
73
|
+
# ... Or you can directly call the Phlex template
|
74
|
+
portable_text.render.call
|
75
|
+
# => <h1>Hello world!</h1>
|
76
|
+
```
|
77
|
+
|
78
|
+
|
79
|
+
### Rails usage
|
80
|
+
|
81
|
+
To use the `PortableText` HTML serializer in rails, you need to add `phlex-rails` to the Gemfile.
|
82
|
+
|
83
|
+
You don’t need to do the whole phlex installation (as described in the Phlex documentation) if you don’t intend to use Phlex to replace your usual templating language.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
gem 'portable_text'
|
87
|
+
gem 'phlex-rails'
|
88
|
+
```
|
89
|
+
|
90
|
+
Then run `bundle install`
|
91
|
+
|
92
|
+
Then, in a controller or a view, just use `render` as usual.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
portable_text = PortableText::Serializer.new(content: content, to: :html)
|
96
|
+
render portable_text.render
|
97
|
+
```
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
## Configuration
|
102
|
+
|
103
|
+
This library is highly customizable through configuration. This is very straightforward as configuration is just a bunch of hashes that either define classes or key-value pairs.
|
104
|
+
|
105
|
+
Since this library is meant to be used for multiple use cases, and possibly several serializers at once, the type definitions are independent from the rendering.
|
106
|
+
|
107
|
+
So, in order to use a block type or a mark definition, one has to:
|
108
|
+
|
109
|
+
- register it in the PortableText configuration, so it can be passed as an object to the serializer
|
110
|
+
- create the template in the serializer (see HTML configuration)
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
### Registering block types
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
content = [
|
118
|
+
{
|
119
|
+
"_key": "12345ffxx",
|
120
|
+
"_type": "myType",
|
121
|
+
...,
|
122
|
+
"url": "https://www.github.com",
|
123
|
+
"image_url": "https://www.myimage.com/my_image.jpg",
|
124
|
+
"children": [{
|
125
|
+
"_key": "78910xxyy",
|
126
|
+
"_type": "span",
|
127
|
+
"marks": [],
|
128
|
+
"text": "Github"
|
129
|
+
}]
|
130
|
+
}
|
131
|
+
]
|
132
|
+
|
133
|
+
# Under the hood, this library uses dry-initializer.
|
134
|
+
# You can use the option method to configure it easily
|
135
|
+
class MyBlock < PortableText::BlockTypes::Base
|
136
|
+
option :url, default: proc { "" }
|
137
|
+
option :image_url, default: proc { "" }
|
138
|
+
# children is an inherited option so it does not need to be added here
|
139
|
+
end
|
140
|
+
|
141
|
+
# Or use plain old ruby. It needs to have attr_readers!
|
142
|
+
class MyBlock < PortableText::BlockTypes::Base
|
143
|
+
attr_reader :url, :image_url
|
144
|
+
|
145
|
+
def initialize(url: "", image_url:, **)
|
146
|
+
super
|
147
|
+
@url = url
|
148
|
+
@image_url = image_url
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# PortableText transforms keys to ruby format so use conventional ruby!
|
153
|
+
# myType becomes my_type.
|
154
|
+
PortableText.config.block.types.merge! { my_block: MyBlock }
|
155
|
+
```
|
156
|
+
|
157
|
+
|
158
|
+
#### Default block types
|
159
|
+
|
160
|
+
It’s probably a good idea to leave the `list` block type untouched. Change at your own risk.
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
{
|
164
|
+
block: BlockTypes::Block,
|
165
|
+
image: BlockTypes::Image,
|
166
|
+
list: BlockTypes::List,
|
167
|
+
span: BlockTypes::Span
|
168
|
+
}
|
169
|
+
```
|
170
|
+
|
171
|
+
|
172
|
+
### Registering mark definitions
|
173
|
+
|
174
|
+
It’s very similar to registering blocks. In case of doubt, refer to the block documentation.
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
content = [
|
178
|
+
{
|
179
|
+
"_key": "12345ffxx",
|
180
|
+
"_type": "block",
|
181
|
+
...,
|
182
|
+
"markDefs": [{ "_key" => "456", "_type" => "newMarkDef" }],
|
183
|
+
}
|
184
|
+
]
|
185
|
+
|
186
|
+
class NewMarkDef < PortableText::MarkDefs::Base
|
187
|
+
option :label, default: proc { "" }
|
188
|
+
end
|
189
|
+
|
190
|
+
PortableText.config.block.mark_defs.merge! { new_mark_def: NewMarkDef }
|
191
|
+
```
|
192
|
+
|
193
|
+
|
194
|
+
|
195
|
+
## Html Serializer configuration
|
196
|
+
|
197
|
+
After registering your block type or mark definition, you need to create its template.
|
198
|
+
|
199
|
+
Each template takes one argument, a block.
|
200
|
+
|
201
|
+
|
202
|
+
### Block Type Template
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
# Let's use the block defined earlier in Registering block types
|
206
|
+
|
207
|
+
class Html::MyBlock < PortableText::Html::BaseComponent
|
208
|
+
# You can include PortableText::Html::Configured
|
209
|
+
# to get access to the html serializer configuration helpers
|
210
|
+
# The #config method allows you to access config values
|
211
|
+
# The #block_type(:key) method is a shortcut to the relevant block_type
|
212
|
+
include PortableText::Html::Configured
|
213
|
+
|
214
|
+
# This library uses dry-initializer
|
215
|
+
# so you can use `param` to create a simple parameter
|
216
|
+
|
217
|
+
# There is no attribute_reader so `param :my_block` generates `@my_block`
|
218
|
+
# This is recommended because some common HTML method names could conflict with
|
219
|
+
# Phlex methods, like `title`.
|
220
|
+
param :my_block
|
221
|
+
|
222
|
+
def view_template
|
223
|
+
div do
|
224
|
+
img(src: @my_block.image_url)
|
225
|
+
link
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
def link
|
232
|
+
a(href: @my_block.url) do
|
233
|
+
@my_block.children.each do |child|
|
234
|
+
render block_type(:span).new(child, mark_defs: nil)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# It needs to have the same key as the one registered before.
|
241
|
+
PortableText::Html.config.block.types.merge! { my_block: Html::MyBlock }
|
242
|
+
```
|
243
|
+
|
244
|
+
|
245
|
+
### Mark Definition template
|
246
|
+
|
247
|
+
Each mark definition takes one argument, a mark definition registered in the configuration.
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
# Let's use the mark definition defined earlier in Registering mark definition
|
251
|
+
|
252
|
+
class Html::NewMarkDef < PortableText::Html::BaseComponent
|
253
|
+
param :mark_def
|
254
|
+
|
255
|
+
# &block is mandatory because mark definitions always contain other nodes
|
256
|
+
def view_template(&block)
|
257
|
+
a(href: @mark_def.url) { block.call }
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# It needs to have the same key as the one registered before.
|
262
|
+
PortableText::Html.config.block.mark_defs.merge! { new_mark_def: Html::NewMarkDef }
|
263
|
+
```
|
264
|
+
|
265
|
+
|
266
|
+
### Customizing html nodes
|
267
|
+
|
268
|
+
Every HTML node is customizable through config and looks this way:
|
269
|
+
|
270
|
+
`h1: { node: :h1 }`
|
271
|
+
|
272
|
+
You can add HTML attributes by appending them. For example:
|
273
|
+
|
274
|
+
```ruby
|
275
|
+
h1: { node: :h1, class: "header" }
|
276
|
+
```
|
277
|
+
|
278
|
+
|
279
|
+
### Configuring marks
|
280
|
+
|
281
|
+
You can configure marks by updating the marks setting.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
PortableText::Html.config.span.marks.merge! { strong: { node: :b, }}
|
285
|
+
|
286
|
+
# Defaults
|
287
|
+
{
|
288
|
+
strong: { node: :strong },
|
289
|
+
em: { node: :em }
|
290
|
+
}
|
291
|
+
```
|
292
|
+
|
293
|
+
|
294
|
+
### Configuring styles
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
PortableText::Html.config.block.styles.merge! { h1: { node: :h3, class: "header" }}
|
298
|
+
|
299
|
+
|
300
|
+
# Defaults
|
301
|
+
{
|
302
|
+
h1: { node: :h1 },
|
303
|
+
h2: { node: :h2 },
|
304
|
+
h3: { node: :h3 },
|
305
|
+
h4: { node: :h4 },
|
306
|
+
h5: { node: :h5 },
|
307
|
+
h6: { node: :h6 },
|
308
|
+
blockquote: { node: :blockquote },
|
309
|
+
normal: { node: :p },
|
310
|
+
li: { node: :li }
|
311
|
+
}
|
312
|
+
```
|
313
|
+
|
314
|
+
|
315
|
+
### Configuring list types
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
PortableText::Html.config.block.list_types.merge! { bullet: { node: :div }}
|
319
|
+
|
320
|
+
# Defaults
|
321
|
+
{
|
322
|
+
bullet: { node: :ul },
|
323
|
+
numeric: { node: :ol }
|
324
|
+
}
|
325
|
+
```
|
326
|
+
|
327
|
+
|
328
|
+
### Adding a new serializer
|
329
|
+
|
330
|
+
You can add a new serializer by creating a new class. You then need to add it the the config.
|
331
|
+
|
332
|
+
The serializer needs to have a `content` method and takes a list of `blocks` as only parameter.
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
class MySerializer
|
336
|
+
def initialize(blocks)
|
337
|
+
@blocks = blocks
|
338
|
+
end
|
339
|
+
|
340
|
+
def content(**options)
|
341
|
+
blocks.map |block|
|
342
|
+
block.type + " - " + block.key + " - " + options[:context]
|
343
|
+
end.join(" ")
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
PortableText.config.serializers.merge! { my_serializer: MySerializer }
|
348
|
+
|
349
|
+
content = [{ "_key": "12345ffxx", "_type": "block", ... }]
|
350
|
+
serializer = PortableText::Serializer.new(content: content, to: :my_serializer)
|
351
|
+
|
352
|
+
# render forwards any keyword argument to the content method in the serializer
|
353
|
+
serializer.render(context: "readme")
|
354
|
+
# => block - 12345ffxx - readme
|
355
|
+
```
|
356
|
+
|
357
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rubocop/rake_task"
|
5
|
+
require "rake/testtask"
|
6
|
+
|
7
|
+
RuboCop::RakeTask.new
|
8
|
+
|
9
|
+
Rake::TestTask.new do |t|
|
10
|
+
t.libs << "test"
|
11
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: %i[test rubocop]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PortableText
|
2
|
+
module BlockTypes
|
3
|
+
class Base
|
4
|
+
extend Dry::Initializer
|
5
|
+
|
6
|
+
option :_key, as: :key
|
7
|
+
option :_type, as: :type
|
8
|
+
option :markDefs, as: :mark_defs, default: proc { [] }
|
9
|
+
option :children, default: proc { [] }
|
10
|
+
option :style, optional: true
|
11
|
+
option :listItem, as: :list_item, optional: true
|
12
|
+
option :level, optional: true
|
13
|
+
option :asset, optional: true
|
14
|
+
|
15
|
+
def list? = false
|
16
|
+
def list_item? = list_item.present?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# A list can contain blocks and child lists.
|
2
|
+
# items: [Block, List, Block, List, Block]
|
3
|
+
|
4
|
+
module PortableText
|
5
|
+
module BlockTypes
|
6
|
+
class List
|
7
|
+
extend Dry::Initializer
|
8
|
+
|
9
|
+
option :list_type, default: proc { 'bullet' }
|
10
|
+
option :items, default: proc { [] }
|
11
|
+
option :level, default: proc { 1 }
|
12
|
+
option :parent, default: proc { nil }
|
13
|
+
|
14
|
+
def list? = true
|
15
|
+
def type = "list"
|
16
|
+
|
17
|
+
# Adds a block to the list.
|
18
|
+
# If the block is on same level as the list, it will be added to the list.
|
19
|
+
# If the block is on a higher level, it will be added to a child list
|
20
|
+
# Else it will be added to the nearest ancestor list.
|
21
|
+
def add(block)
|
22
|
+
if block.level == level
|
23
|
+
items << block
|
24
|
+
elsif block.level > level
|
25
|
+
add_child(block)
|
26
|
+
else
|
27
|
+
ancestor = find_matching_ancestor(block)
|
28
|
+
ancestor.items << block
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def find_matching_ancestor(block)
|
35
|
+
return self if block.level == level
|
36
|
+
|
37
|
+
parent.find_matching_ancestor(block) if block.level != level
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_child(block)
|
41
|
+
sub_list = find_sub_list(block, level)
|
42
|
+
|
43
|
+
if sub_list.present?
|
44
|
+
sub_list.add(block)
|
45
|
+
else
|
46
|
+
items << List.new(items: [block], level: block.level, parent: self)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_sub_list(block, current_level)
|
51
|
+
target = block.level - 1
|
52
|
+
|
53
|
+
sub_list = items.reverse.find do |item|
|
54
|
+
item.list? && item.level == current_level + 1
|
55
|
+
end
|
56
|
+
|
57
|
+
return unless sub_list
|
58
|
+
return sub_list if sub_list.level == target
|
59
|
+
|
60
|
+
sub_list.find_sub_list(block, current_level + 1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module PortableText
|
2
|
+
class Config
|
3
|
+
extend Dry::Configurable
|
4
|
+
|
5
|
+
# Default settings
|
6
|
+
# These can be overridden
|
7
|
+
# Example: PortableText.config.block.types.merge!({ block: MyCustomBlock })
|
8
|
+
|
9
|
+
setting :block do
|
10
|
+
setting :types, default: {
|
11
|
+
block: BlockTypes::Block,
|
12
|
+
image: BlockTypes::Image,
|
13
|
+
list: BlockTypes::List,
|
14
|
+
span: BlockTypes::Span
|
15
|
+
}
|
16
|
+
|
17
|
+
setting :mark_defs, default: {
|
18
|
+
link: MarkDefs::Link
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
setting :serializers, default: {
|
23
|
+
html: Html::Serializer,
|
24
|
+
plain: Plain::Serializer
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|