slack-block-kit 0.1.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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +4 -0
  6. data/README.md +62 -0
  7. data/Rakefile +6 -0
  8. data/bin/console +27 -0
  9. data/bin/setup +8 -0
  10. data/examples/slack_template_00.rb +46 -0
  11. data/examples/slack_template_01.rb +22 -0
  12. data/examples/slack_template_02.rb +28 -0
  13. data/examples/slack_template_03.rb +57 -0
  14. data/examples/slack_template_04.rb +29 -0
  15. data/examples/slack_template_05.rb +12 -0
  16. data/examples/slack_template_06.rb +53 -0
  17. data/examples/slack_template_07.rb +74 -0
  18. data/examples/slack_template_08.rb +77 -0
  19. data/examples/slack_template_09.rb +53 -0
  20. data/lib/slack-block-kit.rb +1 -0
  21. data/lib/slack/block_kit.rb +38 -0
  22. data/lib/slack/block_kit/block.rb +41 -0
  23. data/lib/slack/block_kit/block/actions_block.rb +35 -0
  24. data/lib/slack/block_kit/block/context_block.rb +30 -0
  25. data/lib/slack/block_kit/block/divider_block.rb +17 -0
  26. data/lib/slack/block_kit/block/image_block.rb +44 -0
  27. data/lib/slack/block_kit/block/section_block.rb +55 -0
  28. data/lib/slack/block_kit/composition_objects/confirmation_dialog.rb +59 -0
  29. data/lib/slack/block_kit/composition_objects/filter.rb +38 -0
  30. data/lib/slack/block_kit/composition_objects/option.rb +57 -0
  31. data/lib/slack/block_kit/composition_objects/option_group.rb +34 -0
  32. data/lib/slack/block_kit/composition_objects/text.rb +61 -0
  33. data/lib/slack/block_kit/element.rb +41 -0
  34. data/lib/slack/block_kit/element/button_element.rb +48 -0
  35. data/lib/slack/block_kit/element/channels_select_element.rb +31 -0
  36. data/lib/slack/block_kit/element/conversations_select_element.rb +43 -0
  37. data/lib/slack/block_kit/element/date_picker_element.rb +56 -0
  38. data/lib/slack/block_kit/element/external_select_element.rb +38 -0
  39. data/lib/slack/block_kit/element/image_element.rb +26 -0
  40. data/lib/slack/block_kit/element/multi_channels_select_element.rb +38 -0
  41. data/lib/slack/block_kit/element/multi_conversations_select_element.rb +38 -0
  42. data/lib/slack/block_kit/element/multi_external_select_element.rb +38 -0
  43. data/lib/slack/block_kit/element/multi_static_select_element.rb +38 -0
  44. data/lib/slack/block_kit/element/multi_users_select_element.rb +39 -0
  45. data/lib/slack/block_kit/element/overflow_element.rb +42 -0
  46. data/lib/slack/block_kit/element/select_element.rb +45 -0
  47. data/lib/slack/block_kit/element/static_select_element.rb +46 -0
  48. data/lib/slack/block_kit/element/users_select_element.rb +24 -0
  49. data/lib/slack/block_kit/execution_context.rb +64 -0
  50. data/lib/slack/block_kit/refinements/hash_compact.rb +15 -0
  51. data/lib/slack/block_kit/type_restricted_array.rb +21 -0
  52. data/lib/slack/block_kit/version.rb +5 -0
  53. data/slack-block-kit.gemspec +29 -0
  54. metadata +166 -0
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module CompositionObjects
6
+ class Filter
7
+ using Refinements::HashCompact
8
+ attr_reader :include, :exclude_external_shared_channels, :exclude_bot_users
9
+
10
+ def self.[](hash)
11
+ new.tap do |object|
12
+ hash[:include].each(&object.include.method(:<<))
13
+ object.exclude_external_shared_channels! if hash[:exclude_external_shared_channels]
14
+ object.exclude_bot_users! if hash[:exclude_bot_users]
15
+ end
16
+ end
17
+
18
+ def initialize
19
+ @include = TypeRestrictedArray.new(String)
20
+ end
21
+
22
+ def exclude_external_shared_channels!
23
+ @exclude_external_shared_channels = true
24
+ end
25
+
26
+ def exclude_bot_users!
27
+ @exclude_bot_users = true
28
+ end
29
+
30
+ def to_h
31
+ { include: self.include,
32
+ exclude_external_shared_channels: exclude_external_shared_channels,
33
+ exclude_bot_users: exclude_bot_users }.compact
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module CompositionObjects
6
+ class Option
7
+ using Refinements::HashCompact
8
+ attr_reader :text, :value, :description, :url
9
+
10
+ def self.[](hash)
11
+ new.tap do |object|
12
+ object.text = hash.fetch(:text)
13
+ object.value = hash.fetch(:value)
14
+ object.description = hash[:description] if hash.key?(:description)
15
+ object.url = hash[:url] if hash.key?(:url)
16
+ end
17
+ end
18
+
19
+ def text=(obj)
20
+ raise TypeError, 'text must be a Text Object' unless obj.is_a?(Text)
21
+ raise RangeError, 'text is max 75 characters' unless obj.text.size <= 75
22
+
23
+ @text = obj
24
+ end
25
+
26
+ def value=(obj)
27
+ raise TypeError, 'value must be a string' unless obj.respond_to?(:to_str)
28
+ raise RangeError, 'value is max 75 characters' unless obj.size <= 75
29
+
30
+ @value = obj.to_s
31
+ end
32
+
33
+ def description=(obj)
34
+ raise TypeError, 'description must be a Text Object' unless obj.is_a?(Text)
35
+ raise TypeError, 'description must be plain_text' unless obj.type == :plain_text
36
+ raise RangeError, 'description is max 75 characters' unless obj.text.size <= 75
37
+
38
+ @description = obj
39
+ end
40
+
41
+ def url=(obj)
42
+ raise TypeError, 'url must be a string' unless url.respond_to?(:to_str)
43
+ raise RangeError, 'url is max 3000 characters' unless url.size <= 3000
44
+
45
+ @url = url.to_s
46
+ end
47
+
48
+ def to_h
49
+ { text: text.to_h,
50
+ value: value,
51
+ description: description&.to_h,
52
+ url: url }.compact
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module CompositionObjects
6
+ class OptionGroup
7
+ attr_reader :label, :options
8
+
9
+ def self.[](hash)
10
+ new.tap do |object|
11
+ object.label = hash.fetch(:label)
12
+ hash[:options].each(&object.options.method(:<<))
13
+ end
14
+ end
15
+
16
+ def initialize
17
+ @options = TypeRestrictedArray.new(Option)
18
+ end
19
+
20
+ def label=(obj)
21
+ raise TypeError, 'label must be a Text Object' unless obj.is_a?(Text)
22
+ raise RangeError, 'label is max 75 characters' unless obj.text.size <= 75
23
+
24
+ @label = obj
25
+ end
26
+
27
+ def to_h
28
+ { label: label.to_h,
29
+ options: options.map(&:to_h) }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ module CompositionObjects
6
+ class Text
7
+ using Refinements::HashCompact
8
+ attr_reader :type, :text, :emoji, :verbatim
9
+
10
+ PLAINTEXT = :plain_text
11
+ MRKDWN = :mrkdwn
12
+ NEWLINE = "\n"
13
+
14
+ def self.[](hash)
15
+ new.tap do |object|
16
+ object.type = hash.keys.find { |key| [PLAINTEXT, MRKDWN].include?(key) }
17
+ raise ArgumentError, 'type must be `plain_text` or `mrkdwn`' unless object.type
18
+
19
+ object.text = hash[object.type]
20
+ object.emoji! if hash[:emoji]
21
+ object.verbatim! if hash[:verbatim]
22
+ end
23
+ end
24
+
25
+ def empty?
26
+ text&.empty?
27
+ end
28
+
29
+ def emoji!
30
+ @emoji = true
31
+ end
32
+
33
+ def verbatim!
34
+ @verbatim = true
35
+ end
36
+
37
+ def type=(type)
38
+ unless [PLAINTEXT, MRKDWN].include?(type.to_sym)
39
+ raise ArgumentError, 'type must be `plain_text` or `mrkdwn`'
40
+ end
41
+
42
+ @type = type.to_sym
43
+ end
44
+
45
+ def text=(text)
46
+ text = text.join(NEWLINE) if text.is_a?(Array)
47
+ raise TypeError, 'text must be a string' unless text.respond_to?(:to_str)
48
+
49
+ @text = text.to_s
50
+ end
51
+
52
+ def to_h
53
+ { type: type,
54
+ text: text,
55
+ emoji: emoji,
56
+ verbatim: verbatim }.compact
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ class Element
6
+ attr_reader :action_id
7
+
8
+ def self.populate(hash, object)
9
+ object.action_id = hash[:action_id] if hash.key?(:action_id)
10
+ raise ArgumentError, "invalid #{name}" unless object.valid?
11
+ end
12
+
13
+ def self.[](hash)
14
+ new.tap { |obj| populate(hash, obj) }
15
+ end
16
+
17
+ def action_id=(obj)
18
+ raise TypeError, 'action_id must be a string' unless action_id.respond_to?(:to_str)
19
+ raise RangeError, 'action_id must be max 255 characters' unless action_id.size <= 255
20
+
21
+ @action_id = obj.to_s
22
+ end
23
+
24
+ def type
25
+ @type ||= self.class.name
26
+ .split('::')
27
+ .last.chomp('Element')
28
+ .gsub(/([a-z])([A-Z])/, '\1_\2').downcase
29
+ end
30
+
31
+ def valid?
32
+ true
33
+ end
34
+
35
+ def to_h
36
+ { type: type,
37
+ action_id: action_id }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ class Element
6
+ class ButtonElement < Element
7
+ using Refinements::HashCompact
8
+ attr_reader :text, :style
9
+ attr_accessor :value
10
+
11
+ def self.populate(hash, object)
12
+ object.text = hash.fetch(:text)
13
+ object.action_id = hash.fetch(:action_id) if hash[:action_id]
14
+ object.style = hash.fetch(:style) if hash[:style]
15
+ object.value = hash.fetch(:value) if hash[:value]
16
+
17
+ super(hash, object)
18
+ end
19
+
20
+ def valid?
21
+ !@text.empty?
22
+ end
23
+
24
+ def text=(obj)
25
+ raise TypeError, 'text must be a Text Object' unless obj.is_a?(CompositionObjects::Text)
26
+
27
+ @text = obj
28
+ end
29
+
30
+ def style=(obj)
31
+ unless %i( default primary danger ).include?(obj.to_sym)
32
+ raise ArgumentError, 'style may only be default, primary, or danger'
33
+ end
34
+
35
+ @style = obj.to_sym
36
+ end
37
+
38
+ def to_h
39
+ super.merge(
40
+ text: text.to_h,
41
+ style: style,
42
+ value: value
43
+ ).compact
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ class Element
6
+ class ChannelsSelectElement < SelectElement
7
+ using Refinements::HashCompact
8
+ attr_reader :initial_channel, :response_url_enabled
9
+ attr_writer :initial_channel
10
+
11
+ def self.populate(hash, object)
12
+ object.initial_channel = hash[:initial_channel] if hash.key?(:initial_channel)
13
+ object.response_url_enabled! if hash.key?(:response_url_enabled)
14
+
15
+ super(hash, object)
16
+ end
17
+
18
+ def response_url_enabled!
19
+ @response_url_enabled = true
20
+ end
21
+
22
+ def to_h
23
+ super.merge(
24
+ initial_channel: initial_channel,
25
+ response_url_enabled: response_url_enabled
26
+ ).compact
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ class Element
6
+ class ConversationsSelectElement < SelectElement
7
+ using Refinements::HashCompact
8
+ attr_reader :initial_conversation, :response_url_enabled, :filter
9
+ attr_writer :initial_conversation
10
+
11
+ def self.populate(hash, object)
12
+ if hash.key?(:initial_conversation)
13
+ object.initial_conversation = hash[:initial_conversation]
14
+ end
15
+ object.response_url_enabled! if hash.key?(:response_url_enabled)
16
+ object.filter = hash[:filter] if hash.key?(:filter)
17
+
18
+ super(hash, object)
19
+ end
20
+
21
+ def response_url_enabled!
22
+ @response_url_enabled = true
23
+ end
24
+
25
+ def filter=(obj)
26
+ unless obj.is_a?(CompositionObjects::Filter)
27
+ raise TypeError, 'confirm must be a Filter Object'
28
+ end
29
+
30
+ @filter = obj
31
+ end
32
+
33
+ def to_h
34
+ super.merge(
35
+ initial_conversation: initial_conversation,
36
+ response_url_enabled: response_url_enabled,
37
+ filter: filter&.to_h
38
+ ).compact
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ class Element
6
+ class DatePickerElement < Element
7
+ using Refinements::HashCompact
8
+ attr_reader :initial_date, :placeholder, :confirm
9
+
10
+ def self.populate(hash, object)
11
+ object.placeholder = hash.fetch(:placeholder) if hash[:placeholder]
12
+ object.confirm = hash.fetch(:confirm) if hash[:confirm]
13
+ object.initial_date = hash.fetch(:initial_date) if hash[:initial_date]
14
+
15
+ super(hash, object)
16
+ end
17
+
18
+ def type
19
+ 'datepicker'
20
+ end
21
+
22
+ def placeholder=(obj)
23
+ raise TypeError, 'placeholder must be a Text Object' unless obj.is_a?(CompositionObjects::Text)
24
+ raise TypeError, 'placeholder must be plain_text' unless obj.type == :plain_text
25
+ raise RangeError, 'placeholder is max 150 characters' unless obj.text.size <= 150
26
+
27
+ @placeholder = obj
28
+ end
29
+
30
+ def confirm=(obj)
31
+ unless obj.is_a?(CompositionObjects::ConfirmationDialog)
32
+ raise TypeError, 'confirm must be a ConfirmationDialog Object'
33
+ end
34
+
35
+ @confirm = obj
36
+ end
37
+
38
+ def initial_date=(date)
39
+ date = date.strftime('%Y-%m-%d') if date.is_a?(Time)
40
+ raise TypeError, 'initial_date must be a string' unless date.respond_to?(:to_str)
41
+ raise TypeError, 'initial_date must be YYYY-MM-DD' unless date.match?(/\d{4}-\d{2}-\d{2}/)
42
+
43
+ @initial_date = date.to_s
44
+ end
45
+
46
+ def to_h
47
+ super.merge(
48
+ initial_date: initial_date,
49
+ placeholder: placeholder&.to_h,
50
+ confirm: confirm&.to_h
51
+ ).compact
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slack
4
+ module BlockKit
5
+ class Element
6
+ class ExternalSelectElement < SelectElement
7
+ using Refinements::HashCompact
8
+ attr_reader :initial_option, :min_query_length
9
+
10
+ def self.populate(hash, object)
11
+ object.initial_option = hash[:initial_option] if hash.key?(:initial_option)
12
+ object.min_query_length = hash[:min_query_length] if hash.key?(:min_query_length)
13
+
14
+ super(hash, object)
15
+ end
16
+
17
+ def initial_option=(obj)
18
+ raise TypeError, 'initial_option must be a Option Object' unless obj.is_a?(CompositionObjects::Option)
19
+
20
+ @initial_option = obj
21
+ end
22
+
23
+ def min_query_length=(length)
24
+ raise TypeError, 'min_query_length must be an integer' unless length.respond_to?(:to_int)
25
+
26
+ @min_query_length = length.to_i
27
+ end
28
+
29
+ def to_h
30
+ super.merge(
31
+ initial_option: initial_option&.to_h,
32
+ min_query_length: min_query_length || 3
33
+ ).compact
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end