line-message-builder 0.7.0 → 0.9.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.document +3 -0
  3. data/.release-please-manifest.json +1 -1
  4. data/.rubocop.yml +2 -0
  5. data/CHANGELOG.md +35 -0
  6. data/CONVENTIONS.md +36 -0
  7. data/README.md +49 -31
  8. data/lib/line/message/builder/actions/message.rb +67 -1
  9. data/lib/line/message/builder/actions/postback.rb +71 -1
  10. data/lib/line/message/builder/actions.rb +18 -1
  11. data/lib/line/message/builder/base.rb +113 -3
  12. data/lib/line/message/builder/container.rb +125 -5
  13. data/lib/line/message/builder/context.rb +111 -3
  14. data/lib/line/message/builder/flex/actionable.rb +46 -1
  15. data/lib/line/message/builder/flex/box.rb +191 -15
  16. data/lib/line/message/builder/flex/bubble.rb +96 -8
  17. data/lib/line/message/builder/flex/builder.rb +74 -8
  18. data/lib/line/message/builder/flex/button.rb +104 -17
  19. data/lib/line/message/builder/flex/carousel.rb +71 -8
  20. data/lib/line/message/builder/flex/image.rb +105 -20
  21. data/lib/line/message/builder/flex/partial.rb +94 -9
  22. data/lib/line/message/builder/flex/position.rb +122 -12
  23. data/lib/line/message/builder/flex/separator.rb +41 -0
  24. data/lib/line/message/builder/flex/size.rb +65 -7
  25. data/lib/line/message/builder/flex/span.rb +110 -0
  26. data/lib/line/message/builder/flex/text.rb +176 -28
  27. data/lib/line/message/builder/flex.rb +56 -12
  28. data/lib/line/message/builder/quick_reply.rb +16 -4
  29. data/lib/line/message/builder/text.rb +12 -1
  30. data/lib/line/message/builder/version.rb +1 -1
  31. data/lib/line/message/builder.rb +33 -3
  32. data/lib/line/message/rspec/matchers/have_flex_component.rb +11 -0
  33. data/lib/line/message/rspec/matchers/have_flex_separator.rb +20 -0
  34. data/lib/line/message/rspec/matchers.rb +1 -0
  35. data/lib/line/message/rspec.rb +14 -0
  36. data/llm.txt +437 -0
  37. metadata +6 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e34932b3afe2a1bbe05967e19991c2d1af583094e2b60be909efa9c773296fe
4
- data.tar.gz: 2c7c94550e9b29a943b517aa5b19a9d03ec213a8779df05b1d0c55a886b8872b
3
+ metadata.gz: e0b31fce5c00d5f47ade90df479d30b79361f73e3e08852e10f4f90996de5bb9
4
+ data.tar.gz: 6a72a291942bd3f7448f5a294da49dd3321424d5dad057745590e4b96b1a1abb
5
5
  SHA512:
6
- metadata.gz: c60b5ee94ee4e2f8dc658acace8538af12c9ee374f48b5fb5577c4baf43d93d661b2b47e18d8d09ee7f0e99f9fdbd0cdc3ce05e9bfd54c96ad36acd82b7b0cb6
7
- data.tar.gz: afb292eec260c30163e79d8108471a7a5db4a722d6f44d54de7e25ae884375968addb2b6be9271240a6aae58023539acaca99109eeba95fc016e0e4aae94f0c8
6
+ metadata.gz: bb358c2a18903395e07bfc878d3befe2048ad23c5d68b4e5cd23e17e1914bb300f7a692858a68f9c71daa3630b1f30761ab5fc603887296c1d1cffd4964b7414
7
+ data.tar.gz: 342237372588844319052e9be35b1014024f5f93552f99699137f19a5abf7f341bd03eda306f4f2c5d181c50f257f47fdf5f0a7899cc2093312a91d4cd42667f
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ lib
2
+ README.md
3
+ LICENSE.txt
@@ -1 +1 @@
1
- {".":"0.7.0"}
1
+ {".":"0.9.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,40 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.0](https://github.com/elct9620/line-message-builder/compare/v0.8.0...v0.9.0) (2025-06-03)
4
+
5
+
6
+ ### Features
7
+
8
+ * add flex separator matcher and tests for flex message components ([0d34bc9](https://github.com/elct9620/line-message-builder/commit/0d34bc9fcfeef9d2ca519548f4a83a3de046eb33))
9
+ * add Flex::Span message builder for LINE messaging API ([a8377e9](https://github.com/elct9620/line-message-builder/commit/a8377e93861fa286e95a69d3cb3c9897c1992044))
10
+ * add have_line_flex_span matcher and comprehensive tests for span component ([b9072cd](https://github.com/elct9620/line-message-builder/commit/b9072cd3e4c56c9346bb3d6cf98ee11374029807))
11
+ * add Separator builder for Flex messages ([accb2aa](https://github.com/elct9620/line-message-builder/commit/accb2aa41f9a016445f377f97e93f085734764c4))
12
+ * add separator method to Flex::Box for adding separator elements ([4125ae1](https://github.com/elct9620/line-message-builder/commit/4125ae15fd5666431ec864121cf40d3c4fb0f56f))
13
+ * add Span component for styled text in Flex Messages ([a458d1d](https://github.com/elct9620/line-message-builder/commit/a458d1d2473e0859bc7e1c81254888a8c8fde9a7))
14
+ * add span method to flex box builder and remove related specs ([af8dc8f](https://github.com/elct9620/line-message-builder/commit/af8dc8f9f4b8c8a7a12c15db23a50331dfc102cb))
15
+ * add support for spans within flex text components ([90ab15d](https://github.com/elct9620/line-message-builder/commit/90ab15df7044d15308d23ef864a69f84d71e28e7))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * add Flex::Separator component and require it in flex.rb ([4152c10](https://github.com/elct9620/line-message-builder/commit/4152c1024a7634b526f94fa4e76a518a6ff0787c))
21
+ * ensure matcher finds flex separator in all nested containers ([d289ea1](https://github.com/elct9620/line-message-builder/commit/d289ea178e17d4ca00b796a9f666c527982d82f6))
22
+ * remove duplicate Separator class definition in flex separator.rb ([2c6b6d0](https://github.com/elct9620/line-message-builder/commit/2c6b6d063a2da97a0781591a697cf3034cb135f6))
23
+
24
+ ## [0.8.0](https://github.com/elct9620/line-message-builder/compare/v0.7.0...v0.8.0) (2025-05-24)
25
+
26
+
27
+ ### Features
28
+
29
+ * Add GitHub Action for RDoc generation and deployment ([dbb0f79](https://github.com/elct9620/line-message-builder/commit/dbb0f797ae6143bb9f463c8e6c111a5fe1f0a53f))
30
+ * 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))
31
+ * add mode option to Line::Message::Builder for SDKv2 support ([f44f787](https://github.com/elct9620/line-message-builder/commit/f44f78758f5cac1dda6cf86f4c1945365c6c1018))
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * pass context to quick reply actions to resolve sdkv2 method error ([1bec753](https://github.com/elct9620/line-message-builder/commit/1bec7539421c0c11b10d2e906f9ee7f44a1c726f))
37
+
3
38
  ## [0.7.0](https://github.com/elct9620/line-message-builder/compare/v0.6.1...v0.7.0) (2025-05-21)
4
39
 
5
40
 
data/CONVENTIONS.md CHANGED
@@ -16,6 +16,42 @@ This gem is designed to provide DSL (Domain Specific Language) for building LINE
16
16
 
17
17
  Only use comments for RDoc documentation. Do not use comments to explain anything in the code. The code should be self-explanatory. If you find yourself needing to write a comment to explain something, consider refactoring the code instead.
18
18
 
19
+ ## DSL
20
+
21
+ When using the `Line::Message::Builder` DSL, following is recommended:
22
+
23
+ ```ruby
24
+ Line::Message::Builder.with do
25
+ flex alt_text: "Hello, World!" do
26
+ bubble do
27
+ header do
28
+ text "Welcome to LINE Messaging API"
29
+ end
30
+ body do
31
+ text "This is a sample message."
32
+ end
33
+ end
34
+ end
35
+ end
36
+ ```
37
+
38
+ DO NOT use `do |container|` syntax as following:
39
+
40
+ ```ruby
41
+ Line::Message::Builder.with do |builder|
42
+ builder.flex alt_text: "Hello, World!" do
43
+ builder.bubble do
44
+ builder.header do
45
+ builder.text "Welcome to LINE Messaging API"
46
+ end
47
+ builder.body do
48
+ builder.text "This is a sample message."
49
+ end
50
+ end
51
+ end
52
+ end
53
+ ```
54
+
19
55
  ## RSpec
20
56
 
21
57
  - Write feature tests instead of unit tests, use `Line::Message::Builder` as test subject to verify the behavior of the DSL.
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 (Experimental)
14
16
 
15
17
  ## Installation
16
18
 
@@ -31,18 +33,32 @@ 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
37
45
  builder = Line::MessageBuilder::Builder.with do
38
- text "Hello, world!"
46
+ text "Hello, world!"
39
47
  end
40
48
 
41
49
  pp builder.build
42
50
  # => [{ type: "text", text: "Hello, world!" }]
43
51
 
44
52
  puts builder.to_json
45
- # => {"type":"text","text":"Hello, world!"}
53
+ # => "[{\"type\":\"text",\"text\":\"Hello, world!\"}"
54
+ ```
55
+
56
+ To use with [line-bot-sdk-ruby](https://github.com/line/line-bot-sdk-ruby) v2, you can set mode to `sdkv2`:
57
+
58
+ ```ruby
59
+ builder = Line::MessageBuilder::Builder.with(mode: :sdkv2) do
60
+ text "Hello, world!"
61
+ end
46
62
  ```
47
63
 
48
64
  ### Context
@@ -51,18 +67,18 @@ The context can make `Builder` to access additional methods and variables.
51
67
 
52
68
  ```ruby
53
69
  context = OpenStruct.new(
54
- name: "John Doe",
70
+ name: "John Doe",
55
71
  )
56
72
 
57
73
  builder = Line::MessageBuilder::Builder.with(context) do
58
- text "Hello, #{name}!"
74
+ text "Hello, #{name}!"
59
75
  end
60
76
 
61
77
  pp builder.build
62
78
  # => [{ type: "text", text: "Hello, John Doe!" }]
63
79
 
64
80
  puts builder.to_json
65
- # => {"type":"text","text":"Hello, John Doe!"}
81
+ # => "[{\"type\":\"text\",\"text\":\"Hello, John Doe!\"}"
66
82
  ```
67
83
 
68
84
  For Rails, you can use `view_context` to make `Builder` to access Rails helpers.
@@ -70,12 +86,12 @@ For Rails, you can use `view_context` to make `Builder` to access Rails helpers.
70
86
  ```ruby
71
87
  # app/controllers/line_controller.rb
72
88
  builder = Line::MessageBuilder::Builder.with(view_context) do
73
- text "Anything you want?" do
74
- quick_reply do
75
- message "Yes", label: "Yes", image_url: image_url("yes.png")
76
- message "No", label: "No", image_url: image_url("no.png")
77
- end
89
+ text "Anything you want?" do
90
+ quick_reply do
91
+ message "Yes", label: "Yes", image_url: image_url("yes.png")
92
+ message "No", label: "No", image_url: image_url("no.png")
78
93
  end
94
+ end
79
95
  end
80
96
  ```
81
97
 
@@ -84,18 +100,18 @@ If not in controller, you can create a `ActionView::Base` instance and pass it t
84
100
  ```ruby
85
101
  # app/presenters/line_presenter.rb
86
102
  context = ActionView::Base.new(
87
- ActionController::Base.view_paths,
88
- {},
89
- ActionController::Base.new,
103
+ ActionController::Base.view_paths,
104
+ {},
105
+ ActionController::Base.new,
90
106
  )
91
107
 
92
108
  builder = Line::MessageBuilder::Builder.with(context) do
93
- text "Anything you want?" do
94
- quick_reply do
95
- message "Yes", label: "Yes", image_url: image_url("yes.png")
96
- message "No", label: "No", image_url: image_url("no.png")
97
- end
109
+ text "Anything you want?" do
110
+ quick_reply do
111
+ message "Yes", label: "Yes", image_url: image_url("yes.png")
112
+ message "No", label: "No", image_url: image_url("no.png")
98
113
  end
114
+ end
99
115
  end
100
116
  ```
101
117
 
@@ -163,6 +179,7 @@ end
163
179
  | `have_line_flex_image` | Match a flex message with image |
164
180
  | `have_line_flex_button` | Match a flex message with button |
165
181
  | `have_line_flex_box` | Match a flex message with box |
182
+ | `have_line_flex_separator` | Match a flex message with separator |
166
183
 
167
184
 
168
185
  Add `line/message/rspec` to your `spec_helper.rb` or `rails_helper.rb`:
@@ -175,7 +192,7 @@ Include `Line::Message::RSpec::Matchers` in your RSpec configuration:
175
192
 
176
193
  ```ruby
177
194
  RSpec.configure do |config|
178
- config.include Line::Message::RSpec::Matchers
195
+ config.include Line::Message::RSpec::Matchers
179
196
  end
180
197
  ```
181
198
 
@@ -183,10 +200,10 @@ Then the matchers are available in your specs:
183
200
 
184
201
  ```ruby
185
202
  let(:builder) do
186
- Line::MessageBuilder::Builder.with do
187
- text "Hello, world!"
188
- text "Nice to meet you!"
189
- end
203
+ Line::MessageBuilder::Builder.with do
204
+ text "Hello, world!"
205
+ text "Nice to meet you!"
206
+ end
190
207
  end
191
208
 
192
209
  subject { builder.build }
@@ -199,12 +216,13 @@ The matchers can work with webmock `a_request`:
199
216
 
200
217
  ```ruby
201
218
  it "reply with message" do
202
- expect(a_request(:post, "https://api.line.me/v2/bot/message/reply")
203
- .with(
204
- body: hash_including({
205
- messages: have_line_text_message(/Hello, world!/),
206
- },
207
- ))).to have_been_made.once
219
+ expect(a_request(:post, "https://api.line.me/v2/bot/message/reply")
220
+ .with(
221
+ body: hash_including({
222
+ messages: have_line_text_message(/Hello, world!/),
223
+ })
224
+ )
225
+ ).to have_been_made.once
208
226
  end
209
227
  ```
210
228
 
@@ -263,8 +281,8 @@ end
263
281
  | Video | ❌ |
264
282
  | Icon | ❌ |
265
283
  | Text | 🚧 |
266
- | Span | |
267
- | Separator | |
284
+ | Span | |
285
+ | Separator | |
268
286
  | Filler | ❌ Deprecated |
269
287
 
270
288
  ## Development
@@ -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
19
+ # text "Select your favorite food:"
20
+ # quick_reply do
21
+ # # When this button is tapped, the user sends "Pizza"
22
+ # button action: :message, label: "Pizza", text: "Pizza"
23
+ # # When this button is tapped, the user sends "Sushi"
24
+ # 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
20
+ # text "What do you want to do?"
21
+ # quick_reply do
22
+ # 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