line-message-builder 0.9.0 → 0.10.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/.rubocop.yml +5 -0
  4. data/CHANGELOG.md +20 -0
  5. data/CLAUDE.md +124 -0
  6. data/README.md +24 -16
  7. data/claudekit.json +11 -0
  8. data/docs/rubrics/rdoc.md +169 -0
  9. data/lib/line/message/builder/actions/message.rb +47 -26
  10. data/lib/line/message/builder/actions/postback.rb +56 -26
  11. data/lib/line/message/builder/actions/uri.rb +139 -0
  12. data/lib/line/message/builder/actions.rb +6 -3
  13. data/lib/line/message/builder/base.rb +92 -67
  14. data/lib/line/message/builder/container.rb +55 -60
  15. data/lib/line/message/builder/context.rb +51 -72
  16. data/lib/line/message/builder/flex/actionable.rb +53 -32
  17. data/lib/line/message/builder/flex/box.rb +342 -79
  18. data/lib/line/message/builder/flex/bubble.rb +73 -46
  19. data/lib/line/message/builder/flex/builder.rb +39 -28
  20. data/lib/line/message/builder/flex/button.rb +90 -42
  21. data/lib/line/message/builder/flex/carousel.rb +48 -20
  22. data/lib/line/message/builder/flex/icon.rb +152 -0
  23. data/lib/line/message/builder/flex/image.rb +95 -33
  24. data/lib/line/message/builder/flex/partial.rb +47 -49
  25. data/lib/line/message/builder/flex/position.rb +187 -100
  26. data/lib/line/message/builder/flex/separator.rb +49 -6
  27. data/lib/line/message/builder/flex/size.rb +67 -47
  28. data/lib/line/message/builder/flex/span.rb +106 -32
  29. data/lib/line/message/builder/flex/text.rb +166 -54
  30. data/lib/line/message/builder/flex.rb +26 -23
  31. data/lib/line/message/builder/quick_reply.rb +174 -4
  32. data/lib/line/message/builder/text.rb +70 -3
  33. data/lib/line/message/builder/version.rb +1 -1
  34. data/lib/line/message/builder.rb +14 -11
  35. data/lib/line/message/rspec/matchers/have_flex_bubble.rb +1 -1
  36. data/lib/line/message/rspec/matchers/have_flex_component.rb +16 -6
  37. data/lib/line/message/rspec/matchers/have_flex_message.rb +1 -1
  38. data/lib/line/message/rspec/matchers/have_flex_separator.rb +1 -1
  39. data/lib/line/message/rspec/matchers/have_quick_reply.rb +1 -1
  40. data/lib/line/message/rspec/matchers/have_text_message.rb +1 -1
  41. data/llm.txt +276 -35
  42. data/release-please-config.json +3 -1
  43. metadata +9 -3
@@ -3,14 +3,109 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
- # The QuickReply allows to attach quick reply buttons to a message.
6
+ # The QuickReply class provides a builder for creating quick reply buttons
7
+ # that can be attached to text messages or flex messages in the LINE Messaging API.
8
+ #
9
+ # Quick reply buttons appear at the bottom of the chat screen and allow users
10
+ # to quickly respond to messages with predefined actions. They provide a
11
+ # convenient way to guide user interactions without requiring typing.
12
+ #
13
+ # Quick replies support:
14
+ # - Message actions that send text messages when tapped
15
+ # - Postback actions that send data to your webhook
16
+ # - Optional image icons for each button (up to 13 buttons total)
17
+ #
18
+ # == Example: Basic quick reply with message actions
19
+ #
20
+ # Line::Message::Builder.with do
21
+ # text "Choose your favorite:" do
22
+ # quick_reply do
23
+ # message "Pizza", label: "Pizza"
24
+ # message "Sushi", label: "Sushi"
25
+ # end
26
+ # end
27
+ # end
28
+ #
29
+ # == Example: Quick reply with postback actions
30
+ #
31
+ # Line::Message::Builder.with do
32
+ # text "What would you like to do?" do
33
+ # quick_reply do
34
+ # postback "action=order", label: "Place Order", display_text: "I want to order"
35
+ # postback "action=track", label: "Track Order"
36
+ # end
37
+ # end
38
+ # end
39
+ #
40
+ # == Example: Quick reply with image icons
41
+ #
42
+ # Line::Message::Builder.with do
43
+ # text "Select a category:" do
44
+ # quick_reply do
45
+ # message "Food", label: "Food", image_url: "https://example.com/food.png"
46
+ # message "Drinks", label: "Drinks", image_url: "https://example.com/drinks.png"
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # See also:
52
+ # - Text
53
+ # - Actions::Message
54
+ # - Actions::Postback
55
+ # - https://developers.line.biz/en/docs/messaging-api/using-quick-reply/
7
56
  class QuickReply < Line::Message::Builder::Base
57
+ # Creates a new quick reply builder.
58
+ #
59
+ # Quick reply builders manage a collection of action buttons that appear
60
+ # at the bottom of messages. The builder provides methods to add message
61
+ # and postback actions with optional image icons.
62
+ #
63
+ # [context]
64
+ # An optional context object for method delegation (default: +nil+)
65
+ # [block]
66
+ # Block for configuring quick reply buttons using +message+, +postback+
67
+ # and +uri+ methods
68
+ #
69
+ # == Example
70
+ #
71
+ # quick_reply = QuickReply.new(context: view_context) do
72
+ # message "Yes", label: "Yes"
73
+ # message "No", label: "No"
74
+ # end
8
75
  def initialize(context: nil, &)
9
76
  @items = []
10
77
 
11
78
  super
12
79
  end
13
80
 
81
+ # Adds a message action button to the quick reply.
82
+ #
83
+ # Message actions send the specified text as a message from the user when
84
+ # the button is tapped. This is useful for providing predefined response
85
+ # options that simplify user interaction.
86
+ #
87
+ # [text]
88
+ # The text message to send when the button is tapped
89
+ # [label]
90
+ # The label text displayed on the button (required)
91
+ # [image_url]
92
+ # Optional icon image URL for the button (default: +nil+)
93
+ # [block]
94
+ # Optional block for additional configuration
95
+ #
96
+ # == Example: Basic message button
97
+ #
98
+ # quick_reply do
99
+ # message "I agree", label: "Yes"
100
+ # message "I disagree", label: "No"
101
+ # end
102
+ #
103
+ # == Example: Message button with icon
104
+ #
105
+ # quick_reply do
106
+ # message "Order pizza", label: "Pizza",
107
+ # image_url: "https://example.com/pizza.png"
108
+ # end
14
109
  def message(text, label:, image_url: nil, &)
15
110
  action(
16
111
  Actions::Message.new(text, context: context, label: label, &),
@@ -18,6 +113,46 @@ module Line
18
113
  )
19
114
  end
20
115
 
116
+ # Adds a postback action button to the quick reply.
117
+ #
118
+ # Postback actions send data to your bot's webhook when the button is tapped,
119
+ # allowing you to trigger backend logic without displaying a message. Optionally,
120
+ # you can specify display text that will appear in the chat as if the user
121
+ # sent it.
122
+ #
123
+ # [data]
124
+ # The data payload to send to the webhook (max 300 characters)
125
+ # [label]
126
+ # The label text displayed on the button (default: +nil+)
127
+ # [display_text]
128
+ # Text to display in chat when tapped (default: +nil+)
129
+ # [image_url]
130
+ # Optional icon image URL for the button (default: +nil+)
131
+ # [block]
132
+ # Optional block for additional configuration
133
+ #
134
+ # == Example: Basic postback button
135
+ #
136
+ # quick_reply do
137
+ # postback "action=yes", label: "Yes"
138
+ # postback "action=no", label: "No"
139
+ # end
140
+ #
141
+ # == Example: Postback with display text
142
+ #
143
+ # quick_reply do
144
+ # postback "action=order&item=pizza",
145
+ # label: "Order Pizza",
146
+ # display_text: "I'd like to order a pizza"
147
+ # end
148
+ #
149
+ # == Example: Postback button with icon
150
+ #
151
+ # quick_reply do
152
+ # postback "action=confirm",
153
+ # label: "Confirm",
154
+ # image_url: "https://example.com/check.png"
155
+ # end
21
156
  def postback(data, label: nil, display_text: nil, image_url: nil, &)
22
157
  action(
23
158
  Actions::Postback.new(data, context: context, label: label, display_text: display_text, &),
@@ -25,9 +160,44 @@ module Line
25
160
  )
26
161
  end
27
162
 
163
+ # Adds a URI action button to the quick reply.
164
+ #
165
+ # URI actions open the given URI when the button is tapped, without any
166
+ # webhook handling. The <code>altUri.desktop</code> property is not
167
+ # supported in a quick reply, so Actions::Uri#alt_uri_desktop has no
168
+ # effect here.
169
+ #
170
+ # [uri]
171
+ # The URI to open. Schemes +http+, +https+, +line+ and +tel+
172
+ # [label]
173
+ # The label text displayed on the button (required)
174
+ # [image_url]
175
+ # Optional icon image URL for the button (default: +nil+)
176
+ # [block]
177
+ # Optional block for additional configuration
178
+ #
179
+ # == Example: Basic URI button
180
+ #
181
+ # quick_reply do
182
+ # uri "https://example.com/menu", label: "See menu"
183
+ # end
184
+ #
185
+ # == Example: URI button with icon
186
+ #
187
+ # quick_reply do
188
+ # uri "tel:+81312345678", label: "Call us",
189
+ # image_url: "https://example.com/phone.png"
190
+ # end
191
+ def uri(uri, label:, image_url: nil, &)
192
+ action(
193
+ Actions::Uri.new(uri, context: context, label: label, &),
194
+ image_url
195
+ )
196
+ end
197
+
28
198
  private
29
199
 
30
- def to_api
200
+ def to_api # :nodoc:
31
201
  {
32
202
  items: @items.map do |item, image_url|
33
203
  {
@@ -39,7 +209,7 @@ module Line
39
209
  }
40
210
  end
41
211
 
42
- def to_sdkv2
212
+ def to_sdkv2 # :nodoc:
43
213
  {
44
214
  items: @items.map do |item, image_url|
45
215
  {
@@ -51,7 +221,7 @@ module Line
51
221
  }
52
222
  end
53
223
 
54
- def action(action, image_url)
224
+ def action(action, image_url) # :nodoc:
55
225
  @items << [action, image_url]
56
226
  end
57
227
  end
@@ -3,10 +3,77 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
- # Text message builder.
6
+ # The Text class provides a builder for creating simple text messages
7
+ # in the LINE Messaging API. Text messages are the most basic message type,
8
+ # consisting of plain text content with optional quick reply buttons and
9
+ # quote tokens for replying to specific messages.
10
+ #
11
+ # Text messages support:
12
+ # - Plain text content (up to 5,000 characters)
13
+ # - Quick reply buttons for user interaction
14
+ # - Quote tokens to reply to specific messages in a conversation
15
+ #
16
+ # == Example: Basic text message
17
+ #
18
+ # Line::Message::Builder.with do
19
+ # text "Hello, world!"
20
+ # end
21
+ #
22
+ # == Example: Text with quick reply
23
+ #
24
+ # Line::Message::Builder.with do
25
+ # text "Choose an option:" do
26
+ # quick_reply do
27
+ # button action: :message, label: "Yes", text: "I agree"
28
+ # button action: :message, label: "No", text: "I disagree"
29
+ # end
30
+ # end
31
+ # end
32
+ #
33
+ # == Example: Text with quote token
34
+ #
35
+ # Line::Message::Builder.with do
36
+ # text "This is a reply" do
37
+ # quote_token "sample_quote_token_value"
38
+ # end
39
+ # end
40
+ #
41
+ # The Text builder inherits from Base and can be used within the
42
+ # Line::Message::Builder.with block using the +text+ method.
7
43
  class Text < Base
44
+ # :method: quote_token
45
+ # :call-seq:
46
+ # quote_token() -> String or nil
47
+ # quote_token(value) -> String
48
+ #
49
+ # Sets or gets the quote token for replying to a specific message.
50
+ #
51
+ # Quote tokens allow your bot to quote and reply to a specific message
52
+ # in the conversation. When set, the original message being replied to
53
+ # will be displayed above the new message.
54
+ #
55
+ # [value]
56
+ # The quote token string obtained from a webhook event
8
57
  option :quote_token, default: nil
9
58
 
59
+ # Creates a new text message builder.
60
+ #
61
+ # [text]
62
+ # The text content of the message (up to 5,000 characters)
63
+ # [context]
64
+ # An optional context object for method delegation (default: +nil+)
65
+ # [option]
66
+ # Additional options to configure the text message
67
+ # [block]
68
+ # Optional block for configuring quick replies or quote token
69
+ #
70
+ # == Example
71
+ #
72
+ # text = Text.new("Hello!", context: view_context) do
73
+ # quick_reply do
74
+ # button action: :message, label: "Hi", text: "Hi back!"
75
+ # end
76
+ # end
10
77
  def initialize(text, context: nil, **options, &block)
11
78
  @text = text
12
79
 
@@ -15,7 +82,7 @@ module Line
15
82
 
16
83
  private
17
84
 
18
- def to_api
85
+ def to_api # :nodoc:
19
86
  {
20
87
  type: "text",
21
88
  text: @text,
@@ -24,7 +91,7 @@ module Line
24
91
  }.compact
25
92
  end
26
93
 
27
- def to_sdkv2
94
+ def to_sdkv2 # :nodoc:
28
95
  {
29
96
  type: "text",
30
97
  text: @text,
@@ -3,7 +3,7 @@
3
3
  module Line
4
4
  module Message
5
5
  module Builder
6
- VERSION = "0.9.0"
6
+ VERSION = "0.10.0"
7
7
  end
8
8
  end
9
9
  end
@@ -35,19 +35,22 @@ module Line
35
35
  module_function
36
36
 
37
37
  # Entry point for building a message container.
38
- # This method initializes a new message {Container} and evaluates the
38
+ # This method initializes a new message Container and evaluates the
39
39
  # provided block within the context of that container.
40
40
  #
41
- # @param context [Object, nil] An optional context object that can be made
42
- # available within the builder block. This can be useful for accessing
43
- # helper methods or data within the block.
44
- # @param mode [Symbol] The mode to use for building messages. Can be either
45
- # `:api` (default) for direct LINE Messaging API format or `:sdkv2` for
46
- # LINE Bot SDK v2 compatible format.
47
- # @yield [container] The block is yielded with the newly created {Container}
48
- # instance, allowing you to define the message structure using the DSL.
49
- # @return [Container] The initialized message container with the defined structure.
50
- # @example
41
+ # [context]
42
+ # An optional context object that can be made available within the builder
43
+ # block. This can be useful for accessing helper methods or data within the
44
+ # block.
45
+ # [mode]
46
+ # The mode to use for building messages. Can be either <code>:api</code> (default) for
47
+ # direct LINE Messaging API format or <code>:sdkv2</code> for LINE Bot SDK v2 compatible
48
+ # format.
49
+ #
50
+ # :yields: container
51
+ #
52
+ # == Example
53
+ #
51
54
  # message = Line::Message::Builder.with do
52
55
  # text "Hello, world!"
53
56
  # end
@@ -51,7 +51,7 @@ module Line
51
51
  end
52
52
  end
53
53
 
54
- def have_line_flex_bubble(expected = nil) # rubocop:disable Naming/PredicateName
54
+ def have_line_flex_bubble(expected = nil) # rubocop:disable Naming/PredicatePrefix
55
55
  HaveFlexBubble.new(expected)
56
56
  end
57
57
  end
@@ -69,11 +69,11 @@ module Line
69
69
  end
70
70
  end
71
71
 
72
- def have_line_flex_component(&) # rubocop:disable Naming/PredicateName
72
+ def have_line_flex_component(&) # rubocop:disable Naming/PredicatePrefix
73
73
  HaveFlexComponent.new(&)
74
74
  end
75
75
 
76
- def have_line_flex_box(**options) # rubocop:disable Naming/PredicateName
76
+ def have_line_flex_box(**options) # rubocop:disable Naming/PredicatePrefix
77
77
  options = Utils.stringify_keys!(options, deep: true)
78
78
 
79
79
  HaveFlexComponent.new(expected_desc: "box(#{options.inspect})") do |content|
@@ -83,7 +83,7 @@ module Line
83
83
  end
84
84
  end
85
85
 
86
- def have_line_flex_text(text, **options) # rubocop:disable Naming/PredicateName
86
+ def have_line_flex_text(text, **options) # rubocop:disable Naming/PredicatePrefix
87
87
  options = Utils.stringify_keys!(options, deep: true)
88
88
 
89
89
  HaveFlexComponent.new(expected_desc: "text(#{text.inspect})") do |content|
@@ -93,7 +93,7 @@ module Line
93
93
  end
94
94
  end
95
95
 
96
- def have_line_flex_button(type, **options) # rubocop:disable Naming/PredicateName
96
+ def have_line_flex_button(type, **options) # rubocop:disable Naming/PredicatePrefix
97
97
  options = Utils.stringify_keys!(options, deep: true)
98
98
 
99
99
  HaveFlexComponent.new(expected_desc: "#{type} button(#{options.inspect})") do |content|
@@ -106,7 +106,7 @@ module Line
106
106
  end
107
107
  end
108
108
 
109
- def have_line_flex_image(url, **options) # rubocop:disable Naming/PredicateName
109
+ def have_line_flex_image(url, **options) # rubocop:disable Naming/PredicatePrefix
110
110
  options = Utils.stringify_keys!(options, deep: true)
111
111
 
112
112
  HaveFlexComponent.new(expected_desc: "image(#{url.inspect})") do |content|
@@ -116,7 +116,17 @@ module Line
116
116
  end
117
117
  end
118
118
 
119
- def have_line_flex_span(text, **options) # rubocop:disable Naming/PredicateName
119
+ def have_line_flex_icon(url, **options) # rubocop:disable Naming/PredicatePrefix
120
+ options = Utils.stringify_keys!(options, deep: true)
121
+
122
+ HaveFlexComponent.new(expected_desc: "icon(#{url.inspect})") do |content|
123
+ next false unless content["type"] == "icon"
124
+
125
+ ::RSpec::Matchers::BuiltIn::Include.new({ "url" => url, **options }).matches?(content)
126
+ end
127
+ end
128
+
129
+ def have_line_flex_span(text, **options) # rubocop:disable Naming/PredicatePrefix
120
130
  options = Utils.stringify_keys!(options, deep: true)
121
131
 
122
132
  HaveFlexComponent.new(expected_desc: "span(#{text.inspect})") do |content|
@@ -37,7 +37,7 @@ module Line
37
37
  end
38
38
  end
39
39
 
40
- def have_line_flex_message(expected = nil) # rubocop:disable Naming/PredicateName
40
+ def have_line_flex_message(expected = nil) # rubocop:disable Naming/PredicatePrefix
41
41
  HaveFlexMessage.new(expected)
42
42
  end
43
43
  end
@@ -5,7 +5,7 @@ module Line
5
5
  module RSpec
6
6
  # :nodoc:
7
7
  module Matchers
8
- def have_line_flex_separator(**options) # rubocop:disable Naming/PredicateName
8
+ def have_line_flex_separator(**options) # rubocop:disable Naming/PredicatePrefix
9
9
  options = Utils.stringify_keys!(options, deep: true)
10
10
 
11
11
  HaveFlexComponent.new(expected_desc: "separator(#{options.inspect})") do |content|
@@ -44,7 +44,7 @@ module Line
44
44
  end
45
45
  end
46
46
 
47
- def have_line_quick_reply(expected = nil) # rubocop:disable Naming/PredicateName
47
+ def have_line_quick_reply(expected = nil) # rubocop:disable Naming/PredicatePrefix
48
48
  HaveQuickReply.new(expected)
49
49
  end
50
50
  end
@@ -38,7 +38,7 @@ module Line
38
38
  end
39
39
  end
40
40
 
41
- def have_line_text_message(*expected) # rubocop:disable Naming/PredicateName
41
+ def have_line_text_message(*expected) # rubocop:disable Naming/PredicatePrefix
42
42
  HaveTextMessage.new(expected)
43
43
  end
44
44
  end