slack-ruby-block-kit 0.4.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 (35) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +49 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +15 -0
  6. data/.rubocop_todo.yml +38 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +59 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +95 -0
  11. data/Rakefile +8 -0
  12. data/lib/slack-ruby-block-kit.rb +3 -0
  13. data/lib/slack/block_kit.rb +26 -0
  14. data/lib/slack/block_kit/blocks.rb +71 -0
  15. data/lib/slack/block_kit/composition/confirmation_dialog.rb +53 -0
  16. data/lib/slack/block_kit/composition/mrkdwn.rb +28 -0
  17. data/lib/slack/block_kit/composition/option.rb +25 -0
  18. data/lib/slack/block_kit/composition/option_group.rb +35 -0
  19. data/lib/slack/block_kit/composition/plain_text.rb +27 -0
  20. data/lib/slack/block_kit/element/button.rb +48 -0
  21. data/lib/slack/block_kit/element/channels_select.rb +48 -0
  22. data/lib/slack/block_kit/element/conversations_select.rb +49 -0
  23. data/lib/slack/block_kit/element/date_picker.rb +48 -0
  24. data/lib/slack/block_kit/element/external_select.rb +56 -0
  25. data/lib/slack/block_kit/element/image.rb +29 -0
  26. data/lib/slack/block_kit/element/overflow_menu.rb +58 -0
  27. data/lib/slack/block_kit/element/static_select.rb +81 -0
  28. data/lib/slack/block_kit/element/users_select.rb +48 -0
  29. data/lib/slack/block_kit/layout/actions.rb +138 -0
  30. data/lib/slack/block_kit/layout/context.rb +48 -0
  31. data/lib/slack/block_kit/layout/divider.rb +26 -0
  32. data/lib/slack/block_kit/layout/image.rb +37 -0
  33. data/lib/slack/block_kit/layout/section.rb +173 -0
  34. data/slack-ruby-block-kit.gemspec +37 -0
  35. metadata +161 -0
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module Element
6
+ # A select menu, just as with a standard HTML <select> tag, creates a drop
7
+ # down menu with a list of options for a user to choose. The select menu
8
+ # also includes type-ahead functionality, where a user can type a part or
9
+ # all of an option string to filter the list.
10
+ #
11
+ # This is the simplest form of select menu, with a static list of options
12
+ # passed in when defining the element.
13
+ #
14
+ # https://api.slack.com/reference/messaging/block-elements#static-select
15
+ class StaticSelect
16
+ TYPE = 'static_select'
17
+
18
+ attr_accessor :confirm, :options, :option_groups, :initial_option
19
+
20
+ def initialize(placeholder:, action_id:, emoji: nil)
21
+ @placeholder = Composition::PlainText.new(text: placeholder, emoji: emoji)
22
+ @action_id = action_id
23
+
24
+ yield(self) if block_given?
25
+ end
26
+
27
+ def confirmation_dialog
28
+ @confirm = Composition::ConfirmationDialog.new
29
+
30
+ yield(@confirm) if block_given?
31
+
32
+ self
33
+ end
34
+
35
+ def option(value:, text:, emoji: nil)
36
+ @options ||= []
37
+ @options << Composition::Option.new(
38
+ value: value,
39
+ text: text,
40
+ emoji: emoji
41
+ )
42
+
43
+ self
44
+ end
45
+
46
+ def option_group(label:, emoji: nil)
47
+ option_group = Composition::OptionGroup.new(label: label, emoji: emoji)
48
+
49
+ yield(option_group) if block_given?
50
+
51
+ @option_groups ||= []
52
+ @option_groups << option_group
53
+
54
+ self
55
+ end
56
+
57
+ def initial(value:, text:, emoji: nil)
58
+ @initial_option = Composition::Option.new(
59
+ value: value,
60
+ text: text,
61
+ emoji: emoji
62
+ )
63
+
64
+ self
65
+ end
66
+
67
+ def as_json(*)
68
+ {
69
+ type: TYPE,
70
+ placeholder: @placeholder.as_json,
71
+ action_id: @action_id,
72
+ options: @options&.map(&:as_json),
73
+ option_groups: @option_groups&.map(&:as_json),
74
+ initial_option: @initial_option&.as_json,
75
+ confirm: @confirm&.as_json
76
+ }.compact
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module Element
6
+ # A select menu, just as with a standard HTML <select> tag, creates a drop
7
+ # down menu with a list of options for a user to choose. The select menu
8
+ # also includes type-ahead functionality, where a user can type a part or
9
+ # all of an option string to filter the list.
10
+ #
11
+ # This select menu will populate its options with a list of Slack users
12
+ # visible to the current user in the active workspace.
13
+ #
14
+ # https://api.slack.com/reference/messaging/block-elements#users-select
15
+ class UsersSelect
16
+ TYPE = 'users_select'
17
+
18
+ attr_accessor :confirm
19
+
20
+ def initialize(placeholder:, action_id:, initial: nil, emoji: nil)
21
+ @placeholder = Composition::PlainText.new(text: placeholder, emoji: emoji)
22
+ @action_id = action_id
23
+ @initial_user = initial
24
+
25
+ yield(self) if block_given?
26
+ end
27
+
28
+ def confirmation_dialog
29
+ @confirm = Composition::ConfirmationDialog.new
30
+
31
+ yield(@confirm) if block_given?
32
+
33
+ self
34
+ end
35
+
36
+ def as_json(*)
37
+ {
38
+ type: TYPE,
39
+ placeholder: @placeholder.as_json,
40
+ action_id: @action_id,
41
+ initial_user: @initial_user,
42
+ confirm: @confirm&.as_json
43
+ }.compact
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module Layout
6
+ # A block that is used to hold interactive elements.
7
+ #
8
+ # https://api.slack.com/reference/messaging/blocks#actions
9
+ class Actions
10
+ TYPE = 'actions'
11
+
12
+ attr_accessor :elements
13
+
14
+ def initialize(block_id: nil)
15
+ @block_id = block_id
16
+ @elements = []
17
+
18
+ yield(self) if block_given?
19
+ end
20
+
21
+ def button(text:, action_id:, style: nil, emoji: nil, url: nil, value: nil)
22
+ element = Element::Button.new(
23
+ text: text,
24
+ action_id: action_id,
25
+ style: style,
26
+ emoji: emoji,
27
+ url: url,
28
+ value: value
29
+ )
30
+
31
+ yield(element) if block_given?
32
+
33
+ append(element)
34
+ end
35
+
36
+ def channel_select(placeholder:, action_id:, initial: nil, emoji: nil)
37
+ element = Element::ChannelsSelect.new(
38
+ placeholder: placeholder,
39
+ action_id: action_id,
40
+ initial: initial,
41
+ emoji: emoji
42
+ )
43
+
44
+ yield(element) if block_given?
45
+
46
+ append(element)
47
+ end
48
+
49
+ def converstation_select(placeholder:, action_id:, initial: nil, emoji: nil)
50
+ element = Element::ConversationsSelect.new(
51
+ placeholder: placeholder,
52
+ action_id: action_id,
53
+ initial: initial,
54
+ emoji: emoji
55
+ )
56
+
57
+ yield(element) if block_given?
58
+
59
+ append(element)
60
+ end
61
+
62
+ def date_picker(action_id:, placeholder: nil, initial: nil, emoji: nil)
63
+ element = Element::DatePicker.new(
64
+ placeholder: placeholder,
65
+ action_id: action_id,
66
+ initial: initial,
67
+ emoji: emoji
68
+ )
69
+
70
+ yield(element) if block_given?
71
+
72
+ append(element)
73
+ end
74
+
75
+ def external_select(placeholder:, action_id:, initial: nil, min_query_length: nil, emoji: nil)
76
+ element = Element::ExternalSelect.new(
77
+ placeholder: placeholder,
78
+ action_id: action_id,
79
+ initial: initial,
80
+ min_query_length: min_query_length,
81
+ emoji: emoji
82
+ )
83
+
84
+ yield(element) if block_given?
85
+
86
+ append(element)
87
+ end
88
+
89
+ def overflow_menu(action_id:)
90
+ element = Element::OverflowMenu.new(action_id: action_id)
91
+
92
+ yield(element) if block_given?
93
+
94
+ append(element)
95
+ end
96
+
97
+ def static_select(placeholder:, action_id:, emoji: nil)
98
+ element = Element::StaticSelect.new(
99
+ placeholder: placeholder,
100
+ action_id: action_id,
101
+ emoji: emoji
102
+ )
103
+
104
+ yield(element) if block_given?
105
+
106
+ append(element)
107
+ end
108
+
109
+ def users_select(placeholder:, action_id:, initial: nil, emoji: nil)
110
+ element = Element::UsersSelect.new(
111
+ placeholder: placeholder,
112
+ action_id: action_id,
113
+ emoji: emoji,
114
+ initial: initial
115
+ )
116
+
117
+ yield(element) if block_given?
118
+
119
+ append(element)
120
+ end
121
+
122
+ def append(element)
123
+ @elements << element
124
+
125
+ self
126
+ end
127
+
128
+ def as_json(*)
129
+ {
130
+ type: TYPE,
131
+ elements: @elements.map(&:as_json),
132
+ block_id: @block_id
133
+ }.compact
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module Layout
6
+ # Displays message context, which can include both images and text.
7
+ #
8
+ # https://api.slack.com/reference/messaging/blocks#context
9
+ class Context
10
+ TYPE = 'context'
11
+
12
+ attr_accessor :elements
13
+ def initialize(block_id: nil)
14
+ @block_id = block_id
15
+ @elements = []
16
+
17
+ yield(self) if block_given?
18
+ end
19
+
20
+ def image(url:, alt_text:)
21
+ append(Element::Image.new(image_url: url, alt_text: alt_text))
22
+ end
23
+
24
+ def plain_text(text:, emoji: nil)
25
+ append(Composition::PlainText.new(text: text, emoji: emoji))
26
+ end
27
+
28
+ def mrkdwn(text:, verbatim: nil)
29
+ append(Composition::Mrkdwn.new(text: text, verbatim: verbatim))
30
+ end
31
+
32
+ def append(element)
33
+ @elements << element
34
+
35
+ self
36
+ end
37
+
38
+ def as_json(*)
39
+ {
40
+ type: TYPE,
41
+ elements: @elements.map(&:as_json),
42
+ block_id: @block_id
43
+ }.compact
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module Layout
6
+ # A content divider, like an <hr>, to split up different blocks inside of
7
+ # a message.
8
+ #
9
+ # https://api.slack.com/reference/messaging/blocks#divider
10
+ class Divider
11
+ TYPE = 'divider'
12
+
13
+ def initialize(block_id: nil)
14
+ @block_id = block_id
15
+ end
16
+
17
+ def as_json(*)
18
+ {
19
+ type: TYPE,
20
+ block_id: @block_id
21
+ }.compact
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module Layout
6
+ # A simple image block, designed to make those cat photos really pop.
7
+ #
8
+ # https://api.slack.com/reference/messaging/blocks#context
9
+ class Image
10
+ TYPE = 'image'
11
+
12
+ def initialize(url:, alt_text:, title: nil, block_id: nil, emoji: nil)
13
+ @image_url = url
14
+ @alt_text = alt_text
15
+ @block_id = block_id
16
+
17
+ return unless title
18
+
19
+ @title = Composition::PlainText.new(
20
+ text: title,
21
+ emoji: emoji
22
+ )
23
+ end
24
+
25
+ def as_json(*)
26
+ {
27
+ type: TYPE,
28
+ image_url: @image_url,
29
+ alt_text: @alt_text,
30
+ title: @title&.as_json,
31
+ block_id: @block_id
32
+ }.compact
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module Layout
6
+ # A section is one of the most flexible blocks available - it can be used
7
+ # as a simple text block, in combination with text fields, or side-by-side
8
+ # with any of the available block elements.
9
+ #
10
+ # https://api.slack.com/reference/messaging/blocks#section
11
+ class Section
12
+ TYPE = 'section'
13
+
14
+ attr_accessor :fields, :text, :accessory
15
+
16
+ def initialize(block_id: nil)
17
+ @block_id = block_id
18
+
19
+ yield(self) if block_given?
20
+ end
21
+
22
+ def plaintext_field(text:, emoji: nil)
23
+ @fields ||= []
24
+
25
+ @fields << Composition::PlainText.new(text: text, emoji: emoji)
26
+
27
+ self
28
+ end
29
+
30
+ def mrkdwn_field(text:, verbatim: nil)
31
+ @fields ||= []
32
+
33
+ @fields << Composition::Mrkdwn.new(text: text, verbatim: verbatim)
34
+
35
+ self
36
+ end
37
+
38
+ def plain_text(text:, emoji: nil)
39
+ @text = Composition::PlainText.new(text: text, emoji: emoji)
40
+
41
+ self
42
+ end
43
+
44
+ def mrkdwn(text:, verbatim: nil)
45
+ @text = Composition::Mrkdwn.new(text: text, verbatim: verbatim)
46
+
47
+ self
48
+ end
49
+
50
+ def button(text:, action_id:, style: nil, emoji: nil, url: nil, value: nil)
51
+ element = Element::Button.new(
52
+ text: text,
53
+ action_id: action_id,
54
+ style: style,
55
+ emoji: emoji,
56
+ url: url,
57
+ value: value
58
+ )
59
+
60
+ yield(element) if block_given?
61
+
62
+ accessorise(element)
63
+ end
64
+
65
+ def channel_select(placeholder:, action_id:, initial: nil, emoji: nil)
66
+ element = Element::ChannelsSelect.new(
67
+ placeholder: placeholder,
68
+ action_id: action_id,
69
+ initial: initial,
70
+ emoji: emoji
71
+ )
72
+
73
+ yield(element) if block_given?
74
+
75
+ accessorise(element)
76
+ end
77
+
78
+ def conversation_select(placeholder:, action_id:, initial: nil, emoji: nil)
79
+ element = Element::ConversationsSelect.new(
80
+ placeholder: placeholder,
81
+ action_id: action_id,
82
+ initial: initial,
83
+ emoji: emoji
84
+ )
85
+
86
+ yield(element) if block_given?
87
+
88
+ accessorise(element)
89
+ end
90
+
91
+ def date_picker(action_id:, placeholder: nil, initial: nil, emoji: nil)
92
+ element = Element::DatePicker.new(
93
+ placeholder: placeholder,
94
+ action_id: action_id,
95
+ initial: initial,
96
+ emoji: emoji
97
+ )
98
+
99
+ yield(element) if block_given?
100
+
101
+ accessorise(element)
102
+ end
103
+
104
+ def external_select(placeholder:, action_id:, initial: nil, min_query_length: nil, emoji: nil)
105
+ element = Element::ExternalSelect.new(
106
+ placeholder: placeholder,
107
+ action_id: action_id,
108
+ initial: initial,
109
+ min_query_length: min_query_length,
110
+ emoji: emoji
111
+ )
112
+
113
+ yield(element) if block_given?
114
+
115
+ accessorise(element)
116
+ end
117
+
118
+ def overflow_menu(action_id:)
119
+ element = Element::OverflowMenu.new(action_id: action_id)
120
+
121
+ yield(element) if block_given?
122
+
123
+ accessorise(element)
124
+ end
125
+
126
+ def static_select(placeholder:, action_id:, emoji: nil)
127
+ element = Element::StaticSelect.new(
128
+ placeholder: placeholder,
129
+ action_id: action_id,
130
+ emoji: emoji
131
+ )
132
+
133
+ yield(element) if block_given?
134
+
135
+ accessorise(element)
136
+ end
137
+
138
+ def users_select(placeholder:, action_id:, initial: nil, emoji: nil)
139
+ element = Element::UsersSelect.new(
140
+ placeholder: placeholder,
141
+ action_id: action_id,
142
+ emoji: emoji,
143
+ initial: initial
144
+ )
145
+
146
+ yield(element) if block_given?
147
+
148
+ accessorise(element)
149
+ end
150
+
151
+ def image(url:, alt_text:)
152
+ accessorize(Element::Image.new(image_url: url, alt_text: alt_text))
153
+ end
154
+
155
+ def accessorise(element)
156
+ @accessory = element
157
+
158
+ self
159
+ end
160
+
161
+ def as_json(*)
162
+ {
163
+ type: TYPE,
164
+ text: @text.as_json,
165
+ block_id: @block_id,
166
+ fields: @fields&.map(&:as_json),
167
+ accessory: @accessory&.as_json
168
+ }.compact
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end