Class: ContentBlockTools::ContentBlockReference

Inherits:
Data
  • Object
show all
Defined in:
lib/content_block_tools/content_block_reference.rb

Overview

Defines a reference pointer for a Content Block

Constant Summary collapse

SUPPORTED_DOCUMENT_TYPES =

An array of the supported document types

%w[contact content_block_pension content_block_contact].freeze
UUID_REGEX =

The regex used to find UUIDs

/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
CONTENT_ID_ALIAS_REGEX =

The regex used to find content ID aliases

/[a-z0-9\-–—]+/
FIELD_REGEX =

The regex to find optional field names after the UUID, begins with ‘/’

/(\/[a-z0-9_\-–—\/]*)?/
EMBED_REGEX =

The regex used when scanning a document using find_all_in_document

/({{embed:(#{SUPPORTED_DOCUMENT_TYPES.join('|')}):(#{UUID_REGEX}|#{CONTENT_ID_ALIAS_REGEX})#{FIELD_REGEX}}})/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#document_typeString (readonly)

The document type of the content block - this will be used to work out which Presenter will be used to render the content block. All supported document_types are documented in SUPPORTED_DOCUMENT_TYPES

Examples:

content_block_reference.document_type #=> "content_block_pension"

Returns:

  • (String)

    the document type



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/content_block_tools/content_block_reference.rb', line 29

class ContentBlockReference < Data
  # An array of the supported document types
  SUPPORTED_DOCUMENT_TYPES = %w[contact content_block_pension content_block_contact].freeze
  # The regex used to find UUIDs
  UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
  # The regex used to find content ID aliases
  CONTENT_ID_ALIAS_REGEX = /[a-z0-9\-–—]+/
  # The regex to find optional field names after the UUID, begins with '/'
  FIELD_REGEX = /(\/[a-z0-9_\-–—\/]*)?/
  # The regex used when scanning a document using {ContentBlockTools::ContentBlockReference.find_all_in_document}
  EMBED_REGEX = /({{embed:(#{SUPPORTED_DOCUMENT_TYPES.join('|')}):(#{UUID_REGEX}|#{CONTENT_ID_ALIAS_REGEX})#{FIELD_REGEX}}})/

  # Returns if the identifier is an alias
  #
  # @return Boolean
  def identifier_is_alias?
    !identifier.match?(UUID_REGEX)
  end

  # Returns the content store path for this content block reference
  #
  # Constructs a path used to identify and retrieve the content block from the content store.
  # The path follows the format `/content-blocks/{document_type}/{identifier}`.
  #
  # @example
  #   reference = ContentBlockReference.new(document_type: "content_block_contact", identifier: "some-slug", embed_code: "...")
  #   reference.content_store_identifier
  #   #=> "/content-blocks/content_block_contact/some-slug"
  #
  # @return [String] the content store path for this content block
  def content_store_identifier
    "/content-blocks/#{document_type}/#{identifier}"
  end

  class << self
    # Finds all content block references within a document, using `ContentBlockReference::EMBED_REGEX`
    # to scan through the document
    #
    # @return [Array<ContentBlockReference>] An array of content block references
    def find_all_in_document(document)
      document.scan(EMBED_REGEX).map do |match_data|
        ContentBlockReference.from_match_data(match_data)
      end
    end

    # Converts a single embed code string into a ContentBlockReference object
    #
    # Parses an embed code string using {EMBED_REGEX} to extract the document type,
    # identifier, and embed code, then creates a ContentBlockReference instance.
    #
    # @param embed_code [String] the embed code to parse
    # @example Parse an embed code with a UUID
    #   ContentBlockReference.from_string("{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}")
    #   #=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">
    # @example Parse an embed code with a slug
    #   ContentBlockReference.from_string("{{embed:content_block_contact:some-slug}}")
    #   #=> #<ContentBlockReference document_type="content_block_contact" identifier="some-slug" embed_code="{{embed:content_block_contact:some-slug}}">
    # @return [ContentBlockReference] a new ContentBlockReference instance
    # @raise [InvalidEmbedCodeError] if the embed_code doesn't match {EMBED_REGEX} (match_data will be nil)
    # @see from_match_data
    def from_string(embed_code)
      match_data = embed_code.match(/^#{EMBED_REGEX}$/)
      raise InvalidEmbedCodeError unless match_data

      ContentBlockReference.from_match_data(match_data.captures)
    end

    # Converts match data from a regex scan into a ContentBlockReference object
    #
    # This method is used internally by {find_all_in_document} and {from_string} to create
    # ContentBlockReference instances from regex match data. It normalizes the match data
    # by replacing en/em dashes with double/triple dashes (which can occur due to Kramdown's
    # markdown parsing) before creating the object.
    #
    # @param match_data [MatchData, Array] the match data from scanning with {EMBED_REGEX}
    #   Expected to contain: [full_match, document_type, identifier, field]
    # @example Creating from match data
    #   match_data = "{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}".match(EMBED_REGEX)
    #   ContentBlockReference.from_match_data(match_data)
    #   #=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">
    # @return [ContentBlockReference] a new ContentBlockReference instance
    # @api private
    # @see find_all_in_document
    # @see from_string
    # @see prepare_match
    def from_match_data(match_data)
      match = prepare_match(match_data)
      ContentBlockTools.logger.info("Found Content Block Reference: #{match}")
      ContentBlockReference.new(document_type: match[1], identifier: match[2], embed_code: match[0])
    end

  private

    # This replaces an en / em dashes in content block references with double or triple dashes. This can occur
    # because Kramdown (the markdown parser that Govspeak is based on) replaces double dashes with en dashes and
    # triple dashes with em dashes
    def prepare_match(match)
      [
        match[0],
        match[1],
        replace_dashes(match[2]),
        match[3],
      ]
    end

    def replace_dashes(value)
      value&.gsub("", "--")
        &.gsub("", "---")
    end
  end
end

#embed_codeString (readonly)

The embed_code used for a block

Examples:

content_block_reference.embed_code #=> "{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}"

Returns:

  • (String)


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/content_block_tools/content_block_reference.rb', line 29

class ContentBlockReference < Data
  # An array of the supported document types
  SUPPORTED_DOCUMENT_TYPES = %w[contact content_block_pension content_block_contact].freeze
  # The regex used to find UUIDs
  UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
  # The regex used to find content ID aliases
  CONTENT_ID_ALIAS_REGEX = /[a-z0-9\-–—]+/
  # The regex to find optional field names after the UUID, begins with '/'
  FIELD_REGEX = /(\/[a-z0-9_\-–—\/]*)?/
  # The regex used when scanning a document using {ContentBlockTools::ContentBlockReference.find_all_in_document}
  EMBED_REGEX = /({{embed:(#{SUPPORTED_DOCUMENT_TYPES.join('|')}):(#{UUID_REGEX}|#{CONTENT_ID_ALIAS_REGEX})#{FIELD_REGEX}}})/

  # Returns if the identifier is an alias
  #
  # @return Boolean
  def identifier_is_alias?
    !identifier.match?(UUID_REGEX)
  end

  # Returns the content store path for this content block reference
  #
  # Constructs a path used to identify and retrieve the content block from the content store.
  # The path follows the format `/content-blocks/{document_type}/{identifier}`.
  #
  # @example
  #   reference = ContentBlockReference.new(document_type: "content_block_contact", identifier: "some-slug", embed_code: "...")
  #   reference.content_store_identifier
  #   #=> "/content-blocks/content_block_contact/some-slug"
  #
  # @return [String] the content store path for this content block
  def content_store_identifier
    "/content-blocks/#{document_type}/#{identifier}"
  end

  class << self
    # Finds all content block references within a document, using `ContentBlockReference::EMBED_REGEX`
    # to scan through the document
    #
    # @return [Array<ContentBlockReference>] An array of content block references
    def find_all_in_document(document)
      document.scan(EMBED_REGEX).map do |match_data|
        ContentBlockReference.from_match_data(match_data)
      end
    end

    # Converts a single embed code string into a ContentBlockReference object
    #
    # Parses an embed code string using {EMBED_REGEX} to extract the document type,
    # identifier, and embed code, then creates a ContentBlockReference instance.
    #
    # @param embed_code [String] the embed code to parse
    # @example Parse an embed code with a UUID
    #   ContentBlockReference.from_string("{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}")
    #   #=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">
    # @example Parse an embed code with a slug
    #   ContentBlockReference.from_string("{{embed:content_block_contact:some-slug}}")
    #   #=> #<ContentBlockReference document_type="content_block_contact" identifier="some-slug" embed_code="{{embed:content_block_contact:some-slug}}">
    # @return [ContentBlockReference] a new ContentBlockReference instance
    # @raise [InvalidEmbedCodeError] if the embed_code doesn't match {EMBED_REGEX} (match_data will be nil)
    # @see from_match_data
    def from_string(embed_code)
      match_data = embed_code.match(/^#{EMBED_REGEX}$/)
      raise InvalidEmbedCodeError unless match_data

      ContentBlockReference.from_match_data(match_data.captures)
    end

    # Converts match data from a regex scan into a ContentBlockReference object
    #
    # This method is used internally by {find_all_in_document} and {from_string} to create
    # ContentBlockReference instances from regex match data. It normalizes the match data
    # by replacing en/em dashes with double/triple dashes (which can occur due to Kramdown's
    # markdown parsing) before creating the object.
    #
    # @param match_data [MatchData, Array] the match data from scanning with {EMBED_REGEX}
    #   Expected to contain: [full_match, document_type, identifier, field]
    # @example Creating from match data
    #   match_data = "{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}".match(EMBED_REGEX)
    #   ContentBlockReference.from_match_data(match_data)
    #   #=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">
    # @return [ContentBlockReference] a new ContentBlockReference instance
    # @api private
    # @see find_all_in_document
    # @see from_string
    # @see prepare_match
    def from_match_data(match_data)
      match = prepare_match(match_data)
      ContentBlockTools.logger.info("Found Content Block Reference: #{match}")
      ContentBlockReference.new(document_type: match[1], identifier: match[2], embed_code: match[0])
    end

  private

    # This replaces an en / em dashes in content block references with double or triple dashes. This can occur
    # because Kramdown (the markdown parser that Govspeak is based on) replaces double dashes with en dashes and
    # triple dashes with em dashes
    def prepare_match(match)
      [
        match[0],
        match[1],
        replace_dashes(match[2]),
        match[3],
      ]
    end

    def replace_dashes(value)
      value&.gsub("", "--")
        &.gsub("", "---")
    end
  end
end

#identifierString (readonly)

The identifier for a block - can be a UUID or a slug. The UUID will refer to the ‘content_id` of a block within Publishing API, while the slug will refer to a block’s Content ID alias.

Examples:

content_block_reference.identifier #=> "2b92cade-549c-4449-9796-e7a3957f3a86"
content_block_reference.identifier #=> "some-slug"

Returns:

  • (String)


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/content_block_tools/content_block_reference.rb', line 29

class ContentBlockReference < Data
  # An array of the supported document types
  SUPPORTED_DOCUMENT_TYPES = %w[contact content_block_pension content_block_contact].freeze
  # The regex used to find UUIDs
  UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
  # The regex used to find content ID aliases
  CONTENT_ID_ALIAS_REGEX = /[a-z0-9\-–—]+/
  # The regex to find optional field names after the UUID, begins with '/'
  FIELD_REGEX = /(\/[a-z0-9_\-–—\/]*)?/
  # The regex used when scanning a document using {ContentBlockTools::ContentBlockReference.find_all_in_document}
  EMBED_REGEX = /({{embed:(#{SUPPORTED_DOCUMENT_TYPES.join('|')}):(#{UUID_REGEX}|#{CONTENT_ID_ALIAS_REGEX})#{FIELD_REGEX}}})/

  # Returns if the identifier is an alias
  #
  # @return Boolean
  def identifier_is_alias?
    !identifier.match?(UUID_REGEX)
  end

  # Returns the content store path for this content block reference
  #
  # Constructs a path used to identify and retrieve the content block from the content store.
  # The path follows the format `/content-blocks/{document_type}/{identifier}`.
  #
  # @example
  #   reference = ContentBlockReference.new(document_type: "content_block_contact", identifier: "some-slug", embed_code: "...")
  #   reference.content_store_identifier
  #   #=> "/content-blocks/content_block_contact/some-slug"
  #
  # @return [String] the content store path for this content block
  def content_store_identifier
    "/content-blocks/#{document_type}/#{identifier}"
  end

  class << self
    # Finds all content block references within a document, using `ContentBlockReference::EMBED_REGEX`
    # to scan through the document
    #
    # @return [Array<ContentBlockReference>] An array of content block references
    def find_all_in_document(document)
      document.scan(EMBED_REGEX).map do |match_data|
        ContentBlockReference.from_match_data(match_data)
      end
    end

    # Converts a single embed code string into a ContentBlockReference object
    #
    # Parses an embed code string using {EMBED_REGEX} to extract the document type,
    # identifier, and embed code, then creates a ContentBlockReference instance.
    #
    # @param embed_code [String] the embed code to parse
    # @example Parse an embed code with a UUID
    #   ContentBlockReference.from_string("{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}")
    #   #=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">
    # @example Parse an embed code with a slug
    #   ContentBlockReference.from_string("{{embed:content_block_contact:some-slug}}")
    #   #=> #<ContentBlockReference document_type="content_block_contact" identifier="some-slug" embed_code="{{embed:content_block_contact:some-slug}}">
    # @return [ContentBlockReference] a new ContentBlockReference instance
    # @raise [InvalidEmbedCodeError] if the embed_code doesn't match {EMBED_REGEX} (match_data will be nil)
    # @see from_match_data
    def from_string(embed_code)
      match_data = embed_code.match(/^#{EMBED_REGEX}$/)
      raise InvalidEmbedCodeError unless match_data

      ContentBlockReference.from_match_data(match_data.captures)
    end

    # Converts match data from a regex scan into a ContentBlockReference object
    #
    # This method is used internally by {find_all_in_document} and {from_string} to create
    # ContentBlockReference instances from regex match data. It normalizes the match data
    # by replacing en/em dashes with double/triple dashes (which can occur due to Kramdown's
    # markdown parsing) before creating the object.
    #
    # @param match_data [MatchData, Array] the match data from scanning with {EMBED_REGEX}
    #   Expected to contain: [full_match, document_type, identifier, field]
    # @example Creating from match data
    #   match_data = "{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}".match(EMBED_REGEX)
    #   ContentBlockReference.from_match_data(match_data)
    #   #=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">
    # @return [ContentBlockReference] a new ContentBlockReference instance
    # @api private
    # @see find_all_in_document
    # @see from_string
    # @see prepare_match
    def from_match_data(match_data)
      match = prepare_match(match_data)
      ContentBlockTools.logger.info("Found Content Block Reference: #{match}")
      ContentBlockReference.new(document_type: match[1], identifier: match[2], embed_code: match[0])
    end

  private

    # This replaces an en / em dashes in content block references with double or triple dashes. This can occur
    # because Kramdown (the markdown parser that Govspeak is based on) replaces double dashes with en dashes and
    # triple dashes with em dashes
    def prepare_match(match)
      [
        match[0],
        match[1],
        replace_dashes(match[2]),
        match[3],
      ]
    end

    def replace_dashes(value)
      value&.gsub("", "--")
        &.gsub("", "---")
    end
  end
end

Class Method Details

.find_all_in_document(document) ⇒ Array<ContentBlockReference>

Finds all content block references within a document, using ‘ContentBlockReference::EMBED_REGEX` to scan through the document

Returns:



68
69
70
71
72
# File 'lib/content_block_tools/content_block_reference.rb', line 68

def find_all_in_document(document)
  document.scan(EMBED_REGEX).map do |match_data|
    ContentBlockReference.from_match_data(match_data)
  end
end

.from_match_data(match_data) ⇒ ContentBlockReference

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Converts match data from a regex scan into a ContentBlockReference object

This method is used internally by find_all_in_document and from_string to create ContentBlockReference instances from regex match data. It normalizes the match data by replacing en/em dashes with double/triple dashes (which can occur due to Kramdown’s markdown parsing) before creating the object.

Examples:

Creating from match data

match_data = "{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}".match(EMBED_REGEX)
ContentBlockReference.from_match_data(match_data)
#=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">

Parameters:

  • match_data (MatchData, Array)

    the match data from scanning with EMBED_REGEX Expected to contain: [full_match, document_type, identifier, field]

Returns:

See Also:



114
115
116
117
118
# File 'lib/content_block_tools/content_block_reference.rb', line 114

def from_match_data(match_data)
  match = prepare_match(match_data)
  ContentBlockTools.logger.info("Found Content Block Reference: #{match}")
  ContentBlockReference.new(document_type: match[1], identifier: match[2], embed_code: match[0])
end

.from_string(embed_code) ⇒ ContentBlockReference

Converts a single embed code string into a ContentBlockReference object

Parses an embed code string using EMBED_REGEX to extract the document type, identifier, and embed code, then creates a ContentBlockReference instance.

Examples:

Parse an embed code with a UUID

ContentBlockReference.from_string("{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}")
#=> #<ContentBlockReference document_type="content_block_pension" identifier="2b92cade-549c-4449-9796-e7a3957f3a86" embed_code="{{embed:content_block_pension:2b92cade-549c-4449-9796-e7a3957f3a86}}">

Parse an embed code with a slug

ContentBlockReference.from_string("{{embed:content_block_contact:some-slug}}")
#=> #<ContentBlockReference document_type="content_block_contact" identifier="some-slug" embed_code="{{embed:content_block_contact:some-slug}}">

Parameters:

  • embed_code (String)

    the embed code to parse

Returns:

Raises:

See Also:



89
90
91
92
93
94
# File 'lib/content_block_tools/content_block_reference.rb', line 89

def from_string(embed_code)
  match_data = embed_code.match(/^#{EMBED_REGEX}$/)
  raise InvalidEmbedCodeError unless match_data

  ContentBlockReference.from_match_data(match_data.captures)
end

Instance Method Details

#content_store_identifierString

Returns the content store path for this content block reference

Constructs a path used to identify and retrieve the content block from the content store. The path follows the format ‘/content-blocks/#document_type/#identifier`.

Examples:

reference = ContentBlockReference.new(document_type: "content_block_contact", identifier: "some-slug", embed_code: "...")
reference.content_store_identifier
#=> "/content-blocks/content_block_contact/some-slug"

Returns:

  • (String)

    the content store path for this content block



59
60
61
# File 'lib/content_block_tools/content_block_reference.rb', line 59

def content_store_identifier
  "/content-blocks/#{document_type}/#{identifier}"
end

#identifier_is_alias?Boolean

Returns if the identifier is an alias

Returns:

  • (Boolean)

    Boolean



44
45
46
# File 'lib/content_block_tools/content_block_reference.rb', line 44

def identifier_is_alias?
  !identifier.match?(UUID_REGEX)
end