notion_to_md 2.2.4 → 2.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fb7426b97e6a8fdd9cd27cd1254939c5ccd3cb3455920ff35071d5c3429a4f1
4
- data.tar.gz: 523b5ff29517de54208a7506970115f11d087c9bb314e4b0a626650760ec89f3
3
+ metadata.gz: bba453442621ce806b36625a5febfd14a2f75d8051c49923d845435535e11574
4
+ data.tar.gz: bc8a6b8a8ba768728cbb88b88a5d4c4069aa173ac9275d5c6a9cb95a37827a43
5
5
  SHA512:
6
- metadata.gz: f9f181ba6bdbece59d9f8f5ad81c05afe86e942fcca913674ca42f04876a158091e8dc21b4eb392d9226c8839a8eea021c223a9d40c681093c9a16f153ec9793
7
- data.tar.gz: 4999b3781d9bf53357f8e5f8585beef8c4c3b501c6b35d360f2593b8596ae5b615979bb20246b2aee911342f5d97824a0c1da882c8d54a544fc14c1e0e61dbb1
6
+ metadata.gz: d7005a6bdfd26d3322a92ee49274ec8006549e90f850cc0d4fbc5907a0e0a9ecc526578b7c8964462563395e7980269a87d43d705fd6a2ccd8aaf26b240b6006
7
+ data.tar.gz: 9c52fe4feff24a090c19c6b736d1af5daaefc8dd5ce0fbe994acf6b41c2c1bbf8631846d71c59b5757219dc72583d50d6c316fc73bb81a453e8e3a02270407d6
data/README.md CHANGED
@@ -25,6 +25,14 @@ notion_converter = NotionToMd::Converter.new(page_id: 'b91d5...', token: 'secret
25
25
  md = notion_converter.convert
26
26
  ```
27
27
 
28
+ Since v2.3 you can also use the convenient `convert` method from the root module.
29
+
30
+ ```ruby
31
+ require 'notion_to_md'
32
+
33
+ md = NotionToMd.convert(page_id: 'b91d5...', token: 'secret_...')
34
+ ```
35
+
28
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.
29
37
 
30
38
  ```bash
@@ -36,6 +44,8 @@ require 'notion_to_md'
36
44
 
37
45
  notion_converter = NotionToMd::Converter.new(page_id: 'b91d5...')
38
46
  md = notion_converter.convert
47
+ # or
48
+ md = NotionToMd.convert(page_id: 'b91d5...')
39
49
  ```
40
50
 
41
51
  And that's all. The `md` is a string variable containing the notion page formatted in markdown.
@@ -49,7 +59,7 @@ Everything in a notion page body is a [block object](https://developers.notion.c
49
59
  * `heading_2`
50
60
  * `heading_3`
51
61
  * `bulleted_list_item`
52
- * `numbered_list_item` as `bulleted_list_item`
62
+ * `numbered_list_item` (supported since v2.3, in previous versions is displayed as `bulleted_list_item`)
53
63
  * `to_do`
54
64
  * `image`
55
65
  * `bookmark`
@@ -77,6 +87,8 @@ By default, the front matter section is not included to the document. To do so,
77
87
 
78
88
  ```ruby
79
89
  NotionToMd::Converter.new(page_id: 'b91d5...').convert(frontmatter: tue)
90
+ # or
91
+ NotionToMd.convert(page_id: 'b91d5...', frontmatter: true) # Since v2.3
80
92
  ```
81
93
 
82
94
  Default notion [properties](https://developers.notion.com/reference/page#all-pages) are page `id`, `title`, `created_time`, `last_edited_time`, `icon`, `archived` and `cover`.
@@ -9,6 +9,8 @@ module NotionToMd
9
9
 
10
10
  attr_reader :block, :children
11
11
 
12
+ def_delegators :block, :type
13
+
12
14
  # === Parameters:
13
15
  # block::
14
16
  # A {Notion::Messages::Message}[https://github.com/orbit-love/notion-ruby-client/blob/main/lib/notion/messages/message.rb] object.
@@ -32,14 +34,18 @@ module NotionToMd
32
34
  #
33
35
  def to_md(tab_width: 0)
34
36
  block_type = block.type.to_sym
35
- md = Types.send(block_type, block[block_type])
37
+ md = Types.send(block_type, block[block_type]) + newline
36
38
  md + build_nested_blocks(tab_width + 1)
37
39
  rescue NoMethodError
38
40
  Logger.info("Unsupported block type: #{block_type}")
39
41
  nil
40
42
  end
41
43
 
42
- private
44
+ protected
45
+
46
+ def newline
47
+ "\n\n"
48
+ end
43
49
 
44
50
  def build_nested_blocks(tab_width)
45
51
  mds = markdownify_children(tab_width).compact
@@ -54,7 +60,7 @@ module NotionToMd
54
60
 
55
61
  def indent_children(mds, tab_width)
56
62
  mds.map do |md|
57
- "\n\n#{"\t" * tab_width}#{md}"
63
+ "#{"\t" * tab_width}#{md}"
58
64
  end
59
65
  end
60
66
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class Builder
6
+ ##
7
+ # Array containing the block types allowed to have nested blocks (children).
8
+ BLOCKS_WITH_PERMITTED_CHILDREN = %i[
9
+ bulleted_list_item
10
+ numbered_list_item
11
+ paragraph
12
+ to_do
13
+ table
14
+ ].freeze
15
+
16
+ # === Parameters
17
+ # block::
18
+ # A {Notion::Messages::Message}[https://github.com/orbit-love/notion-ruby-client/blob/main/lib/notion/messages/message.rb] object.
19
+ #
20
+ # === Returns
21
+ # A boolean indicating if the blocked passed in
22
+ # is permitted to have children based on its type.
23
+ #
24
+ def self.permitted_children_for?(block:)
25
+ BLOCKS_WITH_PERMITTED_CHILDREN.include?(block.type.to_sym) && block.has_children
26
+ end
27
+
28
+ attr_reader :block_id, :fetch_blocks
29
+
30
+ # === Parameters
31
+ # block_id::
32
+ # A string representing a notion block id .
33
+ # fetch_blocks::
34
+ # A block that fetches the blocks from the Notion API.
35
+ #
36
+ # === Returns
37
+ # An array of NotionToMd::Blocks::Block.
38
+ #
39
+ def initialize(block_id:, &fetch_blocks)
40
+ @block_id = block_id
41
+ @fetch_blocks = fetch_blocks
42
+ end
43
+
44
+ # === Parameters
45
+ #
46
+ # === Returns
47
+ # An array of NotionToMd::Blocks::Block.
48
+ #
49
+ def build
50
+ notion_messages = fetch_blocks.call(block_id)
51
+ blocks = notion_messages.results.map do |block|
52
+ children = if Builder.permitted_children_for?(block: block)
53
+ Builder.new(block_id: block.id, &fetch_blocks).build
54
+ else
55
+ []
56
+ end
57
+ Factory.build(block: block, children: children)
58
+ end
59
+
60
+ Normalizer.normalize(blocks: blocks)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class BulletedListBlock < Block
6
+ def initialize(children: [])
7
+ @children = children
8
+ end
9
+
10
+ # === Parameters:
11
+ # tab_width::
12
+ # The number of tabs used to indent the block.
13
+ #
14
+ # === Returns
15
+ # The current block (and its children) converted to a markdown string.
16
+ #
17
+ def to_md(tab_width: 0)
18
+ if tab_width.zero?
19
+ build_nested_blocks(tab_width) + newline
20
+ else
21
+ build_nested_blocks(tab_width)
22
+ end
23
+ end
24
+
25
+ def type
26
+ 'bulleted_list'
27
+ end
28
+
29
+ def newline
30
+ "\n"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class BulletedListItemBlock < Block
6
+ def newline
7
+ "\n"
8
+ end
9
+
10
+ def indent_children(mds, _tab_width)
11
+ mds
12
+ end
13
+ end
14
+ end
15
+ end
@@ -7,6 +7,14 @@ module NotionToMd
7
7
  case block.type.to_sym
8
8
  when :table
9
9
  TableBlock.new(block: block, children: children)
10
+ when :table_row
11
+ TableRowBlock.new(block: block, children: children)
12
+ when :bulleted_list_item
13
+ BulletedListItemBlock.new(block: block, children: children)
14
+ when :numbered_list_item
15
+ NumberedListItemBlock.new(block: block, children: children)
16
+ when :to_do
17
+ ToDoListItemBlock.new(block: block, children: children)
10
18
  else
11
19
  Blocks::Block.new(block: block, children: children)
12
20
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class Normalizer
6
+ # === Parameters
7
+ # blocks::
8
+ # An array of NotionToMd::Blocks::Block.
9
+ #
10
+ def self.normalize(blocks:)
11
+ new(blocks: blocks).normalize
12
+ end
13
+
14
+ attr_reader :blocks, :normalized_blocks
15
+
16
+ def initialize(blocks:)
17
+ @blocks = blocks
18
+ @normalized_blocks = blocks
19
+ end
20
+
21
+ def normalize
22
+ normalize_for :bulleted_list_item
23
+ normalize_for :numbered_list_item
24
+ normalize_for :to_do
25
+ end
26
+
27
+ def normalize_for(type)
28
+ new_blocks = []
29
+
30
+ normalized_blocks.each do |block|
31
+ if block.type.to_sym == type
32
+ blocks_to_normalize << block
33
+ else
34
+ # When we encounter a block that is not of the provided type,
35
+ # we need to normalize the blocks we've collected so far.
36
+ # Then we add the current block to the new blocks array.
37
+ # This is because we want to keep the order of the blocks.
38
+ new_blocks << new_block_and_reset(type, blocks_to_normalize) unless blocks_to_normalize.empty?
39
+ new_blocks << block
40
+ end
41
+ end
42
+
43
+ # If the last block is of the provided type, it won't be added to the new blocks array.
44
+ # So, we need to normalize the blocks we've collected so far.
45
+ new_blocks << new_block_and_reset(type, blocks_to_normalize) unless blocks_to_normalize.empty?
46
+
47
+ normalized_blocks.replace(new_blocks)
48
+ end
49
+
50
+ private
51
+
52
+ def new_block_and_reset(type, children)
53
+ new_block = new_block_for(type, children)
54
+ reset_blocks_to_normalize
55
+ new_block
56
+ end
57
+
58
+ def blocks_to_normalize
59
+ @blocks_to_normalize ||= []
60
+ end
61
+
62
+ def reset_blocks_to_normalize
63
+ @blocks_to_normalize = []
64
+ end
65
+
66
+ def new_block_for(type, children)
67
+ case type
68
+ when :bulleted_list_item
69
+ BulletedListBlock.new(children: children)
70
+ when :numbered_list_item
71
+ NumberedListBlock.new(children: children)
72
+ when :to_do
73
+ ToDoListBlock.new(children: children)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class NumberedListBlock < BulletedListBlock
6
+ def type
7
+ 'numbered_list'
8
+ end
9
+
10
+ protected
11
+
12
+ def markdownify_children(tab_width)
13
+ children.map.with_index do |nested_block, index|
14
+ nested_block.to_md(tab_width: tab_width, index: index + 1)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './bulleted_list_item_block'
4
+
5
+ module NotionToMd
6
+ module Blocks
7
+ class NumberedListItemBlock < BulletedListItemBlock
8
+ # === Parameters:
9
+ # tab_width::
10
+ # The number of tabs used to indent the block.
11
+ # index::
12
+ # The index of the block in its parent's children array used to number the item.
13
+ #
14
+ # === Returns
15
+ # The current block (and its children) converted to a markdown string.
16
+ #
17
+ def to_md(tab_width: 0, index: nil)
18
+ md = Types.numbered_list_item(block[block.type.to_sym], index) + newline
19
+ md + build_nested_blocks(tab_width + 1)
20
+ rescue NoMethodError
21
+ Logger.info("Unsupported block type: #{block.type}")
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -10,17 +10,21 @@ module NotionToMd
10
10
  table_aligment = markdownify_aligment
11
11
  table_body = table[1..table.size]
12
12
 
13
- [table_header, table_aligment, table_body.join("\n")].compact.join("\n")
13
+ [table_header, table_aligment, table_body].compact.join + newline
14
14
  end
15
15
 
16
- private
16
+ protected
17
17
 
18
18
  def row_size
19
19
  @row_size ||= children.first.block.table_row.cells.size
20
20
  end
21
21
 
22
22
  def markdownify_aligment
23
- "|#{row_size.times.map { '---' }.join('|')}|" if block.table.has_column_header
23
+ "|#{row_size.times.map { '---' }.join('|')}|\n" if block.table.has_column_header
24
+ end
25
+
26
+ def newline
27
+ "\n"
24
28
  end
25
29
  end
26
30
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class TableRowBlock < Block
6
+ def newline
7
+ "\n"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class ToDoListBlock < BulletedListBlock
6
+ def type
7
+ 'to_do_list'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './bulleted_list_item_block'
4
+
5
+ module NotionToMd
6
+ module Blocks
7
+ class ToDoListItemBlock < BulletedListItemBlock
8
+ def newline
9
+ "\n"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -36,9 +36,10 @@ module NotionToMd
36
36
  "- #{convert_text(block)}"
37
37
  end
38
38
 
39
- def numbered_list_item(block)
40
- Logger.info('numbered_list_item type not supported. Shown as bulleted_list_item.')
41
- bulleted_list_item(block)
39
+ def numbered_list_item(block, index = nil)
40
+ return bulleted_list_item(block) if index.nil?
41
+
42
+ "#{index}. #{convert_text(block)}"
42
43
  end
43
44
 
44
45
  def to_do(block)
@@ -1,51 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './blocks/block'
3
+ require_relative './blocks/builder'
4
+ require_relative './blocks/normalizer'
4
5
  require_relative './blocks/factory'
5
- require_relative './blocks/table_block'
6
6
  require_relative './blocks/types'
7
+ require_relative './blocks/block'
8
+ require_relative './blocks/table_block'
9
+ require_relative './blocks/table_row_block'
10
+ require_relative './blocks/bulleted_list_block'
11
+ require_relative './blocks/bulleted_list_item_block'
12
+ require_relative './blocks/numbered_list_block'
13
+ require_relative './blocks/numbered_list_item_block'
14
+ require_relative './blocks/to_do_list_block'
15
+ require_relative './blocks/to_do_list_item_block'
7
16
 
8
17
  module NotionToMd
9
18
  module Blocks
10
- ##
11
- # Array containing the block types allowed to have nested blocks (children).
12
- PERMITTED_CHILDREN = [
13
- Types.method(:bulleted_list_item).name,
14
- Types.method(:numbered_list_item).name,
15
- Types.method(:paragraph).name,
16
- Types.method(:to_do).name,
17
- :table
18
- ].freeze
19
-
20
- # === Parameters
21
- # block::
22
- # A {Notion::Messages::Message}[https://github.com/orbit-love/notion-ruby-client/blob/main/lib/notion/messages/message.rb] object.
23
- #
24
- # === Returns
25
- # A boolean indicating if the blocked passed in
26
- # is permitted to have children based on its type.
27
- #
28
- def self.permitted_children?(block:)
29
- PERMITTED_CHILDREN.include?(block.type.to_sym) && block.has_children
30
- end
31
-
32
19
  # === Parameters
33
20
  # block_id::
34
21
  # A string representing a notion block id .
22
+ # fetch_blocks::
23
+ # A block that fetches the blocks from the Notion API.
35
24
  #
36
25
  # === Returns
37
26
  # An array of NotionToMd::Blocks::Block.
38
27
  #
39
28
  def self.build(block_id:, &fetch_blocks)
40
- blocks = fetch_blocks.call(block_id)
41
- blocks.results.map do |block|
42
- children = if permitted_children?(block: block)
43
- build(block_id: block.id, &fetch_blocks)
44
- else
45
- []
46
- end
47
- Factory.build(block: block, children: children)
48
- end
29
+ Builder.new(block_id: block_id, &fetch_blocks).build
49
30
  end
50
31
  end
51
32
  end
@@ -45,7 +45,7 @@ module NotionToMd
45
45
  end
46
46
 
47
47
  def body
48
- @body ||= blocks.map(&:to_md).compact.join("\n\n")
48
+ @body ||= blocks.map(&:to_md).compact.join
49
49
  end
50
50
 
51
51
  def frontmatter
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NotionToMd
4
- VERSION = '2.2.4'
4
+ VERSION = '2.3.0'
5
5
  end
data/lib/notion_to_md.rb CHANGED
@@ -12,3 +12,20 @@ require_relative './notion_to_md/page'
12
12
  require_relative './notion_to_md/blocks'
13
13
  require_relative './notion_to_md/text'
14
14
  require_relative './notion_to_md/text_annotation'
15
+
16
+ module NotionToMd
17
+ # === Parameters
18
+ # page_id::
19
+ # A string representing the notion page id.
20
+ # token::
21
+ # The notion API secret token. The token can replaced by the environment variable NOTION_TOKEN.
22
+ # frontmatter::
23
+ # A boolean indicating whether to include the page properties as frontmatter.
24
+ #
25
+ # === Returns
26
+ # The string that represent the markdown document.
27
+ #
28
+ def self.convert(page_id:, token: nil, frontmatter: false)
29
+ Converter.new(page_id: page_id, token: token).convert(frontmatter: frontmatter)
30
+ end
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: notion_to_md
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.4
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Enrique Arias
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-22 00:00:00.000000000 Z
11
+ date: 2023-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: vcr
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  description: Notion Markdown Exporter in Ruby
112
126
  email: emoriarty81@gmail.com
113
127
  executables: []
@@ -118,8 +132,17 @@ files:
118
132
  - lib/notion_to_md.rb
119
133
  - lib/notion_to_md/blocks.rb
120
134
  - lib/notion_to_md/blocks/block.rb
135
+ - lib/notion_to_md/blocks/builder.rb
136
+ - lib/notion_to_md/blocks/bulleted_list_block.rb
137
+ - lib/notion_to_md/blocks/bulleted_list_item_block.rb
121
138
  - lib/notion_to_md/blocks/factory.rb
139
+ - lib/notion_to_md/blocks/normalizer.rb
140
+ - lib/notion_to_md/blocks/numbered_list_block.rb
141
+ - lib/notion_to_md/blocks/numbered_list_item_block.rb
122
142
  - lib/notion_to_md/blocks/table_block.rb
143
+ - lib/notion_to_md/blocks/table_row_block.rb
144
+ - lib/notion_to_md/blocks/to_do_list_block.rb
145
+ - lib/notion_to_md/blocks/to_do_list_item_block.rb
123
146
  - lib/notion_to_md/blocks/types.rb
124
147
  - lib/notion_to_md/converter.rb
125
148
  - lib/notion_to_md/logger.rb