notion_to_md 2.5.1 → 3.0.0.beta2

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: bb8852f9013936bcb404e0779e4930e84831e2db4235b2f0bca8268a9e4edaf0
4
- data.tar.gz: d3b9fe50d279cfc14abf20d7f8468d1534b043b73e00ee8123248556c03b43bf
3
+ metadata.gz: 3892953f8ab81cb3c4ff219452e8daf615997b27b41aedcd64984d127fcc2d6e
4
+ data.tar.gz: d85c49651c090a1fbd47d0fd57584b71ec7e76955147c9a4526b60ffd496c354
5
5
  SHA512:
6
- metadata.gz: e49f3a1d040fb7381e8369c774cff8a31a510909c3e7813c4ba8016085075d613d01808a23a8075d9d9246335eadd1e08347225d287c702c07753df44f3f3af7
7
- data.tar.gz: 52806a867c8d24e96e993c4a07741ff13322d14151e0285351c341237a772579f7084913764112f703435e963d598754e326e0c047c29f2d74f4ce6b58f68951
6
+ metadata.gz: 7414f7b60612f7dbf9565b4e5d97d0f6c96c389abd5f0c0d46906711621d99e5e617fcf94e2809e483f244438f9184dacc4ba043bc52d6129909a93ae05229cc
7
+ data.tar.gz: 6a4520688e495502e140e170f69ff78fa55ead84021e0bb8e2cbc99615fd30d87be3bb613ee18a253a0c41403567b0786f44f78f6654c9c272f690aeddb1c176
data/README.md CHANGED
@@ -1,114 +1,122 @@
1
+ <img src="https://ik.imagekit.io/gxidvqvc9/noiton_to_md_logo_white_bg_-OiZSEkqY.png?updatedAt=1756209770491" width="150">
2
+
1
3
  # notion_to_md
2
- Notion Markdown Exporter in Ruby.
3
4
 
4
- The generated document is compliant with the [GitHub Flavored Markdown specification](https://github.github.com/gfm/).
5
+ A Ruby library to export [Notion](https://www.notion.so/) pages and databases to Markdown.
6
+ The output is fully compliant with the [GitHub Flavored Markdown specification](https://github.github.com/gfm/).
7
+
8
+ [![Gem Version](https://badge.fury.io/rb/notion_to_md.svg)](https://badge.fury.io/rb/notion_to_md)
9
+ [![CI](https://github.com/emoriarty/notion_to_md/actions/workflows/ci.yml/badge.svg)](https://github.com/emoriarty/notion_to_md/actions)
10
+
11
+ > [!NOTE]
12
+ > You are reading the documentation for the latest development branch.
13
+ > For the stable **v2.x.x** documentation, see [the v2.x.x branch](https://github.com/emoriarty/notion_to_md/tree/v2.x.x).
5
14
 
6
15
  ## Installation
7
- Use gem to install.
16
+
17
+ Install via RubyGems:
18
+
8
19
  ```bash
9
- $ gem install 'notion_to_md'
20
+ gem install notion_to_md
10
21
  ```
11
22
 
12
- Or add it to the `Gemfile`.
23
+ Or add it to your `Gemfile`:
24
+
13
25
  ```ruby
14
- # Gemfile
15
26
  gem 'notion_to_md'
16
27
  ```
17
28
 
18
- ## Usage
19
- Before using the gem create an integration and generate a secret token. Check [notion getting started guide](https://developers.notion.com/docs/getting-started) to learn more.
20
-
21
- Pass the page id and secret token to the constructor and execute the `convert` method.
29
+ ### Beta version (3.0.0.beta1)
22
30
 
23
- ```ruby
24
- require 'notion_to_md'
31
+ If you want to try the **beta release**, install with the `--pre` flag:
25
32
 
26
- notion_converter = NotionToMd::Converter.new(page_id: 'b91d5...', token: 'secret_...')
27
- md = notion_converter.convert
33
+ ```bash
34
+ gem install notion_to_md --pre
28
35
  ```
29
36
 
30
- Since v2.3 you can also use the convenient `convert` method from the root module.
37
+ Or pin the beta in your Gemfile:
31
38
 
32
39
  ```ruby
33
- md = NotionToMd.convert(page_id: 'b91d5...', token: 'secret_...')
40
+ gem "notion_to_md", "3.0.0.beta1"
34
41
  ```
35
42
 
36
- If the secret token is provided as an environment variable —`NOTION_TOKEN`—, there's no need to pass it as an argument to the constructor.
43
+ ⚠️ This version is under active development. For stable usage, prefer the latest `2.x.x` release.
37
44
 
38
- ```bash
39
- $ export NOTION_TOKEN=<secret_...>
40
- ```
45
+
46
+ ## Quick Start
41
47
 
42
48
  ```ruby
43
- notion_converter = NotionToMd::Converter.new(page_id: 'b91d5...')
44
- md = notion_converter.convert
45
- # or
46
- md = NotionToMd.convert(page_id: 'b91d5...')
49
+ # Convert a Notion page to Markdown
50
+ md = NotionToMd.call(:page, id: 'b91d5...', token: ENV['NOTION_TOKEN'])
51
+ File.write("page.md", md)
47
52
  ```
48
53
 
49
- And that's all. The `md` is a string variable containing the notion document formatted in markdown.
54
+ ## Usage
50
55
 
51
- ### Callable
56
+ Before using the gem, create a Notion integration and obtain a secret token.
57
+ See the [Notion Getting Started Guide](https://developers.notion.com/docs/getting-started) for details.
52
58
 
53
- From version 2.5.0, the `call` method is also available. It's an alias for `convert`.
59
+ ### Pages
54
60
 
55
61
  ```ruby
56
- md = NotionToMd.call(page_id: 'b91d5...')
62
+ md = NotionToMd.call(:page, id: 'b91d5...', token: 'secret_...')
63
+ # or equivalently
64
+ md = NotionToMd.convert(:page, id: 'b91d5...', token: 'secret_...')
57
65
  ```
58
66
 
59
- The `call` method also supports the passing of a block.
67
+ `md` is a string containing the page content in Markdown format.
68
+
69
+ ### Databases
60
70
 
61
71
  ```ruby
62
- NotionToMd.call(page_id: 'b91d5...') { puts _1 }
72
+ mds = NotionToMd.call(:database, id: 'b91d5...')
63
73
  ```
64
74
 
65
- ## Blocks
75
+ `mds` is an array of strings, one per page.
66
76
 
67
- Everything in a notion page body is a [block object](https://developers.notion.com/reference/block#block-object-keys). Therefore, not every type can be mapped to Markdown. Below there's a list of current supported blocks types.
77
+ ### Environment Variables
68
78
 
69
- * `paragraph`
70
- * `heading_1`
71
- * `heading_2`
72
- * `heading_3`
73
- * `bulleted_list_item`
74
- * `numbered_list_item` (supported since v2.3, in previous versions is displayed as `bulleted_list_item`)
75
- * `to_do`
76
- * `image`
77
- * `bookmark`
78
- * `callout`
79
- * `quote`
80
- * `divider`
81
- * `table`
82
- * `embed`
83
- * `code`
84
- * `link_preview`
85
- * `file`
86
- * `pdf`
87
- * `video`
88
- * `equation`
79
+ If your token is stored in `NOTION_TOKEN`, you don’t need to pass it explicitly:
89
80
 
90
- ### Nested blocks
81
+ ```bash
82
+ export NOTION_TOKEN=<secret_...>
83
+ ```
91
84
 
92
- Starting with v2, nested blocks are supported. The permitted blocks to have children are:
85
+ ```ruby
86
+ md = NotionToMd.call(:page, id: 'b91d5...')
87
+ ```
88
+
89
+ ## Supported Blocks
93
90
 
94
- * `paragraph`
95
- * `bulleted_list_item`
96
- * `numbered_list_item`
97
- * `to_do`
91
+ Everything in Notion is a [block object](https://developers.notion.com/reference/block#block-object-keys).
98
92
 
99
- ## Front matter
93
+ | Block type | Nested children supported? |
94
+ |-----------------------------------|----------------------------|
95
+ | paragraph | ✅ |
96
+ | heading_1 / heading_2 / heading_3 | ❌ |
97
+ | bulleted_list_item | ✅ |
98
+ | numbered_list_item | ✅ |
99
+ | to_do | ✅ |
100
+ | image, file, pdf, video | ❌ |
101
+ | bookmark, embed, link_preview | ❌ |
102
+ | callout | ❌ |
103
+ | quote | ❌ |
104
+ | divider | ❌ |
105
+ | table | ❌ |
106
+ | code | ❌ |
107
+ | equation | ❌ |
100
108
 
101
- From version 0.2.0, notion_to_md supports front matter in markdown files.
109
+ ## Front Matter
102
110
 
103
- By default, the front matter section is not included to the document. To do so, provide the `:frontmatter` option set to `true` to `convert` method.
111
+ By default, front matter is **disabled**. Enable it with:
104
112
 
105
113
  ```ruby
106
- NotionToMd::Converter.new(page_id: 'b91d5...').convert(frontmatter: tue)
107
- # or
108
- NotionToMd.convert(page_id: 'b91d5...', frontmatter: true) # Since v2.3
114
+ NotionToMd.call(:page, id: 'b91d5...', frontmatter: true)
109
115
  ```
110
116
 
111
- Default notion [properties](https://developers.notion.com/reference/page#all-pages) are page `id`, `title`, `created_time`, `last_edited_time`, `icon`, `archived` and `cover`.
117
+ ### Examples
118
+
119
+ **Default properties:**
112
120
 
113
121
  ```yml
114
122
  ---
@@ -126,9 +134,7 @@ last_edited_by_object: user
126
134
  ---
127
135
  ```
128
136
 
129
- In addition to default properties, custom properties are also supported.
130
- Custom properties name is [parameterized](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize) and [underscorized](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore) before added to front matter.
131
- For example, two properties named `Multiple Options` and `Tags` will be transformed to `multiple_options` and `tags`, respectively.
137
+ **Custom properties:**
132
138
 
133
139
  ```yml
134
140
  ---
@@ -137,25 +143,30 @@ multiple_options: option1, option2
137
143
  ---
138
144
  ```
139
145
 
140
- The supported property types are:
146
+ Custom property names are [parameterized](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize) and [underscored](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore).
147
+
148
+ Supported property types:
149
+
150
+ - `number`
151
+ - `select`
152
+ - `multi_select`
153
+ - `date`
154
+ - `people`
155
+ - `files`
156
+ - `checkbox`
157
+ - `url`
158
+ - `email`
159
+ - `phone_number`
160
+ - `rich_text` (plain text only)
141
161
 
142
- * `number`
143
- * `select`
144
- * `multi_select`
145
- * `date`
146
- * `people`
147
- * `files`
148
- * `checkbox`
149
- * `url`
150
- * `email`
151
- * `phone_number`
152
- * `rich_text`, supported but in plain text
162
+ > [!NOTE]
163
+ > Advanced types such as `formula`, `relation`, and `rollup` are **not supported**.
164
+ > See the [Notion property value docs](https://developers.notion.com/reference/property-value-object#all-property-values).
153
165
 
154
- Advanced types like `formula`, `relation` and `rollup` are not supported.
166
+ ## Development
155
167
 
156
- Check notion documentation about [property values](https://developers.notion.com/reference/property-value-object#all-property-values) to know more.
168
+ Run the test suite with:
157
169
 
158
- ## Test
159
170
  ```bash
160
171
  rspec
161
172
  ```
@@ -6,38 +6,53 @@ class NotionToMd
6
6
  module Blocks
7
7
  # === NotionToMd::Blocks::Block
8
8
  #
9
- # This class is responsible for converting Notion blocks to markdown.
9
+ # Represents a Notion block and provides functionality
10
+ # to convert it (and its children) into Markdown.
11
+ #
12
+ # A block corresponds to a `Notion::Messages::Message`
13
+ # returned by the [notion-ruby-client](https://github.com/orbit-love/notion-ruby-client).
14
+ #
15
+ # @example Convert a block to Markdown
16
+ # block = NotionToMd::Blocks::Block.new(block: notion_message, children: [])
17
+ # puts block.to_md
18
+ #
19
+ # @see NotionToMd::Blocks::Renderer
10
20
  class Block
11
21
  extend Forwardable
12
22
 
13
- attr_reader :block, :children
23
+ # @return [Notion::Messages::Message] The Notion API block data.
24
+ attr_reader :block
14
25
 
26
+ # @return [Array<NotionToMd::Blocks::Block>] The nested child blocks.
27
+ attr_reader :children
28
+
29
+ # Delegate the `#type` method to the underlying `block`.
15
30
  def_delegators :block, :type
16
31
 
17
- # === Parameters:
18
- # block::
19
- # A {Notion::Messages::Message}[https://github.com/orbit-love/notion-ruby-client/blob/main/lib/notion/messages/message.rb] object.
20
- # children::
21
- # An array of NotionToMd::Block::Block objects.
22
- #
23
- # === Returns
24
- # A Block object.
32
+ # Initialize a new block wrapper.
25
33
  #
34
+ # @param block [Notion::Messages::Message]
35
+ # A Notion block message from the Notion API.
36
+ # @param children [Array<NotionToMd::Blocks::Block>]
37
+ # Nested blocks belonging to this block.
26
38
  def initialize(block:, children: [])
27
39
  @block = block
28
40
  @children = children
29
41
  end
30
42
 
31
- # === Parameters:
32
- # tab_width::
33
- # The number of tabs used to indent the block.
43
+ # Convert the block (and its children) into Markdown.
34
44
  #
35
- # === Returns
36
- # The current block (and its children) converted to a markdown string.
45
+ # Uses {NotionToMd::Blocks::Renderer} to render
46
+ # the Markdown representation of the block.
37
47
  #
48
+ # @param tab_width [Integer] The number of tabs used to indent nested blocks. Defaults to 0.
49
+ #
50
+ # @return [String, nil] The Markdown string, or `nil` if the block type is unsupported.
51
+ #
52
+ # @see NotionToMd::Blocks::Renderer
38
53
  def to_md(tab_width: 0)
39
54
  block_type = block.type.to_sym
40
- md = Types.send(block_type, block[block_type]) + newline
55
+ md = Renderer.send(block_type, block[block_type]) + newline
41
56
  md + build_nested_blocks(tab_width + 1)
42
57
  rescue NoMethodError
43
58
  Logger.info("Unsupported block type: #{block_type}")
@@ -46,21 +61,35 @@ class NotionToMd
46
61
 
47
62
  protected
48
63
 
64
+ # @return [String] Two newlines separating blocks.
49
65
  def newline
50
66
  "\n\n"
51
67
  end
52
68
 
69
+ # Build the Markdown for all nested blocks.
70
+ #
71
+ # @param tab_width [Integer] The indentation level.
72
+ # @return [String]
53
73
  def build_nested_blocks(tab_width)
54
74
  mds = markdownify_children(tab_width).compact
55
75
  indent_children(mds, tab_width).join
56
76
  end
57
77
 
78
+ # Render children blocks into Markdown.
79
+ #
80
+ # @param tab_width [Integer] The indentation level.
81
+ # @return [Array<String, nil>] Markdown strings (or nil if unsupported).
58
82
  def markdownify_children(tab_width)
59
83
  children.map do |nested_block|
60
84
  nested_block.to_md(tab_width: tab_width)
61
85
  end
62
86
  end
63
87
 
88
+ # Indent the Markdown output of children by `tab_width`.
89
+ #
90
+ # @param mds [Array<String>] Markdown strings.
91
+ # @param tab_width [Integer] The indentation level.
92
+ # @return [Array<String>] Indented Markdown strings.
64
93
  def indent_children(mds, tab_width)
65
94
  mds.map do |md|
66
95
  "#{"\t" * tab_width}#{md}"
@@ -4,26 +4,58 @@ class NotionToMd
4
4
  module Blocks
5
5
  # === NotionToMd::Blocks::Factory
6
6
  #
7
- # Factory class to create block instances based on the block type
7
+ # Factory class for instantiating the correct block wrapper class
8
+ # depending on the Notion block type.
9
+ #
10
+ # This is used by {NotionToMd::Page::Builder} when traversing Notion
11
+ # blocks returned from the API.
12
+ #
13
+ # @example Build a block instance from a Notion API message
14
+ # block = notion_client.block_children(block_id: "xxxx").results.first
15
+ # instance = NotionToMd::Blocks::Factory.build(block: block)
16
+ # instance # => NotionToMd::Blocks::Block subclass
17
+ #
18
+ # @see NotionToMd::Blocks::Block
19
+ # @see NotionToMd::Page::Builder
8
20
  class Factory
9
- # rubocop:disable Metrics/MethodLength
10
- def self.build(block:, children: [])
11
- case block.type.to_sym
12
- when :table
13
- TableBlock.new(block: block, children: children)
14
- when :table_row
15
- TableRowBlock.new(block: block, children: children)
16
- when :bulleted_list_item
17
- BulletedListItemBlock.new(block: block, children: children)
18
- when :numbered_list_item
19
- NumberedListItemBlock.new(block: block, children: children)
20
- when :to_do
21
- ToDoListItemBlock.new(block: block, children: children)
22
- else
23
- Blocks::Block.new(block: block, children: children)
21
+ class << self
22
+ # Build the appropriate block wrapper.
23
+ #
24
+ # @param block [Notion::Messages::Message]
25
+ # A Notion block message from the API.
26
+ # @param children [Array<NotionToMd::Blocks::Block>]
27
+ # Optional nested block instances.
28
+ #
29
+ # @return [NotionToMd::Blocks::Block]
30
+ # A block instance of the corresponding subclass:
31
+ #
32
+ # * {TableBlock} for `:table`
33
+ # * {TableRowBlock} for `:table_row`
34
+ # * {BulletedListItemBlock} for `:bulleted_list_item`
35
+ # * {NumberedListItemBlock} for `:numbered_list_item`
36
+ # * {ToDoListItemBlock} for `:to_do`
37
+ # * {NotionToMd::Blocks::Block} for all others
38
+ #
39
+ # @note Custom block classes must implement `#to_md`.
40
+ def call(block:, children: [])
41
+ case block.type.to_sym
42
+ when :table
43
+ TableBlock.new(block: block, children: children)
44
+ when :table_row
45
+ TableRowBlock.new(block: block, children: children)
46
+ when :bulleted_list_item
47
+ BulletedListItemBlock.new(block: block, children: children)
48
+ when :numbered_list_item
49
+ NumberedListItemBlock.new(block: block, children: children)
50
+ when :to_do
51
+ ToDoListItemBlock.new(block: block, children: children)
52
+ else
53
+ Blocks::Block.new(block: block, children: children)
54
+ end
24
55
  end
56
+
57
+ alias build call
25
58
  end
26
- # rubocop:enable Metrics/MethodLength
27
59
  end
28
60
  end
29
61
  end
@@ -4,73 +4,141 @@ class NotionToMd
4
4
  module Blocks
5
5
  # === NotionToMd::Blocks::Normalizer
6
6
  #
7
- # This class is responsible for normalizing blocks of the same type
7
+ # Responsible for normalizing sequences of adjacent blocks of the same type
8
+ # into grouped list blocks (e.g., multiple consecutive `bulleted_list_item`
9
+ # blocks into a single `BulletedListBlock`).
10
+ #
11
+ # This ensures Markdown output has proper list structures instead of
12
+ # repeated, ungrouped items.
13
+ #
14
+ # @example Normalize a sequence of blocks
15
+ # blocks = [
16
+ # BulletedListItemBlock.new(block: block1),
17
+ # BulletedListItemBlock.new(block: block2),
18
+ # NotionToMd::Blocks::Block.new(block: block3)
19
+ # ]
20
+ #
21
+ # normalized = NotionToMd::Blocks::Normalizer.normalize(blocks: blocks)
22
+ # # => [BulletedListBlock(children: [..]), Block(..)]
23
+ #
24
+ # @see NotionToMd::Blocks::Block
25
+ # @see NotionToMd::Blocks::Factory
8
26
  class Normalizer
9
- # === Parameters
10
- # blocks::
11
- # An array of NotionToMd::Blocks::Block.
12
- #
13
- def self.normalize(blocks:)
14
- new(blocks: blocks).normalize
27
+ class << self
28
+ # Normalize a list of blocks.
29
+ #
30
+ # @param blocks [Array<NotionToMd::Blocks::Block>]
31
+ # Raw blocks to normalize.
32
+ #
33
+ # @return [Array<NotionToMd::Blocks::Block>]
34
+ # Normalized blocks where consecutive list items are grouped.
35
+ def call(blocks:)
36
+ new(blocks: blocks).call
37
+ end
15
38
  end
16
39
 
40
+ # @return [Array<NotionToMd::Blocks::Block>]
41
+ # The mutable, normalized block list.
17
42
  attr_reader :normalized_blocks
18
43
 
44
+ # Create a new normalizer.
45
+ #
46
+ # @param blocks [Array<NotionToMd::Blocks::Block>]
47
+ # Raw blocks to normalize.
19
48
  def initialize(blocks:)
20
49
  @normalized_blocks = blocks.dup
21
50
  end
22
51
 
23
- def normalize
52
+ # Run normalization for all supported types:
53
+ #
54
+ # * `:bulleted_list_item`
55
+ # * `:numbered_list_item`
56
+ # * `:to_do`
57
+ #
58
+ # @return [Array<NotionToMd::Blocks::Block>]
59
+ # The final normalized block array.
60
+ def call
24
61
  normalize_for :bulleted_list_item
25
62
  normalize_for :numbered_list_item
26
63
  normalize_for :to_do
64
+ normalized_blocks
27
65
  end
28
66
 
67
+ # Normalize consecutive blocks of the given +type+ into a single grouped block,
68
+ # while preserving original order for everything else.
69
+ #
70
+ # The algorithm scans left-to-right, buffering runs of +type+. When a different
71
+ # type appears (or we reach the end), the buffered run is flushed as a grouped
72
+ # block and the non-matching block is appended as-is.
29
73
  def normalize_for(type)
30
74
  new_blocks = []
31
75
 
32
76
  normalized_blocks.each do |block|
33
77
  if block_of_type?(block, type)
78
+ # Accumulate consecutive blocks of the target type
34
79
  blocks_to_normalize << block
35
80
  else
36
- # When we encounter a block that is not of the provided type,
37
- # we need to normalize the blocks we've collected so far.
38
- # Then we add the current block to the new blocks array.
39
- # This is to keep the order of the blocks as they are in the original array.
81
+ # A different type breaks the run:
82
+ # 1) flush the buffered run as a single grouped block
83
+ # 2) append the current (non-matching) block
40
84
  flush_blocks_to_normalize_into(new_blocks, type)
41
85
  new_blocks << block
42
86
  end
43
87
  end
44
88
 
45
- # If the last block is the provided type, it won't be added to the new blocks array.
46
- # So, we need to normalize the blocks we've collected so far.
89
+ # If the sequence ended with a run of the target type, it hasn’t been
90
+ # emitted yet—flush it now.
47
91
  flush_blocks_to_normalize_into(new_blocks, type)
48
92
 
93
+ # Replace the working set with the newly normalized list
49
94
  normalized_blocks.replace(new_blocks)
50
95
  end
51
96
 
52
97
  private
53
98
 
99
+ # @api private
100
+ # @param block [NotionToMd::Blocks::Block]
101
+ # @param type [Symbol]
102
+ # @return [Boolean] true if the block matches the given type.
54
103
  def block_of_type?(block, type)
55
104
  block.type.to_sym == type
56
105
  end
57
106
 
107
+ # @api private
108
+ # Flush accumulated blocks to the new array as a grouped block.
109
+ #
110
+ # @param new_blocks [Array<NotionToMd::Blocks::Block>]
111
+ # @param type [Symbol]
112
+ # @return [void]
58
113
  def flush_blocks_to_normalize_into(new_blocks, type)
59
114
  return if blocks_to_normalize.empty?
60
115
 
61
116
  new_blocks << new_block_and_reset_blocks_to_normalize(type)
62
117
  end
63
118
 
119
+ # @api private
120
+ # Create a grouped block and reset buffer.
121
+ #
122
+ # @param type [Symbol]
123
+ # @return [NotionToMd::Blocks::Block]
64
124
  def new_block_and_reset_blocks_to_normalize(type)
65
125
  new_block = new_block_for(type, blocks_to_normalize)
66
126
  @blocks_to_normalize = []
67
127
  new_block
68
128
  end
69
129
 
130
+ # @api private
131
+ # @return [Array<NotionToMd::Blocks::Block>]
70
132
  def blocks_to_normalize
71
133
  @blocks_to_normalize ||= []
72
134
  end
73
135
 
136
+ # @api private
137
+ # Create a wrapper block (list) depending on type.
138
+ #
139
+ # @param type [Symbol]
140
+ # @param children [Array<NotionToMd::Blocks::Block>]
141
+ # @return [NotionToMd::Blocks::Block]
74
142
  def new_block_for(type, children)
75
143
  case type
76
144
  when :bulleted_list_item
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './bulleted_list_item_block'
4
-
5
3
  class NotionToMd
6
4
  module Blocks
7
5
  class NumberedListItemBlock < BulletedListItemBlock
@@ -15,7 +13,7 @@ class NotionToMd
15
13
  # The current block (and its children) converted to a markdown string.
16
14
  #
17
15
  def to_md(tab_width: 0, index: nil)
18
- md = Types.numbered_list_item(block[block.type.to_sym], index) + newline
16
+ md = Renderer.numbered_list_item(block[block.type.to_sym], index) + newline
19
17
  md + build_nested_blocks(tab_width + 1)
20
18
  rescue NoMethodError
21
19
  Logger.info("Unsupported block type: #{block.type}")
@@ -2,7 +2,23 @@
2
2
 
3
3
  class NotionToMd
4
4
  module Blocks
5
- class Types
5
+ # === NotionToMd::Blocks::Renderer
6
+ #
7
+ # Stateless renderer for individual Notion block payloads into
8
+ # GitHub-Flavored Markdown (GFM). Each public class method expects a
9
+ # simplified block hash (already focused on the inner block content)
10
+ # and returns a Markdown string.
11
+ #
12
+ # This class is used by {NotionToMd::Blocks::Block#to_md}.
13
+ #
14
+ # @example Render a paragraph block
15
+ # md = NotionToMd::Blocks::Renderer.paragraph({ rich_text: [{ type: :text, plain_text: "Hello" }] })
16
+ # # => "Hello"
17
+ #
18
+ # @see NotionToMd::Blocks::Block
19
+ # @see NotionToMd::Blocks::Text
20
+ # @see NotionToMd::Blocks::TextAnnotation
21
+ class Renderer
6
22
  class << self
7
23
  def paragraph(block)
8
24
  return blank if block[:rich_text].empty?
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './bulleted_list_item_block'
4
-
5
3
  class NotionToMd
6
4
  module Blocks
7
5
  class ToDoListItemBlock < BulletedListItemBlock