Module: Whatsapp::MessageTemplates::Placeholders

Defined in:
lib/ruby/whatsapp/message_templates/placeholders.rb

Overview

Parses the {{...}} placeholders out of template text.

Three components need this (header text, body text, and the single variable a URL button may append), so the pattern and the "what style is this?" question live in one place rather than being re-derived per component.

Placeholder names are collapsed to unique values in order of first appearance: Meta counts a repeated {{1}} or {{name}} as one parameter, so the count a caller must supply examples for is the unique count — that is Placeholders.count. Where a rule is about position rather than parameters, Placeholders.occurrences counts repeats instead; see the URL button. Source: https://developers.facebook.com/documentation/business-messaging/whatsapp/templates/overview

Constant Summary collapse

PATTERN =

Matches {{name}} / {{1}}, tolerating internal whitespace. Deliberately restricted to word characters so malformed forms ({{}}, {{a-b}}) are not mistaken for parameters.

/\{\{\s*(\w+)\s*\}\}/

Class Method Summary collapse

Class Method Details

.count(text) ⇒ Integer

Returns The number of unique placeholders.

Parameters:

  • text (String, nil)

Returns:

  • (Integer)

    The number of unique placeholders.



31
32
33
# File 'lib/ruby/whatsapp/message_templates/placeholders.rb', line 31

def self.count(text)
  extract(text).size
end

.extract(text) ⇒ Array<String>

Returns Unique placeholder names, in order of appearance.

Parameters:

  • text (String, nil)

Returns:

  • (Array<String>)

    Unique placeholder names, in order of appearance.



25
26
27
# File 'lib/ruby/whatsapp/message_templates/placeholders.rb', line 25

def self.extract(text)
  text.to_s.scan(PATTERN).flatten.uniq
end

.named?(text) ⇒ Boolean

Returns true when the text has placeholders and none are numeric.

Parameters:

  • text (String, nil)

Returns:

  • (Boolean)

    true when the text has placeholders and none are numeric.



67
68
69
# File 'lib/ruby/whatsapp/message_templates/placeholders.rb', line 67

def self.named?(text)
  style(text) == :named
end

.occurrences(text) ⇒ Integer

Returns The number of placeholder occurrences, counting repeats. Contrast count, which collapses repeated names. The URL button rule is positional — a single variable, appended at the very end — so a second occurrence breaks it even when it names the same parameter.

Parameters:

  • text (String, nil)

Returns:

  • (Integer)

    The number of placeholder occurrences, counting repeats. Contrast count, which collapses repeated names. The URL button rule is positional — a single variable, appended at the very end — so a second occurrence breaks it even when it names the same parameter.



40
41
42
# File 'lib/ruby/whatsapp/message_templates/placeholders.rb', line 40

def self.occurrences(text)
  text.to_s.scan(PATTERN).size
end

.positional?(text) ⇒ Boolean

Returns true when the text has placeholders and all are numeric.

Parameters:

  • text (String, nil)

Returns:

  • (Boolean)

    true when the text has placeholders and all are numeric.



61
62
63
# File 'lib/ruby/whatsapp/message_templates/placeholders.rb', line 61

def self.positional?(text)
  style(text) == :positional
end

.sequential?(text) ⇒ Boolean

Whether positional placeholders form the run 1..n that Meta requires.

Order of appearance in the text does not matter — "{{2}} after {{1}}" is valid — only that the set of indexes has no gaps and starts at 1. Text with no placeholders, or named placeholders, is trivially sequential.

Parameters:

  • text (String, nil)

Returns:

  • (Boolean)


78
79
80
81
82
83
# File 'lib/ruby/whatsapp/message_templates/placeholders.rb', line 78

def self.sequential?(text)
  indexes = extract(text).grep(/\A\d+\z/).map(&:to_i)
  return true if indexes.empty?

  indexes.sort == (1..indexes.size).to_a
end

.style(text) ⇒ Symbol?

Returns :positional, :named, :mixed, or nil when the text has no placeholders.

Parameters:

  • text (String, nil)

Returns:

  • (Symbol, nil)

    :positional, :named, :mixed, or nil when the text has no placeholders.



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ruby/whatsapp/message_templates/placeholders.rb', line 47

def self.style(text)
  names = extract(text)
  return if names.empty?

  numeric, rest = names.partition { |name| name.match?(/\A\d+\z/) }

  return :positional if rest.empty?
  return :named if numeric.empty?

  :mixed
end