slack-ruby-block-kit 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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