line-message-builder 0.7.0 → 0.8.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: 2e34932b3afe2a1bbe05967e19991c2d1af583094e2b60be909efa9c773296fe
4
- data.tar.gz: 2c7c94550e9b29a943b517aa5b19a9d03ec213a8779df05b1d0c55a886b8872b
3
+ metadata.gz: c49876bd79a2b1e98639a5e709a41736ebc11e8c7b5bf811ca478c52df762183
4
+ data.tar.gz: 65ceed1d2fbef231161bc7eb131269354875c39b202da96bb6fcd42edce4bf86
5
5
  SHA512:
6
- metadata.gz: c60b5ee94ee4e2f8dc658acace8538af12c9ee374f48b5fb5577c4baf43d93d661b2b47e18d8d09ee7f0e99f9fdbd0cdc3ce05e9bfd54c96ad36acd82b7b0cb6
7
- data.tar.gz: afb292eec260c30163e79d8108471a7a5db4a722d6f44d54de7e25ae884375968addb2b6be9271240a6aae58023539acaca99109eeba95fc016e0e4aae94f0c8
6
+ metadata.gz: bf2111f3470856dc021330c2fa73e3275842596d858e636d198f3dd8dcea7bddf9a60a252e4a80eb5e6297126b0ec3afeeaf143181ef565db06af9cf558c93c6
7
+ data.tar.gz: e98ec6e6696268c00f599de55e6b6acb09067c2370c6fc5357f8d1ee7f74b9b6e7d2c9b59a527ae5bc806ca6c685163311648b3a78641bd028d8ba6b71dc3bf1
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ lib
2
+ README.md
3
+ LICENSE.txt
@@ -1 +1 @@
1
- {".":"0.7.0"}
1
+ {".":"0.8.0"}
data/.rubocop.yml CHANGED
@@ -6,6 +6,8 @@ AllCops:
6
6
  TargetRubyVersion: 3.1
7
7
  NewCops: enable
8
8
  SuggestExtensions: false
9
+ Exclude:
10
+ - 'vendor/**/*'
9
11
 
10
12
  Style/StringLiterals:
11
13
  EnforcedStyle: double_quotes
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0](https://github.com/elct9620/line-message-builder/compare/v0.7.0...v0.8.0) (2025-05-24)
4
+
5
+
6
+ ### Features
7
+
8
+ * Add GitHub Action for RDoc generation and deployment ([dbb0f79](https://github.com/elct9620/line-message-builder/commit/dbb0f797ae6143bb9f463c8e6c111a5fe1f0a53f))
9
+ * Add GitHub Action for RDoc generation and deployment ([#9](https://github.com/elct9620/line-message-builder/issues/9)) ([c1a9920](https://github.com/elct9620/line-message-builder/commit/c1a99203b3abef99d149ecc7a055367268f5c2f8))
10
+ * add mode option to Line::Message::Builder for SDKv2 support ([f44f787](https://github.com/elct9620/line-message-builder/commit/f44f78758f5cac1dda6cf86f4c1945365c6c1018))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * pass context to quick reply actions to resolve sdkv2 method error ([1bec753](https://github.com/elct9620/line-message-builder/commit/1bec7539421c0c11b10d2e906f9ee7f44a1c726f))
16
+
3
17
  ## [0.7.0](https://github.com/elct9620/line-message-builder/compare/v0.6.1...v0.7.0) (2025-05-21)
4
18
 
5
19
 
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![.github/workflows/main.yml](https://github.com/elct9620/line-message-builder/actions/workflows/main.yml/badge.svg)](https://github.com/elct9620/line-message-builder/actions/workflows/main.yml)
4
4
  [![codecov](https://codecov.io/gh/elct9620/line-message-builder/graph/badge.svg?token=9TJTSRIL0X)](https://codecov.io/gh/elct9620/line-message-builder)
5
5
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/elct9620/line-message-builder)
6
+ [![Documentation](https://img.shields.io/badge/docs-gh--pages-brightgreen)](https://elct9620.github.io/line-message-builder/)
6
7
 
7
8
  Build LINE messages using DSL (Domain Specific Language) in Ruby.
8
9
 
@@ -11,6 +12,7 @@ Build LINE messages using DSL (Domain Specific Language) in Ruby.
11
12
  - Build LINE messages using DSL
12
13
  - Validation of properties
13
14
  - RSpec matchers for testing
15
+ - LINE Bot SDK v2 support (WIP)
14
16
 
15
17
  ## Installation
16
18
 
@@ -31,6 +33,12 @@ gem install line-message-builder
31
33
  > [!NOTE]
32
34
  > Working in progress.
33
35
 
36
+ ### LLM.txt
37
+
38
+ The `llm.txt` is available in document assets.
39
+
40
+ You can get it from [http://aotoki.me/line-message-builder/llm.txt](http://aotoki.me/line-message-builder/llm.txt).
41
+
34
42
  ### Builder
35
43
 
36
44
  ```ruby
@@ -3,22 +3,88 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
+ # The `Actions` module serves as a namespace for action objects that can be
7
+ # associated with various LINE message components, such as buttons in
8
+ # quick replies or imagemaps. Each class within this module represents a
9
+ # specific type of action a user can perform.
6
10
  module Actions
7
- # The Message class is used to build message actions for quick replies.
11
+ # Represents a "message action" for LINE messages.
12
+ #
13
+ # A message action sends a specified text message to the chat from the user
14
+ # when a button associated with this action is tapped. It's commonly used
15
+ # in quick replies or other interactive message components.
16
+ #
17
+ # @example Creating a message action for a quick reply button
18
+ # Line::Message::Builder.with do |root|
19
+ # root.text "Select your favorite food:"
20
+ # root.quick_reply do |qr|
21
+ # # When this button is tapped, the user sends "Pizza"
22
+ # qr.button action: :message, label: "Pizza", text: "Pizza"
23
+ # # When this button is tapped, the user sends "Sushi"
24
+ # qr.button action: :message, label: "Sushi", text: "Sushi"
25
+ # end
26
+ # end
27
+ #
28
+ # @see https://developers.line.biz/en/reference/messaging-api/#message-action
8
29
  class Message < Line::Message::Builder::Base
30
+ # @!attribute [r] text
31
+ # @return [String] The text that is sent as a message from the user
32
+ # when the action is performed. This is a required attribute.
9
33
  attr_reader :text
10
34
 
35
+ # Defines an optional `label` for the action.
36
+ # The label is recommended by LINE for accessibility purposes, but it's
37
+ # not displayed in all LINE versions. For some message types like buttons,
38
+ # the label of the button itself is used as the action's label.
39
+ #
40
+ # @!method label(value = nil)
41
+ # @param value [String, nil] The label text for the action.
42
+ # @return [String, nil] The current label text.
11
43
  option :label, default: nil
12
44
 
45
+ # Initializes a new Message action.
46
+ #
47
+ # @param text [String] The text to be sent when the action is performed.
48
+ # This is a required parameter.
49
+ # @param context [Object, nil] An optional context object for resolving
50
+ # method calls within a block, if one is provided.
51
+ # @param options [Hash] A hash of options to set instance variables.
52
+ # Can include `:label`.
53
+ # @param block [Proc, nil] An optional block to be instance-eval'd.
54
+ # While available, it's not commonly used for simple actions like this.
55
+ # @raise [RequiredError] if `text` is nil. (This check is done in `to_h`
56
+ # but `text` is conceptually required on initialization).
13
57
  def initialize(text, context: nil, **options, &)
14
58
  @text = text
15
59
 
16
60
  super(context: context, **options, &)
17
61
  end
18
62
 
63
+ # Converts the Message action object to a hash suitable for the LINE
64
+ # Messaging API.
65
+ #
66
+ # @return [Hash] A hash representing the message action.
67
+ # Includes `:type`, `:label` (if set), and `:text`.
68
+ # @raise [RequiredError] if `text` is nil.
19
69
  def to_h
20
70
  raise RequiredError, "text is required" if text.nil?
21
71
 
72
+ return to_sdkv2 if context.sdkv2?
73
+
74
+ to_api
75
+ end
76
+
77
+ private
78
+
79
+ def to_api
80
+ {
81
+ type: "message",
82
+ label: label,
83
+ text: text
84
+ }.compact
85
+ end
86
+
87
+ def to_sdkv2
22
88
  {
23
89
  type: "message",
24
90
  label: label,
@@ -4,22 +4,83 @@ module Line
4
4
  module Message
5
5
  module Builder
6
6
  module Actions
7
- # The Postback class is used to build postback actions for quick replies.
7
+ # Represents a "postback action" for LINE messages.
8
+ #
9
+ # A postback action sends a postback event to your bot's webhook when a
10
+ # button associated with this action is tapped. The event contains the
11
+ # specified `data` payload. Optionally, `displayText` can be provided,
12
+ # which will be shown in the chat as a message from the user.
13
+ #
14
+ # This action is useful for triggering specific backend logic or flows
15
+ # without necessarily displaying a message in the chat, or for displaying
16
+ # a different message than the data payload.
17
+ #
18
+ # @example Creating a postback action for a quick reply button
19
+ # Line::Message::Builder.with do |root|
20
+ # root.text "What do you want to do?"
21
+ # root.quick_reply do |qr|
22
+ # qr.button action: :postback,
23
+ # label: "Track Order",
24
+ # data: "action=track_order&order_id=123",
25
+ # display_text: "I want to track my order."
26
+ # end
27
+ # end
28
+ #
29
+ # @see https://developers.line.biz/en/reference/messaging-api/#postback-action
8
30
  class Postback < Line::Message::Builder::Base
31
+ # @!attribute [r] data
32
+ # @return [String] The data payload to be sent in the postback event
33
+ # to the webhook. This is a required attribute. Max 300 characters.
9
34
  attr_reader :data
10
35
 
36
+ # Defines an optional `label` for the action.
37
+ # The label is recommended by LINE for accessibility. For some message
38
+ # types (e.g., buttons), the button's label itself is used.
39
+ #
40
+ # @!method label(value = nil)
41
+ # @param value [String, nil] The label text for the action.
42
+ # @return [String, nil] The current label text.
11
43
  option :label, default: nil
44
+
45
+ # Defines an optional `displayText` for the action.
46
+ # This is the text that will be displayed in the chat as a message from
47
+ # the user when the action is performed. If not set, no message is displayed.
48
+ #
49
+ # @!method display_text(value = nil)
50
+ # @param value [String, nil] The text to display in the chat. Max 300 characters.
51
+ # @return [String, nil] The current display text.
12
52
  option :display_text, default: nil
13
53
 
54
+ # Initializes a new Postback action.
55
+ #
56
+ # @param data [String] The data to be sent in the postback event. This is required.
57
+ # @param context [Object, nil] An optional context object.
58
+ # @param options [Hash] Options for the action, including `:label` and `:display_text`.
59
+ # @param block [Proc, nil] An optional block for instance_eval.
60
+ # @raise [RequiredError] if `data` is nil. (This check is done in `to_h`
61
+ # but `data` is conceptually required on initialization).
14
62
  def initialize(data, context: nil, **options, &)
15
63
  @data = data
16
64
 
17
65
  super(context: context, **options, &)
18
66
  end
19
67
 
68
+ # Converts the Postback action object to a hash suitable for the LINE Messaging API.
69
+ #
70
+ # @return [Hash] A hash representing the postback action.
71
+ # Includes `:type`, `:label` (if set), `:data`, and `:displayText` (if set).
72
+ # @raise [RequiredError] if `data` is nil.
20
73
  def to_h
21
74
  raise RequiredError, "data is required" if data.nil?
22
75
 
76
+ return to_sdkv2 if context.sdkv2?
77
+
78
+ to_api
79
+ end
80
+
81
+ private
82
+
83
+ def to_api
23
84
  {
24
85
  type: "postback",
25
86
  label: label,
@@ -27,6 +88,15 @@ module Line
27
88
  displayText: display_text
28
89
  }.compact
29
90
  end
91
+
92
+ def to_sdkv2
93
+ {
94
+ type: "postback",
95
+ label: label,
96
+ data: data,
97
+ display_text: display_text
98
+ }.compact
99
+ end
30
100
  end
31
101
  end
32
102
  end
@@ -3,7 +3,24 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
- # The actions module contains classes for building different types of actions
6
+ # The `Line::Message::Builder::Actions` module serves as a central namespace
7
+ # for all action objects used within the LINE message builder DSL. Actions
8
+ # represent operations that can be triggered by users interacting with
9
+ # message components like buttons (in quick replies, templates, etc.) or
10
+ # imagemap areas.
11
+ #
12
+ # This module itself does not define specific action classes directly but
13
+ # acts as a container that loads them from individual files (e.g., `message.rb`,
14
+ # `postback.rb`). Each loaded file defines a class corresponding to a
15
+ # specific action type supported by the LINE Messaging API.
16
+ #
17
+ # By organizing actions under this namespace, the builder provides a clear
18
+ # and consistent way to create and manage different types of interactive
19
+ # elements in messages.
20
+ #
21
+ # @see Line::Message::Builder::Actions::Message
22
+ # @see Line::Message::Builder::Actions::Postback
23
+ # @see https://developers.line.biz/en/reference/messaging-api/#action-objects
7
24
  module Actions
8
25
  require_relative "actions/message"
9
26
  require_relative "actions/postback"
@@ -3,21 +3,63 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
- # The base class to provide DSL functionality.
6
+ # The `Base` class serves as the foundation for all message builder classes
7
+ # within the `Line::Message::Builder` DSL. It provides core functionality
8
+ # for defining options, handling initialization, and delegating method calls
9
+ # to a context object.
10
+ #
11
+ # This class is not typically used directly but is inherited by specific
12
+ # message type builders (e.g., `Text`, `Flex`).
7
13
  class Base
8
14
  class << self
15
+ # @!visibility private
16
+ # @!parse extend ClassMethods
9
17
  def inherited(subclass)
10
18
  super
11
19
  subclass.extend ClassMethods
12
20
  end
13
21
  end
14
22
 
15
- # :nodoc:
23
+ # The `ClassMethods` module is automatically extended by any class that
24
+ # inherits from {Base}. It provides class-level methods for defining
25
+ # DSL options and configurations.
16
26
  module ClassMethods
27
+ # Returns an array of option names that have been defined for this class
28
+ # using the {option} method.
29
+ #
30
+ # @return [Array<Symbol>] A list of defined option names.
31
+ # @!visibility private
17
32
  def options
18
33
  @options ||= []
19
34
  end
20
35
 
36
+ # Defines a new option for the builder class.
37
+ # This method dynamically creates an instance method on the builder class
38
+ # with the given `name`. When this instance method is called:
39
+ # - Without arguments, it returns the current value of the option, or
40
+ # the `default` value if not set.
41
+ # - With an argument, it sets the value of the option. If a `validator`
42
+ # is provided, the value is validated before being set.
43
+ #
44
+ # @param name [Symbol] The name of the option to define. This will also
45
+ # be the name of the generated instance method.
46
+ # @param default [Object, nil] The default value for the option if not
47
+ # explicitly set.
48
+ # @param validator [#valid!?, nil] An optional validator object that must
49
+ # respond to `valid!(value)`. If the value is invalid, the validator
50
+ # should raise an error (typically a {ValidationError}).
51
+ # @return [void]
52
+ # @example
53
+ # class MyBuilder < Base
54
+ # option :color, default: "red"
55
+ # option :size, validator: SizeValidator.new
56
+ # end
57
+ #
58
+ # builder = MyBuilder.new
59
+ # puts builder.color # => "red"
60
+ # builder.color "blue"
61
+ # puts builder.color # => "blue"
62
+ # builder.size "large" # Assuming SizeValidator allows "large"
21
63
  def option(name, default: nil, validator: nil)
22
64
  options << name
23
65
 
@@ -34,8 +76,20 @@ module Line
34
76
 
35
77
  attr_reader :context
36
78
 
79
+ # Initializes a new instance of a builder class.
80
+ #
81
+ # @param context [Object, nil] An optional external context object.
82
+ # Methods not defined in the builder will be delegated to this context
83
+ # if it responds to them. This allows for using helper methods or
84
+ # accessing data from the surrounding environment within the builder DSL.
85
+ # @param options [Hash] A hash of options to set on the builder instance.
86
+ # These options are typically defined using the {ClassMethods.option .option}
87
+ # method in the builder class.
88
+ # @param block [Proc, nil] An optional block that is instance-eval'd
89
+ # within the new builder instance. This is the primary way the DSL
90
+ # is used to define message content.
37
91
  def initialize(context: nil, **options, &block)
38
- @context = Context.new(context)
92
+ @context = context
39
93
  @quick_reply = nil
40
94
 
41
95
  self.class.options.each do |option|
@@ -45,14 +99,54 @@ module Line
45
99
  instance_eval(&block) if ::Kernel.block_given?
46
100
  end
47
101
 
102
+ # Defines a quick reply for the message.
103
+ # A quick reply consists of a set of buttons that are displayed along
104
+ # with the message, allowing users to make quick responses.
105
+ #
106
+ # The provided block is executed in the context of a new {QuickReply}
107
+ # instance, where you can define the quick reply buttons.
108
+ #
109
+ # @yield [quick_reply] The block is yielded with a {QuickReply} instance.
110
+ # @return [QuickReply] The created {QuickReply} object.
111
+ # @example
112
+ # text_message do
113
+ # text "Please choose an option:"
114
+ # quick_reply do
115
+ # button action: :message, label: "Option A", text: "You chose A"
116
+ # button action: :camera, label: "Open Camera"
117
+ # end
118
+ # end
48
119
  def quick_reply(&)
49
120
  @quick_reply = QuickReply.new(context: context, &)
50
121
  end
51
122
 
123
+ # Determines if the builder can respond to a given method, including
124
+ # checking if the context object can respond to it.
125
+ # This is part of Ruby's mechanism for `method_missing` and is used
126
+ # here to enable delegation to the `context` object.
127
+ #
128
+ # @param method_name [Symbol] The name of the method.
129
+ # @param include_private [Boolean] Whether to include private methods
130
+ # in the search.
131
+ # @return [Boolean] `true` if the builder or its context can respond to
132
+ # the method, `false` otherwise.
133
+ # @!visibility private
52
134
  def respond_to_missing?(method_name, include_private = false)
53
135
  context.respond_to?(method_name, include_private) || super
54
136
  end
55
137
 
138
+ # Handles calls to undefined methods by attempting to delegate them to the
139
+ # `context` object. If the `context` object responds to the method,
140
+ # it is called. Otherwise, it behaves like the standard `method_missing`.
141
+ # This allows the DSL to feel more integrated with the surrounding code
142
+ # by making methods from the `context` directly available within the
143
+ # builder block.
144
+ #
145
+ # @param method_name [Symbol] The name of the missing method.
146
+ # @param ... [Object] Arguments passed to the method.
147
+ # @raise [NoMethodError] If neither the builder nor the context can
148
+ # handle the method call.
149
+ # @!visibility private
56
150
  def method_missing(method_name, ...)
57
151
  if context.respond_to?(method_name)
58
152
  context.public_send(method_name, ...)
@@ -60,6 +154,22 @@ module Line
60
154
  super
61
155
  end
62
156
  end
157
+
158
+ def to_h
159
+ return to_sdkv2 if sdkv2?
160
+
161
+ to_api
162
+ end
163
+
164
+ private
165
+
166
+ def to_api
167
+ raise NotImplementedError, "#{self.class} must implement #to_api"
168
+ end
169
+
170
+ def to_sdkv2
171
+ raise NotImplementedError, "#{self.class} must implement #to_sdkv2"
172
+ end
63
173
  end
64
174
  end
65
175
  end
@@ -3,31 +3,151 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
- # The container class is main container to manage messages.
7
- class Container < Base
6
+ # The `Container` class is the top-level entry point for constructing a batch
7
+ # of LINE messages using the builder DSL. It acts as a holder for one or
8
+ # more individual message objects (such as {Text} or {Flex} messages).
9
+ #
10
+ # When you use `Line::Message::Builder.with {}`, you are operating within
11
+ # the context of a `Container` instance. This container allows you to define
12
+ # multiple messages that can be sent together in a single API call to LINE,
13
+ # although the LINE API typically expects an array of message objects,
14
+ # which this container helps to build.
15
+ #
16
+ # Each message added to the container can also have its own quick reply.
17
+ #
18
+ # @example Building multiple messages
19
+ # message_payload = Line::Message::Builder.with do |root|
20
+ # root.text "Hello, this is the first message!"
21
+ # root.flex alt_text: "This is a Flex Message" do |flex_builder|
22
+ # flex_builder.bubble do |bubble|
23
+ # bubble.body do |body|
24
+ # body.text "This is a Flex Message body."
25
+ # end
26
+ # end
27
+ # end
28
+ # end.build # => Returns an array of message hashes
29
+ #
30
+ # @see Base
31
+ # @see Text
32
+ # @see Flex::Builder
33
+ # @see QuickReply
34
+ class Container
35
+ # @!attribute [r] context
36
+ # @return [Context] The context object, which can hold external data or
37
+ # helper methods accessible within the builder blocks.
8
38
  attr_reader :context
9
39
 
10
- def initialize(context: nil, &)
11
- @messages = []
40
+ # Initializes a new message container.
41
+ # This is typically not called directly but through `Line::Message::Builder.with`.
42
+ # The provided block is instance-eval'd, allowing DSL methods like
43
+ # {#text} and {#flex} to be called directly on the container instance.
44
+ #
45
+ # @param context [Object, nil] An optional context object that can be used
46
+ # to share data or helper methods within the builder block. It's wrapped
47
+ # in a {Context} object.
48
+ # @param mode [Symbol] The mode to use for building messages. Can be either
49
+ # `:api` (default) for direct LINE Messaging API format or `:sdkv2` for
50
+ # LINE Bot SDK v2 compatible format.
51
+ # @param block [Proc] A block containing DSL calls to define messages
52
+ # (e.g., `text "Hello"`, `flex { ... }`).
53
+ def initialize(context: nil, mode: :api, &block)
54
+ @messages = [] # Initializes an empty array to store message objects
55
+ @context = Context.new(context, mode:)
12
56
 
13
- super
57
+ instance_eval(&block) if ::Kernel.block_given?
14
58
  end
15
59
 
60
+ # Creates a new {Text} message and adds it to this container.
61
+ #
62
+ # @param text [String] The text content of the message.
63
+ # @param options [Hash] Additional options for the text message,
64
+ # such as `:quick_reply`. See {Text#initialize}.
65
+ # @param block [Proc, nil] An optional block that will be instance-eval'd
66
+ # in the context of the new {Text} message instance. This can be used
67
+ # to add a quick reply to the text message.
68
+ # @return [Text] The newly created {Text} message object.
69
+ #
70
+ # @example
71
+ # root.text "Hello, world!" do
72
+ # quick_reply do
73
+ # button action: :message, label: "Hi!", text: "Hi!"
74
+ # end
75
+ # end
16
76
  def text(text, **options, &)
17
77
  @messages << Text.new(text, context: context, **options, &)
18
78
  end
19
79
 
80
+ # Creates a new {Flex::Builder} for constructing a Flex Message and adds it
81
+ # to this container. The block is mandatory and is used to define the
82
+ # content of the Flex Message using the Flex Message DSL.
83
+ #
84
+ # @param options [Hash] Options for the Flex Message, primarily `:alt_text`.
85
+ # See {Flex::Builder#initialize}. It's important to provide `alt_text`.
86
+ # @param block [Proc] A block that will be instance-eval'd in the context
87
+ # of the new {Flex::Builder} instance. This block is used to define the
88
+ # structure and content of the Flex Message (e.g., bubbles, carousels).
89
+ # @return [Flex::Builder] The newly created {Flex::Builder} object.
90
+ # @raise [ArgumentError] if `alt_text` is not provided in options (validation
91
+ # is typically within Flex::Builder).
92
+ #
93
+ # @example
94
+ # root.flex alt_text: "Important information" do |fb|
95
+ # fb.bubble do |bubble|
96
+ # bubble.header { |h| h.text "Header" }
97
+ # bubble.body { |b| b.text "Body" }
98
+ # end
99
+ # end
20
100
  def flex(**options, &)
21
101
  @messages << Flex::Builder.new(context: context, **options, &)
22
102
  end
23
103
 
104
+ # Converts all messages held by this container into their hash representations.
105
+ # This method iterates over each message object (e.g., {Text}, {Flex::Builder})
106
+ # stored in the container and calls its `to_h` method. The result is an
107
+ # array of hashes, where each hash represents a single LINE message object
108
+ # ready for JSON serialization and sending to the LINE Messaging API.
109
+ #
110
+ # @return [Array<Hash>] An array of message objects, each represented as a hash.
111
+ # This is the format expected by the LINE API for the `messages` field
112
+ # in a request body.
24
113
  def build
25
114
  @messages.map(&:to_h)
26
115
  end
27
116
 
117
+ # Converts the array of message hashes (obtained from {#build}) into a
118
+ # JSON string. This is a convenience method for serializing the messages
119
+ # payload.
120
+ #
121
+ # @param args [Object] Optional arguments that are passed along to `to_json`
122
+ # method of the underlying array.
123
+ # @return [String] A JSON string representing the array of message objects.
28
124
  def to_json(*args)
29
125
  build.to_json(*args)
30
126
  end
127
+
128
+ # Checks if a method is defined in the context object.
129
+ # This is part of Ruby's method_missing mechanism.
130
+ #
131
+ # @param method_name [Symbol] The name of the method being checked
132
+ # @param include_private [Boolean] Whether to include private methods
133
+ # @return [Boolean] True if the method exists in the context, false otherwise
134
+ def respond_to_missing?(method_name, include_private = false)
135
+ context.respond_to?(method_name, include_private) || super
136
+ end
137
+
138
+ # Delegates method calls to the context object if they exist there.
139
+ # This allows helper methods defined in the context to be called directly
140
+ # from within the builder DSL.
141
+ #
142
+ # @param method_name [Symbol] The name of the method being called
143
+ # @param args [Array] The arguments passed to the method
144
+ # @return [Object] The result of calling the method on the context
145
+ # @raise [NoMethodError] If the method doesn't exist in the context
146
+ def method_missing(method_name, ...)
147
+ return context.send(method_name, ...) if context.respond_to?(method_name)
148
+
149
+ super
150
+ end
31
151
  end
32
152
  end
33
153
  end