block-kit 1.0.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 +7 -0
- data/.rspec +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +183 -0
- data/Rakefile +8 -0
- data/lib/block_kit/base.rb +190 -0
- data/lib/block_kit/blocks.rb +50 -0
- data/lib/block_kit/composition/confirmation_dialog.rb +48 -0
- data/lib/block_kit/composition/conversation_filter.rb +42 -0
- data/lib/block_kit/composition/dispatch_action_config.rb +33 -0
- data/lib/block_kit/composition/input_parameter.rb +19 -0
- data/lib/block_kit/composition/mrkdwn.rb +15 -0
- data/lib/block_kit/composition/option.rb +41 -0
- data/lib/block_kit/composition/option_group.rb +27 -0
- data/lib/block_kit/composition/overflow_option.rb +21 -0
- data/lib/block_kit/composition/plain_text.rb +15 -0
- data/lib/block_kit/composition/slack_file.rb +36 -0
- data/lib/block_kit/composition/text.rb +29 -0
- data/lib/block_kit/composition/trigger.rb +33 -0
- data/lib/block_kit/composition/workflow.rb +19 -0
- data/lib/block_kit/composition.rb +19 -0
- data/lib/block_kit/concerns/confirmable.rb +21 -0
- data/lib/block_kit/concerns/conversation_selection.rb +26 -0
- data/lib/block_kit/concerns/dispatchable.rb +24 -0
- data/lib/block_kit/concerns/dsl_generation.rb +76 -0
- data/lib/block_kit/concerns/external.rb +19 -0
- data/lib/block_kit/concerns/focusable_on_load.rb +17 -0
- data/lib/block_kit/concerns/has_initial_option.rb +23 -0
- data/lib/block_kit/concerns/has_initial_options.rb +23 -0
- data/lib/block_kit/concerns/has_option_groups.rb +37 -0
- data/lib/block_kit/concerns/has_options.rb +28 -0
- data/lib/block_kit/concerns/has_placeholder.rb +21 -0
- data/lib/block_kit/concerns/has_rich_text_elements.rb +83 -0
- data/lib/block_kit/concerns/plain_text_emoji_assignment.rb +39 -0
- data/lib/block_kit/concerns.rb +18 -0
- data/lib/block_kit/elements/base.rb +23 -0
- data/lib/block_kit/elements/base_button.rb +45 -0
- data/lib/block_kit/elements/button.rb +27 -0
- data/lib/block_kit/elements/channels_select.rb +22 -0
- data/lib/block_kit/elements/checkboxes.rb +14 -0
- data/lib/block_kit/elements/conversations_select.rb +24 -0
- data/lib/block_kit/elements/date_picker.rb +21 -0
- data/lib/block_kit/elements/datetime_picker.rb +21 -0
- data/lib/block_kit/elements/email_text_input.rb +24 -0
- data/lib/block_kit/elements/external_select.rb +21 -0
- data/lib/block_kit/elements/file_input.rb +33 -0
- data/lib/block_kit/elements/image.rb +53 -0
- data/lib/block_kit/elements/multi_channels_select.rb +31 -0
- data/lib/block_kit/elements/multi_conversations_select.rb +33 -0
- data/lib/block_kit/elements/multi_external_select.rb +21 -0
- data/lib/block_kit/elements/multi_select.rb +21 -0
- data/lib/block_kit/elements/multi_static_select.rb +12 -0
- data/lib/block_kit/elements/multi_users_select.rb +31 -0
- data/lib/block_kit/elements/number_input.rb +52 -0
- data/lib/block_kit/elements/overflow.rb +23 -0
- data/lib/block_kit/elements/plain_text_input.rb +66 -0
- data/lib/block_kit/elements/radio_buttons.rb +14 -0
- data/lib/block_kit/elements/rich_text_input.rb +26 -0
- data/lib/block_kit/elements/select.rb +18 -0
- data/lib/block_kit/elements/static_select.rb +12 -0
- data/lib/block_kit/elements/time_picker.rb +43 -0
- data/lib/block_kit/elements/url_text_input.rb +24 -0
- data/lib/block_kit/elements/users_select.rb +17 -0
- data/lib/block_kit/elements/workflow_button.rb +18 -0
- data/lib/block_kit/elements.rb +36 -0
- data/lib/block_kit/fixers/associated.rb +27 -0
- data/lib/block_kit/fixers/base.rb +30 -0
- data/lib/block_kit/fixers/null_value.rb +42 -0
- data/lib/block_kit/fixers/truncate.rb +35 -0
- data/lib/block_kit/fixers.rb +11 -0
- data/lib/block_kit/layout/actions.rb +84 -0
- data/lib/block_kit/layout/base.rb +23 -0
- data/lib/block_kit/layout/context.rb +48 -0
- data/lib/block_kit/layout/divider.rb +9 -0
- data/lib/block_kit/layout/file.rb +19 -0
- data/lib/block_kit/layout/header.rb +22 -0
- data/lib/block_kit/layout/image.rb +61 -0
- data/lib/block_kit/layout/input.rb +119 -0
- data/lib/block_kit/layout/markdown.rb +19 -0
- data/lib/block_kit/layout/rich_text/elements/broadcast.rb +22 -0
- data/lib/block_kit/layout/rich_text/elements/channel.rb +21 -0
- data/lib/block_kit/layout/rich_text/elements/color.rb +20 -0
- data/lib/block_kit/layout/rich_text/elements/date.rb +34 -0
- data/lib/block_kit/layout/rich_text/elements/emoji.rb +27 -0
- data/lib/block_kit/layout/rich_text/elements/link.rb +28 -0
- data/lib/block_kit/layout/rich_text/elements/mention_style.rb +27 -0
- data/lib/block_kit/layout/rich_text/elements/text.rb +23 -0
- data/lib/block_kit/layout/rich_text/elements/text_style.rb +23 -0
- data/lib/block_kit/layout/rich_text/elements/user.rb +21 -0
- data/lib/block_kit/layout/rich_text/elements/usergroup.rb +21 -0
- data/lib/block_kit/layout/rich_text/elements.rb +35 -0
- data/lib/block_kit/layout/rich_text/list.rb +41 -0
- data/lib/block_kit/layout/rich_text/preformatted.rb +19 -0
- data/lib/block_kit/layout/rich_text/quote.rb +19 -0
- data/lib/block_kit/layout/rich_text/section.rb +11 -0
- data/lib/block_kit/layout/rich_text.rb +53 -0
- data/lib/block_kit/layout/section.rb +152 -0
- data/lib/block_kit/layout/video.rb +71 -0
- data/lib/block_kit/layout.rb +35 -0
- data/lib/block_kit/surfaces/base.rb +173 -0
- data/lib/block_kit/surfaces/home.rb +40 -0
- data/lib/block_kit/surfaces/message.rb +140 -0
- data/lib/block_kit/surfaces/modal.rb +74 -0
- data/lib/block_kit/surfaces.rb +11 -0
- data/lib/block_kit/typed_array.rb +114 -0
- data/lib/block_kit/typed_set.rb +78 -0
- data/lib/block_kit/types/array.rb +45 -0
- data/lib/block_kit/types/blocks.rb +37 -0
- data/lib/block_kit/types/generic.rb +45 -0
- data/lib/block_kit/types/option.rb +48 -0
- data/lib/block_kit/types/set.rb +35 -0
- data/lib/block_kit/types/text.rb +80 -0
- data/lib/block_kit/types.rb +17 -0
- data/lib/block_kit/validators/array_inclusion_validator.rb +55 -0
- data/lib/block_kit/validators/associated_validator.rb +61 -0
- data/lib/block_kit/validators.rb +8 -0
- data/lib/block_kit/version.rb +5 -0
- data/lib/block_kit.rb +38 -0
- metadata +190 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
class ConversationFilter < Base
|
6
|
+
self.type = :conversation_filter
|
7
|
+
|
8
|
+
VALID_INCLUDES = [
|
9
|
+
IM = "im",
|
10
|
+
MPIM = "mpim",
|
11
|
+
PUBLIC = "public",
|
12
|
+
PRIVATE = "private"
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
attribute :include, Types::Set.of(:string)
|
16
|
+
attribute :exclude_external_shared_channels, :boolean
|
17
|
+
attribute :exclude_bot_users, :boolean
|
18
|
+
|
19
|
+
validates :include, presence: true, "block_kit/validators/array_inclusion": {in: VALID_INCLUDES, message: "contains invalid values: %{rejected_values}"}, allow_nil: true
|
20
|
+
fixes :include, null_value: {error_types: [:blank, :inclusion]}
|
21
|
+
|
22
|
+
VALID_INCLUDES.each do |value|
|
23
|
+
define_method(:"include_#{value}?") do
|
24
|
+
!!include&.member?(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
define_method(:"include_#{value}!") do
|
28
|
+
self.include ||= []
|
29
|
+
self.include.add(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def as_json(*)
|
34
|
+
super().except(:type).merge(
|
35
|
+
include: include&.to_a,
|
36
|
+
exclude_external_shared_channels: exclude_external_shared_channels,
|
37
|
+
exclude_bot_users: exclude_bot_users
|
38
|
+
).compact
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
class DispatchActionConfig < Base
|
6
|
+
self.type = :dispatch_action_config
|
7
|
+
|
8
|
+
VALID_TRIGGERS = [
|
9
|
+
ON_ENTER_PRESSED = "on_enter_pressed",
|
10
|
+
ON_CHARACTER_ENTERED = "on_character_entered"
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
attribute :trigger_actions_on, Types::Set.of(:string)
|
14
|
+
validates :trigger_actions_on, presence: true, "block_kit/validators/array_inclusion": {in: VALID_TRIGGERS}
|
15
|
+
fixes :trigger_actions_on, null_value: {error_types: [:inclusion]}
|
16
|
+
|
17
|
+
VALID_TRIGGERS.each do |value|
|
18
|
+
define_method(:"trigger_actions_on_#{value}?") do
|
19
|
+
!!trigger_actions_on&.member?(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method(:"trigger_actions_on_#{value}!") do
|
23
|
+
self.trigger_actions_on ||= []
|
24
|
+
self.trigger_actions_on.add(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def as_json(*)
|
29
|
+
super().except(:type).merge(trigger_actions_on: trigger_actions_on&.to_a).compact
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
class InputParameter < Base
|
6
|
+
self.type = :input_parameter
|
7
|
+
|
8
|
+
attribute :name, :string
|
9
|
+
attribute :value, :string
|
10
|
+
|
11
|
+
validates :name, presence: true
|
12
|
+
validates :value, presence: true
|
13
|
+
|
14
|
+
def as_json(*)
|
15
|
+
super().except(:type).merge(name: name, value: value).compact
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
class Option < Base
|
6
|
+
self.type = :option
|
7
|
+
|
8
|
+
MAX_TEXT_LENGTH = 75
|
9
|
+
MAX_VALUE_LENGTH = 150
|
10
|
+
MAX_DESCRIPTION_LENGTH = 75
|
11
|
+
|
12
|
+
plain_text_attribute :text
|
13
|
+
attribute :value, :string
|
14
|
+
plain_text_attribute :description
|
15
|
+
attribute :initial, :boolean
|
16
|
+
|
17
|
+
include Concerns::PlainTextEmojiAssignment.new(:text, :description)
|
18
|
+
|
19
|
+
validates :text, presence: true, length: {maximum: MAX_TEXT_LENGTH}
|
20
|
+
fixes :text, truncate: {maximum: MAX_TEXT_LENGTH}
|
21
|
+
|
22
|
+
validates :value, presence: true, length: {maximum: MAX_VALUE_LENGTH}
|
23
|
+
fixes :value, truncate: {maximum: MAX_VALUE_LENGTH, dangerous: true}
|
24
|
+
|
25
|
+
validates :description, presence: true, length: {maximum: MAX_DESCRIPTION_LENGTH}, allow_nil: true
|
26
|
+
fixes :description, truncate: {maximum: MAX_DESCRIPTION_LENGTH}, null_value: [:blank]
|
27
|
+
|
28
|
+
def initial?
|
29
|
+
!!initial
|
30
|
+
end
|
31
|
+
|
32
|
+
def as_json(*)
|
33
|
+
super().except(:type).merge(
|
34
|
+
text: text&.as_json,
|
35
|
+
value: value,
|
36
|
+
description: description&.as_json
|
37
|
+
).compact
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
class OptionGroup < Base
|
6
|
+
self.type = :option_group
|
7
|
+
|
8
|
+
MAX_LABEL_LENGTH = 75
|
9
|
+
MAX_OPTIONS = 100
|
10
|
+
|
11
|
+
include Concerns::HasOptions.new(limit: MAX_OPTIONS)
|
12
|
+
|
13
|
+
plain_text_attribute :label
|
14
|
+
validates :label, presence: true, length: {maximum: MAX_LABEL_LENGTH}
|
15
|
+
fixes :label, truncate: {maximum: MAX_LABEL_LENGTH}
|
16
|
+
|
17
|
+
dsl_method :options, as: :option, required_fields: [:text, :value], yields: false
|
18
|
+
|
19
|
+
def as_json(*)
|
20
|
+
super().except(:type).merge(
|
21
|
+
label: label&.as_json,
|
22
|
+
options: options&.map(&:as_json)
|
23
|
+
).compact
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module BlockKit
|
6
|
+
module Composition
|
7
|
+
class OverflowOption < Option
|
8
|
+
self.type = :overflow_option
|
9
|
+
|
10
|
+
MAX_URL_LENGTH = 3000
|
11
|
+
|
12
|
+
attribute :url, :string
|
13
|
+
validates :url, presence: true, format: {with: URI::DEFAULT_PARSER.make_regexp, message: "is not a valid URI", allow_blank: true}, length: {maximum: MAX_URL_LENGTH}, allow_nil: true
|
14
|
+
fixes :url, truncate: {maximum: MAX_URL_LENGTH, dangerous: true, omission: ""}, null_value: [:blank]
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(url: url).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module BlockKit
|
6
|
+
module Composition
|
7
|
+
class SlackFile < Base
|
8
|
+
self.type = :slack_file
|
9
|
+
|
10
|
+
MAX_URL_LENGTH = 3000
|
11
|
+
|
12
|
+
attribute :id, :string
|
13
|
+
attribute :url, :string
|
14
|
+
|
15
|
+
validates :id, presence: true, format: {with: /\AF[A-Z0-9]{8,}\z/, allow_blank: true}, allow_nil: true
|
16
|
+
fixes :id, null_value: [:blank]
|
17
|
+
|
18
|
+
validates :url, presence: true, length: {maximum: MAX_URL_LENGTH}, format: {with: URI::DEFAULT_PARSER.make_regexp(%w[http https]), message: "is not a valid URI", allow_blank: true}, allow_nil: true
|
19
|
+
fixes :url, null_value: [:blank]
|
20
|
+
|
21
|
+
validate :id_or_url_present
|
22
|
+
|
23
|
+
def as_json(*)
|
24
|
+
super().except(:type).merge(id: id, url: url).compact
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def id_or_url_present
|
30
|
+
if (id.blank? && url.blank?) || (id.present? && url.present?)
|
31
|
+
errors.add(:base, "must have either an id or url, but not both")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
class Text < Base
|
6
|
+
MAX_LENGTH = 3000
|
7
|
+
|
8
|
+
attribute :text, :string
|
9
|
+
validates :text, presence: true, length: {maximum: MAX_LENGTH}
|
10
|
+
fixes :text, truncate: {maximum: MAX_LENGTH}
|
11
|
+
|
12
|
+
delegate :blank?, :present?, to: :text
|
13
|
+
|
14
|
+
def length
|
15
|
+
text&.length || 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def truncate(*)
|
19
|
+
dup.tap do |copy|
|
20
|
+
copy.text = text.truncate(*)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def as_json(*)
|
25
|
+
super.merge(text: text)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module BlockKit
|
6
|
+
module Composition
|
7
|
+
class Trigger < Base
|
8
|
+
self.type = :trigger
|
9
|
+
|
10
|
+
MAX_URL_LENGTH = 3000
|
11
|
+
|
12
|
+
attribute :url, :string
|
13
|
+
attribute :customizable_input_parameters, Types::Array.of(Composition::InputParameter)
|
14
|
+
|
15
|
+
validates :url, presence: true, length: {maximum: MAX_URL_LENGTH}, format: {with: URI::DEFAULT_PARSER.make_regexp(%w[http https]), message: "is not a valid URI", allow_blank: true}
|
16
|
+
validates :customizable_input_parameters, presence: true, "block_kit/validators/associated": true, allow_nil: true
|
17
|
+
fixes :customizable_input_parameters, null_value: [:blank]
|
18
|
+
|
19
|
+
dsl_method :customizable_input_parameters, as: :customizable_input_parameter, required_fields: [:name, :value], yields: false
|
20
|
+
|
21
|
+
alias_method :input_parameter, :customizable_input_parameter
|
22
|
+
alias_method :customizable_input_param, :customizable_input_parameter
|
23
|
+
alias_method :input_param, :customizable_input_parameter
|
24
|
+
|
25
|
+
def as_json(*)
|
26
|
+
super().except(:type).merge(
|
27
|
+
url: url,
|
28
|
+
customizable_input_parameters: customizable_input_parameters&.map(&:as_json)
|
29
|
+
).compact
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
class Workflow < Base
|
6
|
+
self.type = :workflow
|
7
|
+
|
8
|
+
attribute :trigger, Types::Generic.of_type(Composition::Trigger)
|
9
|
+
validates :trigger, presence: true, "block_kit/validators/associated": true
|
10
|
+
fixes :trigger, associated: true
|
11
|
+
|
12
|
+
dsl_method :trigger
|
13
|
+
|
14
|
+
def as_json(*)
|
15
|
+
super().except(:type).merge(trigger: trigger&.as_json).compact
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Composition
|
5
|
+
autoload :ConfirmationDialog, "block_kit/composition/confirmation_dialog"
|
6
|
+
autoload :ConversationFilter, "block_kit/composition/conversation_filter"
|
7
|
+
autoload :DispatchActionConfig, "block_kit/composition/dispatch_action_config"
|
8
|
+
autoload :InputParameter, "block_kit/composition/input_parameter"
|
9
|
+
autoload :Mrkdwn, "block_kit/composition/mrkdwn"
|
10
|
+
autoload :OptionGroup, "block_kit/composition/option_group"
|
11
|
+
autoload :Option, "block_kit/composition/option"
|
12
|
+
autoload :OverflowOption, "block_kit/composition/overflow_option"
|
13
|
+
autoload :PlainText, "block_kit/composition/plain_text"
|
14
|
+
autoload :SlackFile, "block_kit/composition/slack_file"
|
15
|
+
autoload :Text, "block_kit/composition/text"
|
16
|
+
autoload :Trigger, "block_kit/composition/trigger"
|
17
|
+
autoload :Workflow, "block_kit/composition/workflow"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module Confirmable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute :confirm, Types::Generic.of_type(Composition::ConfirmationDialog)
|
10
|
+
validates :confirm, "block_kit/validators/associated": true, allow_nil: true
|
11
|
+
fixes :confirm, associated: true
|
12
|
+
|
13
|
+
dsl_method :confirm, required_fields: [:title, :text, :confirm, :deny], yields: false
|
14
|
+
end
|
15
|
+
|
16
|
+
def as_json(*)
|
17
|
+
super.merge(confirm: confirm&.as_json).compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module ConversationSelection
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute :default_to_current_conversation, :boolean
|
10
|
+
attribute :filter, Types::Generic.of_type(Composition::ConversationFilter)
|
11
|
+
|
12
|
+
validates :filter, "block_kit/validators/associated": true, allow_nil: true
|
13
|
+
fixes :filter, associated: true
|
14
|
+
|
15
|
+
dsl_method :filter
|
16
|
+
end
|
17
|
+
|
18
|
+
def as_json(*)
|
19
|
+
super.merge(
|
20
|
+
default_to_current_conversation: default_to_current_conversation,
|
21
|
+
filter: filter&.as_json
|
22
|
+
).compact
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module Dispatchable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute :dispatch_action_config, Types::Generic.of_type(Composition::DispatchActionConfig)
|
10
|
+
|
11
|
+
validates :dispatch_action_config, presence: true, "block_kit/validators/associated": true, allow_nil: true
|
12
|
+
fixes :dispatch_action_config, associated: true
|
13
|
+
|
14
|
+
dsl_method :dispatch_action_config
|
15
|
+
alias_method :dispatch_action_configuration, :dispatch_action_config
|
16
|
+
alias_attribute :dispatch_action_configuration, :dispatch_action_config
|
17
|
+
end
|
18
|
+
|
19
|
+
def as_json(*)
|
20
|
+
super.merge(dispatch_action_config: dispatch_action_config&.as_json).compact
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module DSLGeneration
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
private
|
10
|
+
|
11
|
+
def dsl_method(attribute, as: nil, type: nil, required_fields: [], mutually_exclusive_fields: [], yields: true)
|
12
|
+
type ||= attribute_types[attribute.to_s]
|
13
|
+
type = Types::Generic.of_type(type) if type.is_a?(Class) && type < BlockKit::Base
|
14
|
+
raise ArgumentError, "attribute #{attribute} does not exist" if type.instance_of?(ActiveModel::Type::Value)
|
15
|
+
|
16
|
+
is_array_attribute = false
|
17
|
+
|
18
|
+
if type.instance_of?(Types::Array)
|
19
|
+
is_array_attribute = true
|
20
|
+
type = type.item_type
|
21
|
+
end
|
22
|
+
|
23
|
+
is_array_attribute ||= attribute_types[attribute.to_s].type == :array
|
24
|
+
|
25
|
+
fields = type.block_class.attribute_names.map(&:to_sym)
|
26
|
+
plain_text_fields = type.block_class.attribute_types.select { |_, v| v.respond_to?(:block_class) && v.block_class == Composition::PlainText }.keys.map(&:to_sym)
|
27
|
+
|
28
|
+
define_method(as || attribute) do |args = {}, &block|
|
29
|
+
args = args.symbolize_keys
|
30
|
+
|
31
|
+
# Return the existing attribute if no args or block are passed and we're not in a custom-named method
|
32
|
+
return super() if args.blank? && block.nil? && as.nil?
|
33
|
+
|
34
|
+
if (missing_required_fields = (required_fields - args.keys)) && missing_required_fields.any?
|
35
|
+
message = "missing keyword#{"s" if missing_required_fields.size > 1}: #{missing_required_fields.map(&:inspect).join(", ")}"
|
36
|
+
raise ArgumentError, message
|
37
|
+
end
|
38
|
+
|
39
|
+
unknown_fields = (args.keys - fields)
|
40
|
+
unknown_fields.delete(:emoji) if plain_text_fields.any?
|
41
|
+
|
42
|
+
if unknown_fields.any?
|
43
|
+
message = "unknown keyword#{"s" if unknown_fields.size > 1}: #{unknown_fields.map(&:inspect).join(", ")}"
|
44
|
+
raise ArgumentError, message
|
45
|
+
end
|
46
|
+
|
47
|
+
mutually_exclusive = mutually_exclusive_fields & args.keys
|
48
|
+
if mutually_exclusive.length > 1
|
49
|
+
message = "mutually exclusive keywords: #{mutually_exclusive.map(&:inspect).join(", ")}"
|
50
|
+
raise ArgumentError, message
|
51
|
+
end
|
52
|
+
|
53
|
+
new_value = if is_array_attribute
|
54
|
+
public_send(:"#{attribute}=", []) if public_send(attribute).nil?
|
55
|
+
public_send(attribute) << type.cast(args)
|
56
|
+
public_send(attribute).last
|
57
|
+
else
|
58
|
+
public_send(:"#{attribute}=", type.cast(args))
|
59
|
+
public_send(attribute)
|
60
|
+
end
|
61
|
+
|
62
|
+
plain_text_fields.each do |field|
|
63
|
+
next unless args.key?(field)
|
64
|
+
|
65
|
+
new_value.public_send(field).emoji = args[:emoji] if args.key?(:emoji)
|
66
|
+
end
|
67
|
+
|
68
|
+
block&.call(new_value) if yields
|
69
|
+
|
70
|
+
self
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module External
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute :min_query_length, :integer
|
10
|
+
validates :min_query_length, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: 0}, allow_nil: true
|
11
|
+
fixes :min_query_length, null_value: {error_types: [:greater_than_or_equal_to]}
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json(*)
|
15
|
+
super.merge(min_query_length: min_query_length).compact
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module FocusableOnLoad
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute :focus_on_load, :boolean
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json(*)
|
13
|
+
super.merge(focus_on_load: focus_on_load).compact
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module HasInitialOption
|
6
|
+
def initial_option
|
7
|
+
all_options&.reverse&.find(&:initial?)
|
8
|
+
end
|
9
|
+
|
10
|
+
def as_json(*)
|
11
|
+
super.merge(initial_option: initial_option&.as_json).compact
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def all_options
|
17
|
+
opts = Array(options)
|
18
|
+
opts += Array(option_groups).flat_map(&:options) if respond_to?(:option_groups)
|
19
|
+
opts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module HasInitialOptions
|
6
|
+
def initial_options
|
7
|
+
all_options&.select(&:initial?)
|
8
|
+
end
|
9
|
+
|
10
|
+
def as_json(*)
|
11
|
+
super.merge(initial_options: initial_options&.map(&:as_json).presence).compact
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def all_options
|
17
|
+
opts = Array(options)
|
18
|
+
opts += Array(option_groups).flat_map(&:options) if respond_to?(:option_groups)
|
19
|
+
opts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module HasOptionGroups
|
6
|
+
def self.new(limit:, options_limit:)
|
7
|
+
Module.new do
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
include HasOptions.new(limit: options_limit)
|
12
|
+
|
13
|
+
attribute :option_groups, Types::Array.of(Composition::OptionGroup)
|
14
|
+
validates :option_groups, length: {maximum: limit, message: "is too long (maximum is %{count} groups)"}, "block_kit/validators/associated": true
|
15
|
+
fixes :option_groups, truncate: {maximum: limit, dangerous: true}, associated: true
|
16
|
+
|
17
|
+
validate :options_or_option_groups
|
18
|
+
|
19
|
+
dsl_method :option_groups, as: :option_group, required_fields: [:label]
|
20
|
+
end
|
21
|
+
|
22
|
+
def as_json(*)
|
23
|
+
super.merge(option_groups: option_groups&.map(&:as_json)).compact
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def options_or_option_groups
|
29
|
+
if (options.blank? && option_groups.blank?) || (options.present? && option_groups.present?)
|
30
|
+
errors.add(:base, "must have either options or option groups, not both")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlockKit
|
4
|
+
module Concerns
|
5
|
+
module HasOptions
|
6
|
+
def self.new(limit:)
|
7
|
+
Module.new do
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
attribute :options, Types::Array.of(Composition::Option)
|
12
|
+
validates :options,
|
13
|
+
presence: {unless: ->(block) { block.respond_to?(:option_groups) }},
|
14
|
+
length: {maximum: limit, message: "is too long (maximum is %{count} options)"},
|
15
|
+
"block_kit/validators/associated": true
|
16
|
+
fixes :options, truncate: {maximum: limit, dangerous: true}, associated: true
|
17
|
+
|
18
|
+
dsl_method :options, as: :option, required_fields: [:text, :value], yields: false
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_json(*)
|
22
|
+
super.merge(options: options&.map(&:as_json))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|