NotionRubyMapping に Create a page の position object を追加(2) : hkob の雑記録 (390)

はじめに

hkob の雑記録の第390回目(通算786日目)は、NotionRubyMapping に position object を追加します。前回作成したテストをもとに実装を作成します。

create_child_page を実装

今、data_source.rb には create_child_page が以下のように実装されています。

    # @param [Array<Property, Class, String>] assign
    # @return [NotionRubyMapping::Base]
    # @see https://www.notion.so/hkob/DataSource-1462b24502424539a4231bedc07dc2f5#c217ce78020a4de79b720790fce3092d
    def build_child_page(*assign, template_page: nil)
      assign = properties.map { |p| [p.class, p.name] }.flatten if assign.empty?
      page = Page.new assign: assign, parent: {"data_source_id" => @id}, template_page: template_page
      pp = page.properties
      pp.clear_will_update
      yield page, pp if block_given?
      page
    end

    # @param [Array<Property, Class, String>] assign
    # @param [Boolean] dry_run true if dry_run
    # @return [NotionRubyMapping::Base]
    # @see https://www.notion.so/hkob/DataSource-1462b24502424539a4231bedc07dc2f5#c217ce78020a4de79b720790fce3092d
    def create_child_page(*assign, template_page: nil, dry_run: false)
      assign = properties.map { |p| [p.class, p.name] }.flatten if assign.empty?
      page = Page.new assign: assign, parent: {"data_source_id" => @id}, template_page: template_page
      pp = page.properties
      pp.clear_will_update
      yield page, pp if block_given?
      page.save dry_run: dry_run
    end

これをもとに Page#create_child_page を作成します。一昨日のテストの際に記述したように、Page 内に Page を作る時には Title property しか持たないので、引数を受け付けずに TitleProperty を自動的に作成します。また、position を引数で受けるようにしています。

    # @return [NotionRubyMapping::Base]
    def build_child_page(template_page: nil, position: nil)
      page = Page.new assign: [TitleProperty, "title"], parent: {"type" => "page_id", "page_id" => @id},
                      template_page: template_page, position: position
      pp = page.properties
      pp.clear_will_update
      yield page, pp if block_given?
      page
    end

    # @param [Boolean] dry_run true if dry_run
    # @return [NotionRubyMapping::Base]
    def create_child_page(template_page: nil, position: nil, dry_run: false)
      page = Page.new assign: [TitleProperty, "title"], parent: {"type" => "page_id", "page_id" => @id},
                      template_page: template_page, position: position
      pp = page.properties
      pp.clear_will_update
      yield page, pp if block_given?
      page.save dry_run: dry_run
    end

Page に position の引数を設定する必要があります。これは Page の親クラスである Base に設定します。先日追加した template_page の後ろに position の設定を追加しました。

  class Base
    # @param [Hash, nil] json
    # @param [String, nil] id
    # @param [Array<Property, Class, String>] assign
    def initialize(json: nil, id: nil, assign: [], parent: nil, template_page: nil, position: nil)
      @nc = NotionCache.instance
      @json = json
      @id = @nc.hex_id(id || @json && @json["id"])
      @archived = @json && @json["archived"]
      @has_children = @json && @json["has_children"]
      @new_record = true unless parent.nil?
      raise StandardError, "Unknown id" if !is_a?(List) && !is_a?(Block) && @id.nil? && parent.nil?

      payload_json = {}
      payload_json["parent"] = parent if !is_a?(Block) && parent
      if template_page == "default"
        payload_json["template"] = {"type" => "default"}
      elsif template_page
        payload_json["template"] = {"type" => "template_id", "template_id" => template_page.id}
      end
      if %w[page_start page_end].include? position
        payload_json["position"] = {"type" => position}
      elsif position
        payload_json["position"] = {"type" => "after_block", "after_block" => {"id" => position}}
      end
      @payload = Payload.new(payload_json)
      (後略)

これで dry_run のスクリプト作成まではテストが通りました。あとは WebMock の設定をすれば、実際のアクセステストでもテストが通過するはずです。WebMock の関数に以下の設定を追加しました。

    def create_page
      generate_stubs_sub :post, __method__, :pages_path, {
        (中略)
        page_start: [nil, 200, {
          "properties" => {
            "title" => {
              "type" => "title",
              "title" => [
                {
                  "type" => "text",
                  "text" => {
                    "content" => "New Page at page_start",
                    "link" => nil,
                  },
                  "plain_text" => "New Page at page_start",
                  "href" => nil,
                },
              ],
            },
          },
          "parent" => {
            "type" => "page_id",
            "page_id" => TestConnection::POSITION_TEST_PARENT_PAGE_ID,
          },
          "position" => {
            "type" => "page_start",
          },
        }],
        page_end: [nil, 200, {
          "properties" => {
            "title" => {
              "type" => "title",
              "title" => [
                {
                  "type" => "text",
                  "text" => {
                    "content" => "New Page at page_end",
                    "link" => nil,
                  },
                  "plain_text" => "New Page at page_end",
                  "href" => nil,
                },
              ],
            },
          },
          "parent" => {
            "type" => "page_id",
            "page_id" => TestConnection::POSITION_TEST_PARENT_PAGE_ID,
          },
          "position" => {
            "type" => "page_end",
          },
        }],
        after_block: [nil, 200, {
          "properties" => {
            "title" => {
              "type" => "title",
              "title" => [
                {
                  "type" => "text",
                  "text" => {
                    "content" => "New Page at after_block",
                    "link" => nil,
                  },
                  "plain_text" => "New Page at after_block",
                  "href" => nil,
                },
              ],
            },
          },
          "parent" => {
            "type" => "page_id",
            "page_id" => TestConnection::POSITION_TEST_PARENT_PAGE_ID,
          },
          "position" => {
            "type" => "after_block",
            "after_block" => {
              "id" => "2f0d8e4e98ab812ca8f5cce62b2d3f6e",
            },
          },
        }],
      }
    end

最終的にテストは全て通過しました。

  position object for create a page
    when it creates at page_end
      when not dry_run
        is expected to eq "2f0d8e4e98ab818d8366c12b276c6a7c"
        is expected to eq "2f0d8e4e98ab8065b3a2ec9fd4b3e57a"
      when dry_run
        behaves like dry run
          is expected to eq "#!/bin/sh\ncurl -X POST 'https://api.notion.com/v1/pages' \\\n  -H 'Notion-Version: 2025-09-03' \\\n..."page_id\",\"page_id\":\"2f0d8e4e98ab8065b3a2ec9fd4b3e57a\"},\"position\":{\"type\":\"page_end\"}}'"
    when it creates at page_start
      when not dry_run
        is expected to eq "2f0d8e4e98ab812ca8f5cce62b2d3f6e"
        is expected to eq "2f0d8e4e98ab8065b3a2ec9fd4b3e57a"
      when dry_run
        behaves like dry run
          is expected to eq "#!/bin/sh\ncurl -X POST 'https://api.notion.com/v1/pages' \\\n  -H 'Notion-Version: 2025-09-03' \\\n...age_id\",\"page_id\":\"2f0d8e4e98ab8065b3a2ec9fd4b3e57a\"},\"position\":{\"type\":\"page_start\"}}'"
    when it creates at after_block
      when not dry_run
        is expected to eq "2f0d8e4e98ab818b9968fc9ec363ed6b"
        is expected to eq "2f0d8e4e98ab8065b3a2ec9fd4b3e57a"
      when dry_run
        behaves like dry run
          is expected to eq "#!/bin/sh\ncurl -X POST 'https://api.notion.com/v1/pages' \\\n  -H 'Notion-Version: 2025-09-03' \\\n...sition\":{\"type\":\"after_block\",\"after_block\":{\"id\":\"2f0d8e4e98ab812ca8f5cce62b2d3f6e\"}}}'"

Finished in 0.11899 seconds (files took 0.46668 seconds to load)
129 examples, 0 failures

おわりに

これで Page#create_child_page の実装も追加されました。ここまでをまとめて新しいバージョンのリリースをしてしまおうと思います。