はじめに
hkob の雑記録の第439回目(通算13日目)は、NotionRubyMapping の 2026-03-11 への対応を記録していきます。今日は先日追加された「見出し4」に対応します。
見出し4の読み込み
まずはテストデータを作成します。Heading 4 と Toggle Heading 4 の取得スクリプトを作成しました。
curl 'https://api.notion.com/v1/blocks/32ad8e4e98ab80f1ae91faf02bbb50d8' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Notion-Version: 2026-03-11'
curl 'https://api.notion.com/v1/blocks/32ad8e4e98ab80fc8ee9d16833fce3a7' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Notion-Version: 2026-03-11'
次に block への Heading 4 と Toggle Heading 4 の追加スクリプトを作成します。
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8035a5b4ea240d930619/children' \ -H 'Notion-Version: 2026-03-11' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"children":[{"type":"heading_4","object":"block","heading_4":{"rich_text":[{"type":"text","text":{"content":"Heading 4","link":null},"plain_text":"Heading 4","href":null}],"color":"yellow_background","is_toggleable":false}}]}'
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8035a5b4ea240d930619/children' \ -H 'Notion-Version: 2026-03-11' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"children":[{"type":"heading_4","object":"block","heading_4":{"rich_text":[{"type":"text","text":{"content":"Toggle Heading 4","link":null},"plain_text":"Toggle Heading 4","href":null}],"color":"yellow_background","is_toggleable":true,"children":[{"type":"bulleted_list_item","object":"block","bulleted_list_item":{"rich_text":[{"type":"text","text":{"content":"inside Toggle Heading 4","link":null},"plain_text":"inside Toggle Heading 4","href":null}],"color":"default"}}]}}]}'
同様に page への Heading 4 と Toggle Heading 4 の追加スクリプトを作成します。
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8061b880f8f45db00383/children' \ -H 'Notion-Version: 2026-03-11' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"children":[{"type":"heading_4","object":"block","heading_4":{"rich_text":[{"type":"text","text":{"content":"Heading 4","link":null},"plain_text":"Heading 4","href":null}],"color":"yellow_background","is_toggleable":false}}]}'
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8061b880f8f45db00383/children' \ -H 'Notion-Version: 2026-03-11' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"children":[{"type":"heading_4","object":"block","heading_4":{"rich_text":[{"type":"text","text":{"content":"Toggle Heading 4","link":null},"plain_text":"Toggle Heading 4","href":null}],"color":"yellow_background","is_toggleable":true,"children":[{"type":"bulleted_list_item","object":"block","bulleted_list_item":{"rich_text":[{"type":"text","text":{"content":"inside Toggle Heading 4","link":null},"plain_text":"inside Toggle Heading 4","href":null}],"color":"default"}}]}}]}'
次に color 変更の update スクリプトを作成します。
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab804c9173c15694f45c5f' \ -H 'Notion-Version: 2026-03-11' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"heading_4":{"color":"green_background","rich_text":[{"type":"text","text":{"content":"Heading 4","link":null},"plain_text":"Heading 4","href":null}]}}'
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab808998eae6b408766d68' \ -H 'Notion-Version: 2026-03-11' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"heading_4":{"color":"green_background","rich_text":[{"type":"text","text":{"content":"Toggle Heading 4","link":null},"plain_text":"Toggle Heading 4","href":null}]}}'
次に文字列変更の update スクリプトを作成します。
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab804c9173c15694f45c5f' \ -H 'Notion-Version: 2025-09-03' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"heading_4":{"rich_text":[{"type":"text","text":{"content":"New Heading 4","link":null},"plain_text":"New Heading 4","href":null}]}}'
#!/bin/sh curl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab808998eae6b408766d68' \ -H 'Notion-Version: 2026-03-11' \ -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \ -H 'Content-Type: application/json' \ --data '{"heading_4":{"rich_text":[{"type":"text","text":{"content":"New Toggle Heading 4","link":null},"plain_text":"New Toggle Heading 4","href":null}]}}'
テストの作成
heading_4 のテストを作成します。
# frozen_string_literal: true require_relative "../../spec_helper" module NotionRubyMapping RSpec.describe Heading4Block do type = "heading_4" it_behaves_like "retrieve block", described_class, TestConnection.block_id(type), false, { "object" => "block", "type" => "heading_4", "heading_4" => { "rich_text" => [ { "type" => "text", "text" => { "content" => "Heading 4", "link" => nil, }, "annotations" => { "bold" => false, "italic" => false, "strikethrough" => false, "underline" => false, "code" => false, "color" => "default", }, "plain_text" => "Heading 4", "href" => nil, }, ], "color" => "default", "is_toggleable" => false, }, } describe "create_child_block" do let(:sub_block) { ParagraphBlock.new "with children" } let(:target) { described_class.new "Heading 4", color: "yellow_background" } it_behaves_like "create child block", described_class, "32ad8e4e98ab810b9039c3cdaf566c42", "32ad8e4e98ab8150a545dd6fa02c14dd" end describe "save (update)" do let(:update_id) { TestConnection.update_block_id(type) } let(:target) { described_class.new "Heading 4", color: "blue_background", id: update_id } it_behaves_like "update block rich text array", type, "New Heading 4" it_behaves_like "update block color", type, "green_background", true end end end
同様に ToggleHeading4Block のテストを作成します。
# frozen_string_literal: true require_relative "../../spec_helper" module NotionRubyMapping RSpec.describe ToggleHeading4Block do type = "heading_4" it_behaves_like "retrieve block", described_class, TestConnection::BLOCK_ID_HASH[:toggle_heading_4], true, { "object" => "block", "type" => "heading_4", "heading_4" => { "rich_text" => [ { "type" => "text", "text" => { "content" => "Toggle Heading 4", "link" => nil, }, "annotations" => { "bold" => false, "italic" => false, "strikethrough" => false, "underline" => false, "code" => false, "color" => "default", }, "plain_text" => "Toggle Heading 4", "href" => nil, }, ], "color" => "default", "is_toggleable" => true, }, } describe "create_child_block" do let(:sub_block) { ParagraphBlock.new "with children" } let(:target) do ToggleHeading4Block.new "Toggle Heading 4", color: "yellow_background", sub_blocks: [ BulletedListItemBlock.new("inside Toggle Heading 4"), ] end it_behaves_like "create child block", described_class, "32ad8e4e98ab813f9496d1ffe8a6b351", "32ad8e4e98ab810faed8df938cf3dfb9" end describe "save (update)" do let(:update_id) { TestConnection.update_block_id(:toggle_heading_4) } let(:target) do described_class.new "Toggle Heading 4", color: "green_background", id: update_id, sub_blocks: [ BulletedListItemBlock.new("inside Toggle Heading 4"), ] end it_behaves_like "update block rich text array", type, "New Toggle Heading 4" it_behaves_like "update block color", type, "green_background", true end end end
Heading4Block の作成
Heading4Block を追加します。
# frozen_string_literal: true module NotionRubyMapping # Notion block class Heading4Block < TextSubBlockColorBaseBlock # @param [RichTextArray, String, Array<String>, RichTextObject, Array<RichTextObject>] text_info # @param [String] color def initialize(text_info = nil, color: nil, json: nil, id: nil, parent: nil) super(text_info, color: color, json: json, id: id, parent: parent) @can_have_children = false end # @return [String] def type "heading_4" end # @param [Boolean] not_update false when update # @return [Hash{String (frozen)->Hash}] def block_json(not_update: true) ans = super ans[type]["is_toggleable"] = false ans end end end
ToggleHeading4Block も追加します。
# frozen_string_literal: true module NotionRubyMapping # Notion block class ToggleHeading4Block < TextSubBlockColorBaseBlock # @return [String] def type "heading_4" end # @param [Boolean] not_update false when update # @return [Hash{String (frozen)->Hash}] def block_json(not_update: true) ans = super ans[type]["is_toggleable"] = true ans end end end
notion_ruby_mapping.rb で require します。
# frozen_string_literal: true require "yaml" require_relative "notion_ruby_mapping/version" { blocks: %w[base block database data_source list page url_caption_base_block bookmark_block breadcrumb_block text_sub_block_color_base_block bulleted_list_item_block callout_block child_base_block child_database_block child_page_block code_block column_list_block column_block divider_block embed_block equation_block file_base_block file_block heading1_block heading2_block heading3_block image_block toggle_heading1_block toggle_heading2_block toggle_heading3_block url_base_block link_preview_block link_to_page_block numbered_list_item_block paragraph_block pdf_block quote_block synced_block table_block table_row_block table_of_contents_block to_do_block toggle_block video_block audio_block heading4_block toggle_heading4_block], controllers: %w[notion_cache payload property_cache query rich_text_array discussion_thread search mermaid mermaid_data_source], objects: %w[rich_text_object emoji_object equation_object file_object mention_object text_object user_object comment_object file_upload_object template_object], properties: %w[property checkbox_property multi_property created_by_property date_base_property created_time_property date_property email_property files_property formula_property last_edited_by_property last_edited_time_property multi_select_property number_property people_property phone_number_property relation_property text_property rich_text_property rollup_property select_property status_property title_property unique_id_property url_property button_property verification_property place_property], }.each do |key, values| values.each do |klass| require_relative "notion_ruby_mapping/#{key}/#{klass}" end end module NotionRubyMapping def configure yield NotionRubyMapping::NotionCache.instance end module_function :configure end
WebMock の修正
今回、WebMock の修正は多岐に渡るので、掲載は省略します。変更の結果、heading_4 のテストは以下のように全て通過しました。
NotionRubyMapping::Heading4Block behaves like retrieve block NotionRubyMapping::Heading4Block block receive id can NotionRubyMapping::Heading4Block have children? behaves like raw json is expected to eq {"heading_4" => {"color" => "default", "is_toggleable" => false, "rich_text" => [{"annotations" => {"...nt" => "Heading 4", "link" => nil}, "type" => "text"}]}, "object" => "block", "type" => "heading_4"} create_child_block behaves like create child block create child block when for page when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8061b880f8f45db00383/children...n_text\":\"Heading 4\",\"href\":null}],\"color\":\"yellow_background\",\"is_toggleable\":false}}]}'" when create is expected to eq "32ad8e4e98ab810b9039c3cdaf566c42" when for block when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8035a5b4ea240d930619/children...n_text\":\"Heading 4\",\"href\":null}],\"color\":\"yellow_background\",\"is_toggleable\":false}}]}'" when create is expected to eq "32ad8e4e98ab8150a545dd6fa02c14dd" save (update) behaves like update block rich text array is expected to eq {"heading_4" => {"rich_text" => [{"href" => nil, "plain_text" => "New Heading 4", "text" => {"content" => "New Heading 4", "link" => nil}, "type" => "text"}]}} when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab804c9173c15694f45c5f' \\\n -...":{\"content\":\"New Heading 4\",\"link\":null},\"plain_text\":\"New Heading 4\",\"href\":null}]}}'" when save is expected to eq "New Heading 4" behaves like update block color is expected to eq {"heading_4" => {"color" => "green_background", "rich_text" => [{"href" => nil, "plain_text" => "Heading 4", "text" => {"content" => "Heading 4", "link" => nil}, "type" => "text"}]}} when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab804c9173c15694f45c5f' \\\n -...,\"text\":{\"content\":\"Heading 4\",\"link\":null},\"plain_text\":\"Heading 4\",\"href\":null}]}}'" when save is expected to eq "green_background" Finished in 0.07212 seconds (files took 0.23818 seconds to load) 13 examples, 0 failures
同様に toggle_heading_4 のテストも通過しました。
NotionRubyMapping::ToggleHeading4Block behaves like retrieve block NotionRubyMapping::ToggleHeading4Block block receive id can NotionRubyMapping::ToggleHeading4Block have children? behaves like raw json is expected to eq {"heading_4" => {"color" => "default", "is_toggleable" => true, "rich_text" => [{"annotations" => {"b..."Toggle Heading 4", "link" => nil}, "type" => "text"}]}, "object" => "block", "type" => "heading_4"} create_child_block behaves like create child block create child block when for page when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8061b880f8f45db00383/children...\":\"inside Toggle Heading 4\",\"href\":null}],\"color\":\"default\"}}],\"is_toggleable\":true}}]}'" when create is expected to eq "32ad8e4e98ab813f9496d1ffe8a6b351" when for block when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/26cd8e4e98ab8035a5b4ea240d930619/children...\":\"inside Toggle Heading 4\",\"href\":null}],\"color\":\"default\"}}],\"is_toggleable\":true}}]}'" when create is expected to eq "32ad8e4e98ab810faed8df938cf3dfb9" save (update) behaves like update block rich text array is expected to eq {"heading_4" => {"rich_text" => [{"href" => nil, "plain_text" => "New Toggle Heading 4", "text" => {"content" => "New Toggle Heading 4", "link" => nil}, "type" => "text"}]}} when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab808998eae6b408766d68' \\\n -...:\"New Toggle Heading 4\",\"link\":null},\"plain_text\":\"New Toggle Heading 4\",\"href\":null}]}}'" when save is expected to eq "New Toggle Heading 4" behaves like update block color is expected to eq {"heading_4" => {"color" => "green_background", "rich_text" => [{"href" => nil, "plain_text" => "Toggle Heading 4", "text" => {"content" => "Toggle Heading 4", "link" => nil}, "type" => "text"}]}} when dry_run behaves like dry run is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/blocks/32ad8e4e98ab808998eae6b408766d68' \\\n -...ontent\":\"Toggle Heading 4\",\"link\":null},\"plain_text\":\"Toggle Heading 4\",\"href\":null}]}}'" when save is expected to eq "green_background" Finished in 0.04343 seconds (files took 0.17045 seconds to load) 13 examples, 0 failures
おわりに
Heading4Block と ToggleHeading4Block のテスト・実装を追加しました。