line-message-builder 0.1.0 → 0.2.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 +4 -4
- data/.release-please-manifest.json +1 -0
- data/CHANGELOG.md +18 -0
- data/README.md +67 -8
- data/lib/line/message/builder/actions/message.rb +1 -1
- data/lib/line/message/builder/actions/postback.rb +1 -1
- data/lib/line/message/builder/actions.rb +1 -1
- data/lib/line/message/builder/base.rb +37 -2
- data/lib/line/message/builder/container.rb +34 -0
- data/lib/line/message/builder/flex/box.rb +44 -0
- data/lib/line/message/builder/flex/bubble.rb +51 -0
- data/lib/line/message/builder/flex/builder.rb +33 -0
- data/lib/line/message/builder/flex/button.rb +40 -0
- data/lib/line/message/builder/flex/image.rb +44 -0
- data/lib/line/message/builder/flex/text.rb +42 -0
- data/lib/line/message/builder/flex.rb +21 -0
- data/lib/line/message/builder/quick_reply.rb +37 -5
- data/lib/line/message/builder/text.rb +1 -3
- data/lib/line/message/builder/version.rb +2 -2
- data/lib/line/message/builder.rb +6 -31
- data/lib/line/message/rspec/matchers/have_flex_component.rb +111 -0
- data/lib/line/message/rspec/matchers/have_flex_message.rb +45 -0
- data/lib/line/message/rspec/matchers/have_quick_reply.rb +52 -0
- data/lib/line/message/rspec/matchers/have_text_message.rb +35 -10
- data/lib/line/message/rspec/matchers.rb +4 -0
- metadata +15 -3
- data/lib/line/message/builder/quick_reply/builder.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f66fccfc1d5cd52c32662d8e912aac28f79e82d0b86ee837125ebe292fdd5f13
|
4
|
+
data.tar.gz: b819400dc6c6afbcb85b99335dbbf60c0a8c2cfe71c4f4f2aa3653e56fd5edf5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e7a7f40dede7c209dd9d4f581020a2d9cf9b9403675b055382446a440852861ab1585832b9bae31b31a12aaea68f5bd7c32e2ce9cde8950715db38864b726dd
|
7
|
+
data.tar.gz: 87d0a63d6fdab6a7338745eac2e3b391605c8631ac7cef4226ac17e098b93ac152f39ecfe5bbfbe9f98576ab52fd92e9a1377819c3bfc10238cd95e342ff09fb
|
@@ -0,0 +1 @@
|
|
1
|
+
{".":"0.2.0"}
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [0.2.0](https://github.com/elct9620/line-message-builder/compare/v0.1.0...v0.2.0) (2025-04-05)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* Add Flex button component with message and postback actions ([0fe6ecb](https://github.com/elct9620/line-message-builder/commit/0fe6ecb4cdf17e180c4ca0230a810a6d62bead9b))
|
9
|
+
* Add Flex message builder support to Line message builder ([2f8e191](https://github.com/elct9620/line-message-builder/commit/2f8e191d466e94763b66829520e3f6de3bc83be7))
|
10
|
+
* Add Flex message components for Line messaging with Box, Bubble, and Text support ([92c2c14](https://github.com/elct9620/line-message-builder/commit/92c2c14be36307993452d1705110c8ffecd162da))
|
11
|
+
* Add image method to Flex::Box for creating flex images ([b111f67](https://github.com/elct9620/line-message-builder/commit/b111f67c50777855df09cf2fc7441dea21b13c5b))
|
12
|
+
* Add nested box support in Flex message builder ([b93ea98](https://github.com/elct9620/line-message-builder/commit/b93ea98130de3011bebbc9c216fbfa1de35c8270))
|
13
|
+
* Add optional parameters to Flex Bubble component methods ([5e37949](https://github.com/elct9620/line-message-builder/commit/5e379492c6235756e9c3e0ce2489da2202e59e45))
|
14
|
+
* Add quick reply support to Line message builder with RSpec matchers ([3ea78af](https://github.com/elct9620/line-message-builder/commit/3ea78af7cb0f4334e6d36b6b1567405ef6eb27f4))
|
15
|
+
* Add spec for Line flex text with various text attributes ([8501fb7](https://github.com/elct9620/line-message-builder/commit/8501fb7803bf679697034b9d113714557eaea6c3))
|
16
|
+
* Add support for alt_text in Flex message builder and RSpec matcher ([8b64ba4](https://github.com/elct9620/line-message-builder/commit/8b64ba4566245d10e95aaf88ed303c69f7eeecb2))
|
17
|
+
* Add support for button style and height in flex message matchers ([8de297d](https://github.com/elct9620/line-message-builder/commit/8de297d88a1d4276d6ceb221777b006cccb71257))
|
18
|
+
* Add support for hero images in Line Flex messages ([d776264](https://github.com/elct9620/line-message-builder/commit/d776264e6e276bd4048362ff3cdb6cc9dda91643))
|
data/README.md
CHANGED
@@ -24,7 +24,7 @@ gem install line-message-builder
|
|
24
24
|
### Builder
|
25
25
|
|
26
26
|
```ruby
|
27
|
-
builder = Line::MessageBuilder::Builder.
|
27
|
+
builder = Line::MessageBuilder::Builder.with do
|
28
28
|
text "Hello, world!"
|
29
29
|
end
|
30
30
|
|
@@ -44,7 +44,7 @@ context = OpenStruct.new(
|
|
44
44
|
name: "John Doe",
|
45
45
|
)
|
46
46
|
|
47
|
-
builder = Line::MessageBuilder::Builder.
|
47
|
+
builder = Line::MessageBuilder::Builder.with(context) do
|
48
48
|
text "Hello, #{name}!"
|
49
49
|
end
|
50
50
|
|
@@ -59,7 +59,7 @@ For Rails, you can use `view_context` to make `Builder` to access Rails helpers.
|
|
59
59
|
|
60
60
|
```ruby
|
61
61
|
# app/controllers/line_controller.rb
|
62
|
-
builder = Line::MessageBuilder::Builder.
|
62
|
+
builder = Line::MessageBuilder::Builder.with(view_context) do
|
63
63
|
text "Anything you want?" do
|
64
64
|
quick_reply do
|
65
65
|
action "Yes", label: "Yes", image_url: image_url("yes.png")
|
@@ -79,18 +79,54 @@ context = ActionView::Base.new(
|
|
79
79
|
ActionController::Base.new,
|
80
80
|
)
|
81
81
|
|
82
|
-
builder = Line::MessageBuilder::Builder.
|
82
|
+
builder = Line::MessageBuilder::Builder.with(context) do
|
83
83
|
text "Anything you want?" do
|
84
84
|
quick_reply do
|
85
|
-
action "Yes", label: "Yes", image_url:
|
86
|
-
action "No", label: "No", image_url:
|
85
|
+
action "Yes", label: "Yes", image_url: image_url("yes.png")
|
86
|
+
action "No", label: "No", image_url: image_url("no.png")
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
90
90
|
```
|
91
91
|
|
92
|
+
### RSpec Matcher
|
93
|
+
|
94
|
+
Add `line/message/rspec` to your `spec_helper.rb` or `rails_helper.rb`:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
require "line/message/rspec"
|
98
|
+
```
|
99
|
+
|
100
|
+
Include `Line::Message::RSpec::Matchers` in your RSpec configuration:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
RSpec.configure do |config|
|
104
|
+
config.include Line::Message::RSpec::Matchers
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
Then the matchers are available in your specs:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
let(:builder) do
|
112
|
+
Line::MessageBuilder::Builder.with do
|
113
|
+
text "Hello, world!"
|
114
|
+
text "Nice to meet you!"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
subject { builder.build }
|
119
|
+
|
120
|
+
it { is_expected.to have_line_text_message("Hello, world!") }
|
121
|
+
it { is_expected.to have_line_text_message(/Nice to meet you!/) }
|
122
|
+
```
|
123
|
+
|
92
124
|
## Capabilities
|
93
125
|
|
126
|
+
- ✅ Supported
|
127
|
+
- 🚧 Partially Supported
|
128
|
+
- ❌ Not Supported
|
129
|
+
|
94
130
|
### Message Types
|
95
131
|
|
96
132
|
| Type | Supported |
|
@@ -105,13 +141,20 @@ end
|
|
105
141
|
| Location | ❌ |
|
106
142
|
| Imagemap | ❌ |
|
107
143
|
| Template | ❌ |
|
108
|
-
| Flex |
|
144
|
+
| Flex | 🚧 |
|
145
|
+
|
146
|
+
### Common Properties
|
147
|
+
|
148
|
+
| Property | Supported |
|
149
|
+
| -------- | --------- |
|
150
|
+
| Quick Reply | ✅ |
|
151
|
+
| Sender | ❌ |
|
109
152
|
|
110
153
|
### Actions
|
111
154
|
|
112
155
|
| Action Type | Supported |
|
113
156
|
| ----------- | --------- |
|
114
|
-
| Postback |
|
157
|
+
| Postback | 🚧 |
|
115
158
|
| Message | ✅ |
|
116
159
|
| Uri | ❌ |
|
117
160
|
| Datetime | ❌ |
|
@@ -121,6 +164,22 @@ end
|
|
121
164
|
| Richmenu Switch | ❌ |
|
122
165
|
| Clipboard | ❌ |
|
123
166
|
|
167
|
+
### Flex Components
|
168
|
+
|
169
|
+
| Component | Supported |
|
170
|
+
| --------- | --------- |
|
171
|
+
| Bubble | 🚧 |
|
172
|
+
| Carousel | ❌ |
|
173
|
+
| Box | 🚧 |
|
174
|
+
| Button | 🚧 |
|
175
|
+
| Image | 🚧 |
|
176
|
+
| Video | ❌ |
|
177
|
+
| Icon | ❌ |
|
178
|
+
| Text | 🚧 |
|
179
|
+
| Span | ❌ |
|
180
|
+
| Separator | ❌ |
|
181
|
+
| Filler | ❌ |
|
182
|
+
|
124
183
|
## Development
|
125
184
|
|
126
185
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -2,17 +2,52 @@
|
|
2
2
|
|
3
3
|
module Line
|
4
4
|
module Message
|
5
|
-
|
5
|
+
module Builder
|
6
6
|
# The base class to provide DSL functionality.
|
7
7
|
class Base
|
8
|
+
class << self
|
9
|
+
def inherited(subclass)
|
10
|
+
super
|
11
|
+
subclass.extend ClassMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# :nodoc:
|
16
|
+
module ClassMethods
|
17
|
+
def options
|
18
|
+
@options ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
def option(name, default: nil)
|
22
|
+
options << name
|
23
|
+
|
24
|
+
define_method name do |*args|
|
25
|
+
if args.empty?
|
26
|
+
instance_variable_get("@#{name}") || default
|
27
|
+
else
|
28
|
+
instance_variable_set("@#{name}", args.first)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
8
34
|
attr_reader :context
|
9
35
|
|
10
|
-
def initialize(context: nil, &block)
|
36
|
+
def initialize(context: nil, **options, &block)
|
11
37
|
@context = context
|
38
|
+
@quick_reply = nil
|
39
|
+
|
40
|
+
self.class.options.each do |option|
|
41
|
+
instance_variable_set("@#{option}", options[option]) if options.key?(option)
|
42
|
+
end
|
12
43
|
|
13
44
|
instance_eval(&block) if ::Kernel.block_given?
|
14
45
|
end
|
15
46
|
|
47
|
+
def quick_reply(&)
|
48
|
+
@quick_reply = QuickReply.new(context: context, &)
|
49
|
+
end
|
50
|
+
|
16
51
|
def respond_to_missing?(method_name, include_private = false)
|
17
52
|
context.respond_to?(method_name, include_private) || super
|
18
53
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
# The container class is main container to manage messages.
|
7
|
+
class Container < Base
|
8
|
+
attr_reader :context
|
9
|
+
|
10
|
+
def initialize(context: nil, &)
|
11
|
+
@messages = []
|
12
|
+
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def text(text, &)
|
17
|
+
@messages << Text.new(text, context: context, &)
|
18
|
+
end
|
19
|
+
|
20
|
+
def flex(**args, &)
|
21
|
+
@messages << Flex::Builder.new(**args, context: context, &)
|
22
|
+
end
|
23
|
+
|
24
|
+
def build
|
25
|
+
@messages.map(&:to_h)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_json(*args)
|
29
|
+
build.to_json(*args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
module Flex
|
7
|
+
# The box is a component for the Flex message.
|
8
|
+
class Box < Line::Message::Builder::Base
|
9
|
+
option :layout, default: :horizontal
|
10
|
+
|
11
|
+
def initialize(context: nil, **options, &)
|
12
|
+
@contents = []
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def box(**options, &)
|
18
|
+
@contents << Flex::Box.new(context: context, **options, &)
|
19
|
+
end
|
20
|
+
|
21
|
+
def text(text, **options, &)
|
22
|
+
@contents << Flex::Text.new(text, context: context, **options, &)
|
23
|
+
end
|
24
|
+
|
25
|
+
def button(**options, &)
|
26
|
+
@contents << Flex::Button.new(context: context, **options, &)
|
27
|
+
end
|
28
|
+
|
29
|
+
def image(url, **options, &)
|
30
|
+
@contents << Flex::Image.new(url, context: context, **options, &)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_h
|
34
|
+
{
|
35
|
+
type: "box",
|
36
|
+
layout: @layout,
|
37
|
+
contents: @contents.map(&:to_h)
|
38
|
+
}.compact
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
module Flex
|
7
|
+
# The bubble is container for the Flex message.
|
8
|
+
class Bubble < Line::Message::Builder::Base
|
9
|
+
def initialize(context: nil, &)
|
10
|
+
@header = nil
|
11
|
+
@hero = nil
|
12
|
+
@body = nil
|
13
|
+
@footer = nil
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def header(**options, &)
|
19
|
+
@header = Box.new(**options, context: context, &)
|
20
|
+
end
|
21
|
+
|
22
|
+
def hero(**options, &)
|
23
|
+
@hero = Box.new(**options, context: context, &)
|
24
|
+
end
|
25
|
+
|
26
|
+
def hero_image(url, **options, &)
|
27
|
+
@hero = Image.new(url, **options, context: context, &)
|
28
|
+
end
|
29
|
+
|
30
|
+
def body(**options, &)
|
31
|
+
@body = Box.new(**options, context: context, &)
|
32
|
+
end
|
33
|
+
|
34
|
+
def footer(**options, &)
|
35
|
+
@footer = Box.new(**options, context: context, &)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_h
|
39
|
+
{
|
40
|
+
type: "bubble",
|
41
|
+
header: @header&.to_h,
|
42
|
+
hero: @hero&.to_h,
|
43
|
+
body: @body&.to_h,
|
44
|
+
footer: @footer&.to_h
|
45
|
+
}.compact
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
module Flex
|
7
|
+
# The Builder class is used to build quick reply buttons.
|
8
|
+
class Builder < Line::Message::Builder::Base
|
9
|
+
option :alt_text, default: nil
|
10
|
+
|
11
|
+
def initialize(context: nil, **options, &)
|
12
|
+
@contents = []
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def bubble(&)
|
18
|
+
@contents << Line::Message::Builder::Flex::Bubble.new(context: context, &)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_h
|
22
|
+
{
|
23
|
+
type: "flex",
|
24
|
+
altText: @alt_text,
|
25
|
+
contents: @contents.map(&:to_h),
|
26
|
+
quickReply: @quick_reply&.to_h
|
27
|
+
}.compact
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
module Flex
|
7
|
+
# The button is a component of the Flex message.
|
8
|
+
class Button < Line::Message::Builder::Base
|
9
|
+
option :style, default: :link
|
10
|
+
option :height, default: :md
|
11
|
+
|
12
|
+
def initialize(context: nil, **options, &)
|
13
|
+
@action = nil
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def message(text, label:, display_text: nil, &)
|
19
|
+
@action = Actions::Message.new(text: text, label: label, display_text: display_text, &)
|
20
|
+
end
|
21
|
+
|
22
|
+
def postback(data, label: nil, display_text: nil, &)
|
23
|
+
@action = Actions::Postback.new(data: data, label: label, display_text: display_text, &)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
raise Error, "Action is required" unless @action
|
28
|
+
|
29
|
+
{
|
30
|
+
type: "button",
|
31
|
+
action: @action.to_h,
|
32
|
+
style: @style,
|
33
|
+
height: @height
|
34
|
+
}.compact
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
module Flex
|
7
|
+
# The image is a component for the Flex message.
|
8
|
+
class Image < Line::Message::Builder::Base
|
9
|
+
option :size, default: nil
|
10
|
+
option :aspect_ratio, default: nil
|
11
|
+
option :aspect_mode, default: nil
|
12
|
+
|
13
|
+
def initialize(url, context: nil, **options, &)
|
14
|
+
@url = url
|
15
|
+
|
16
|
+
super(context: context, **options, &)
|
17
|
+
end
|
18
|
+
|
19
|
+
def size(size)
|
20
|
+
@size = size
|
21
|
+
end
|
22
|
+
|
23
|
+
def aspect_ratio(aspect_ratio)
|
24
|
+
@aspect_ratio = aspect_ratio
|
25
|
+
end
|
26
|
+
|
27
|
+
def aspect_mode(aspect_mode)
|
28
|
+
@aspect_mode = aspect_mode
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
{
|
33
|
+
type: "image",
|
34
|
+
url: @url,
|
35
|
+
size: @size,
|
36
|
+
aspectRatio: @aspect_ratio,
|
37
|
+
aspectMode: @aspect_mode
|
38
|
+
}.compact
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
module Flex
|
7
|
+
# The text is a component of the Flex message.
|
8
|
+
class Text < Line::Message::Builder::Base
|
9
|
+
option :wrap, default: false
|
10
|
+
option :line_spacing, default: nil
|
11
|
+
option :color, default: nil
|
12
|
+
option :size, default: nil
|
13
|
+
option :align, default: nil
|
14
|
+
option :flex, default: nil
|
15
|
+
|
16
|
+
def initialize(text, context: nil, **options, &)
|
17
|
+
@text = text
|
18
|
+
|
19
|
+
super(context: context, **options, &)
|
20
|
+
end
|
21
|
+
|
22
|
+
def wrap!
|
23
|
+
@wrap = true
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
{
|
28
|
+
type: "text",
|
29
|
+
text: @text,
|
30
|
+
wrap: @wrap,
|
31
|
+
lineSpacing: @line_spacing,
|
32
|
+
color: @color,
|
33
|
+
size: @size,
|
34
|
+
align: @align,
|
35
|
+
flex: @flex
|
36
|
+
}.compact
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module Builder
|
6
|
+
# The Flex module allows to build Flex messages.
|
7
|
+
module Flex
|
8
|
+
require_relative "flex/builder"
|
9
|
+
|
10
|
+
# Container
|
11
|
+
require_relative "flex/bubble"
|
12
|
+
|
13
|
+
# Components
|
14
|
+
require_relative "flex/box"
|
15
|
+
require_relative "flex/text"
|
16
|
+
require_relative "flex/button"
|
17
|
+
require_relative "flex/image"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -2,13 +2,45 @@
|
|
2
2
|
|
3
3
|
module Line
|
4
4
|
module Message
|
5
|
-
|
5
|
+
module Builder
|
6
6
|
# The QuickReply allows to attach quick reply buttons to a message.
|
7
|
-
|
8
|
-
|
7
|
+
class QuickReply < Line::Message::Builder::Base
|
8
|
+
def initialize(context: nil, &)
|
9
|
+
@items = []
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def message(text, label:, image_url: nil, &)
|
15
|
+
action(
|
16
|
+
Actions::Message.new(text: text, label: label, &),
|
17
|
+
image_url
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def postback(data, label: nil, display_text: nil, image_url: nil, &)
|
22
|
+
action(
|
23
|
+
Actions::Postback.new(data: data, label: label, display_text: display_text, &),
|
24
|
+
image_url
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_h
|
29
|
+
{
|
30
|
+
items: @items.map do |item, image_url|
|
31
|
+
{
|
32
|
+
type: "action",
|
33
|
+
imageUrl: image_url,
|
34
|
+
action: item.to_h
|
35
|
+
}
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def action(action, image_url)
|
43
|
+
@items << [action, image_url]
|
12
44
|
end
|
13
45
|
end
|
14
46
|
end
|
data/lib/line/message/builder.rb
CHANGED
@@ -5,45 +5,20 @@ require_relative "builder/version"
|
|
5
5
|
module Line
|
6
6
|
module Message
|
7
7
|
# The Builder module provides a DSL for building LINE messages.
|
8
|
-
|
8
|
+
module Builder
|
9
9
|
class Error < StandardError; end
|
10
10
|
|
11
11
|
require_relative "builder/base"
|
12
12
|
require_relative "builder/actions"
|
13
13
|
require_relative "builder/quick_reply"
|
14
|
+
require_relative "builder/container"
|
14
15
|
require_relative "builder/text"
|
16
|
+
require_relative "builder/flex"
|
15
17
|
|
16
|
-
|
18
|
+
module_function
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
-
@context = context
|
21
|
-
|
22
|
-
instance_eval(&) if ::Kernel.block_given?
|
23
|
-
end
|
24
|
-
|
25
|
-
def text(text)
|
26
|
-
@messages << Text.new(text, context: context)
|
27
|
-
end
|
28
|
-
|
29
|
-
def build
|
30
|
-
@messages.map(&:to_h)
|
31
|
-
end
|
32
|
-
|
33
|
-
def to_json(*args)
|
34
|
-
build.to_json(*args)
|
35
|
-
end
|
36
|
-
|
37
|
-
def respond_to_missing?(method_name, include_private = false)
|
38
|
-
context.respond_to?(method_name, include_private) || super
|
39
|
-
end
|
40
|
-
|
41
|
-
def method_missing(method_name, ...)
|
42
|
-
if context.respond_to?(method_name)
|
43
|
-
context.public_send(method_name, ...)
|
44
|
-
else
|
45
|
-
super
|
46
|
-
end
|
20
|
+
def with(context = nil, &)
|
21
|
+
Container.new(context: context, &)
|
47
22
|
end
|
48
23
|
end
|
49
24
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module RSpec
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
# The flex component matcher for RSpec to search nested flex components in the message array.
|
9
|
+
class HaveFlexComponent
|
10
|
+
def initialize(expected_desc: nil, &expected)
|
11
|
+
@expected = expected
|
12
|
+
@expected_desc = expected_desc
|
13
|
+
end
|
14
|
+
|
15
|
+
def description
|
16
|
+
return "have flex component" if @expected.nil?
|
17
|
+
return "have flex component matching #{@expected_desc}" if @expected_desc
|
18
|
+
|
19
|
+
"have flex component matching #{@expected.inspect}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def matches?(actual)
|
23
|
+
@actual = actual
|
24
|
+
@actual.any? { |message| match_flex_component?(message) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def failure_message
|
28
|
+
return "expected to find a flex component" if @expected.nil?
|
29
|
+
return "expected to find a flex component matching #{@expected_desc}" if @expected_desc
|
30
|
+
|
31
|
+
"expected to find a flex component matching #{@expected.inspect}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def match_flex_component?(message)
|
37
|
+
return false unless message[:type] == "flex"
|
38
|
+
|
39
|
+
message[:contents].any? { |content| match_content?(content) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def match_content?(content)
|
43
|
+
return match_bubble?(content) if content[:type] == "bubble"
|
44
|
+
|
45
|
+
if content[:contents]
|
46
|
+
return content[:contents].any? do |nested_content|
|
47
|
+
match_content?(nested_content)
|
48
|
+
end || @expected.call(content)
|
49
|
+
end
|
50
|
+
|
51
|
+
@expected.call(content)
|
52
|
+
end
|
53
|
+
|
54
|
+
def match_bubble?(content)
|
55
|
+
%i[header hero body footer].any? do |key|
|
56
|
+
block = content[key]
|
57
|
+
next unless block
|
58
|
+
|
59
|
+
match_bubble_block?(block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def match_bubble_block?(block)
|
64
|
+
return block[:contents].any? { |nested_content| match_content?(nested_content) } if block[:contents]
|
65
|
+
|
66
|
+
match_content?(block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def have_line_flex_component(&) # rubocop:disable Naming/PredicateName
|
71
|
+
HaveFlexComponent.new(&)
|
72
|
+
end
|
73
|
+
|
74
|
+
def have_line_flex_box(**options) # rubocop:disable Naming/PredicateName
|
75
|
+
HaveFlexComponent.new(expected_desc: "box(#{options.inspect})") do |content|
|
76
|
+
next false unless content[:type] == "box"
|
77
|
+
|
78
|
+
::RSpec::Matchers::BuiltIn::Include.new(options).matches?(content)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def have_line_flex_text(text, **options) # rubocop:disable Naming/PredicateName
|
83
|
+
HaveFlexComponent.new(expected_desc: "text(#{text.inspect})") do |content|
|
84
|
+
next false unless content[:type] == "text"
|
85
|
+
|
86
|
+
content[:text].match?(text) && ::RSpec::Matchers::BuiltIn::Include.new(options).matches?(content)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def have_line_flex_button(type, **options) # rubocop:disable Naming/PredicateName
|
91
|
+
HaveFlexComponent.new(expected_desc: "#{type} button(#{options.inspect})") do |content|
|
92
|
+
next false unless content[:type] == "button"
|
93
|
+
|
94
|
+
::RSpec::Matchers::BuiltIn::Include.new({ type: type, **options }).matches?(content[:action]) ||
|
95
|
+
::RSpec::Matchers::BuiltIn::Include.new({ **options, action: ::RSpec::Matchers::BuiltIn::Include.new(
|
96
|
+
type: type
|
97
|
+
) }).matches?(content)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def have_line_flex_image(url, **options) # rubocop:disable Naming/PredicateName
|
102
|
+
HaveFlexComponent.new(expected_desc: "image(#{url.inspect})") do |content|
|
103
|
+
next false unless content[:type] == "image"
|
104
|
+
|
105
|
+
::RSpec::Matchers::BuiltIn::Include.new({ url: url, **options }).matches?(content)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module RSpec
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
# The flex message matcher for RSpec to search for flex messages in the message array.
|
9
|
+
class HaveFlexMessage
|
10
|
+
def initialize(expected)
|
11
|
+
@expected = expected
|
12
|
+
end
|
13
|
+
|
14
|
+
def description
|
15
|
+
return "have flex message" if @expected.nil?
|
16
|
+
|
17
|
+
"have flex message alt text matching #{@expected.inspect}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(actual)
|
21
|
+
@actual = actual
|
22
|
+
@actual.any? { |message| match_alt_text?(message) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_message
|
26
|
+
"expected to find a flex message alt text matching #{@expected}"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def match_alt_text?(message)
|
32
|
+
return false unless message[:type] == "flex"
|
33
|
+
return true if @expected.nil?
|
34
|
+
|
35
|
+
message[:altText].match?(@expected)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def have_line_flex_message(expected = nil) # rubocop:disable Naming/PredicateName
|
40
|
+
HaveFlexMessage.new(expected)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module RSpec
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
# The quick reply matcher for RSpec to search for quick reply action in the message array.
|
9
|
+
class HaveQuickReply
|
10
|
+
def initialize(expected)
|
11
|
+
@expected = expected
|
12
|
+
end
|
13
|
+
|
14
|
+
def description
|
15
|
+
return "have quick reply action" if @expected.nil?
|
16
|
+
|
17
|
+
"have quick reply action matching #{@expected}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(actual)
|
21
|
+
@actual = actual
|
22
|
+
@actual.any? { |message| match_message(message) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_message
|
26
|
+
"expected to find a quick reply message matching #{@expected}"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def match_message(message)
|
32
|
+
reply = message[:quickReply]
|
33
|
+
return false unless reply
|
34
|
+
|
35
|
+
reply[:items].any? { |item| match_action(item[:action]) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def match_action(action)
|
39
|
+
return true if @expected.nil?
|
40
|
+
return true if ::RSpec::Matchers::BuiltIn::Include.new(@expected).matches?(action)
|
41
|
+
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def have_line_quick_reply(expected = nil) # rubocop:disable Naming/PredicateName
|
47
|
+
HaveQuickReply.new(expected)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,17 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Line
|
4
|
+
module Message
|
5
|
+
module RSpec
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
# The text message matcher for RSpec to search for text messages in the message array.
|
9
|
+
class HaveTextMessage
|
10
|
+
def initialize(expected)
|
11
|
+
@expected = expected
|
12
|
+
end
|
7
13
|
|
8
|
-
|
9
|
-
|
14
|
+
def description
|
15
|
+
return "have text message" if @expected.nil?
|
10
16
|
|
11
|
-
|
12
|
-
|
17
|
+
"have text message matching #{@expected.inspect}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(actual)
|
21
|
+
@actual = actual
|
22
|
+
@actual.each do |message|
|
23
|
+
next unless message[:type] == "text"
|
24
|
+
|
25
|
+
return true if message[:text].match?(@expected)
|
26
|
+
end
|
13
27
|
|
14
|
-
|
15
|
-
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def failure_message
|
32
|
+
"expected to find a text message matching #{@expected}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def have_line_text_message(expected = nil) # rubocop:disable Naming/PredicateName
|
37
|
+
HaveTextMessage.new(expected)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
16
41
|
end
|
17
42
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: line-message-builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aotokitsuruya
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: The LINE Messaging API message builder.
|
14
14
|
email:
|
@@ -17,8 +17,10 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
+
- ".release-please-manifest.json"
|
20
21
|
- ".rspec"
|
21
22
|
- ".rubocop.yml"
|
23
|
+
- CHANGELOG.md
|
22
24
|
- CONVENTIONS.md
|
23
25
|
- LICENSE.txt
|
24
26
|
- README.md
|
@@ -28,12 +30,22 @@ files:
|
|
28
30
|
- lib/line/message/builder/actions/message.rb
|
29
31
|
- lib/line/message/builder/actions/postback.rb
|
30
32
|
- lib/line/message/builder/base.rb
|
33
|
+
- lib/line/message/builder/container.rb
|
34
|
+
- lib/line/message/builder/flex.rb
|
35
|
+
- lib/line/message/builder/flex/box.rb
|
36
|
+
- lib/line/message/builder/flex/bubble.rb
|
37
|
+
- lib/line/message/builder/flex/builder.rb
|
38
|
+
- lib/line/message/builder/flex/button.rb
|
39
|
+
- lib/line/message/builder/flex/image.rb
|
40
|
+
- lib/line/message/builder/flex/text.rb
|
31
41
|
- lib/line/message/builder/quick_reply.rb
|
32
|
-
- lib/line/message/builder/quick_reply/builder.rb
|
33
42
|
- lib/line/message/builder/text.rb
|
34
43
|
- lib/line/message/builder/version.rb
|
35
44
|
- lib/line/message/rspec.rb
|
36
45
|
- lib/line/message/rspec/matchers.rb
|
46
|
+
- lib/line/message/rspec/matchers/have_flex_component.rb
|
47
|
+
- lib/line/message/rspec/matchers/have_flex_message.rb
|
48
|
+
- lib/line/message/rspec/matchers/have_quick_reply.rb
|
37
49
|
- lib/line/message/rspec/matchers/have_text_message.rb
|
38
50
|
- release-please-config.json
|
39
51
|
- sig/line/message/builder.rbs
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Line
|
4
|
-
module Message
|
5
|
-
class Builder
|
6
|
-
module QuickReply
|
7
|
-
# The Builder class is used to build quick reply buttons.
|
8
|
-
class Builder < Line::Message::Builder::Base
|
9
|
-
def initialize(context: nil, &)
|
10
|
-
@items = []
|
11
|
-
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
def message(text, label:, image_url: nil, &)
|
16
|
-
action(
|
17
|
-
Actions::Message.new(text: text, label: label, &).to_h,
|
18
|
-
image_url
|
19
|
-
)
|
20
|
-
end
|
21
|
-
|
22
|
-
def postback(data, label: nil, display_text: nil, image_url: nil, &)
|
23
|
-
action(
|
24
|
-
Actions::Postback.new(data: data, label: label, display_text: display_text, &).to_h,
|
25
|
-
image_url
|
26
|
-
)
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_h
|
30
|
-
{ items: @items.map(&:to_h) }
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def action(action, image_url)
|
36
|
-
@items << {
|
37
|
-
type: "action",
|
38
|
-
imageUrl: image_url,
|
39
|
-
action: action
|
40
|
-
}.compact
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|