2016-06-30 11:34:19 -04:00
|
|
|
module Gitlab
|
|
|
|
module SlashCommands
|
|
|
|
module Dsl
|
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
|
|
|
included do
|
2016-08-12 05:19:29 -04:00
|
|
|
cattr_accessor :definitions
|
2016-06-30 11:34:19 -04:00
|
|
|
end
|
|
|
|
|
2016-08-12 05:19:29 -04:00
|
|
|
def execute_command(name, *args)
|
|
|
|
name = name.to_sym
|
|
|
|
cmd_def = self.class.definitions.find do |cmd_def|
|
|
|
|
self.class.command_name_and_aliases(cmd_def).include?(name)
|
|
|
|
end
|
|
|
|
return unless cmd_def && cmd_def[:action_block]
|
|
|
|
return if self.class.command_unavailable?(cmd_def, self)
|
|
|
|
|
|
|
|
block_arity = cmd_def[:action_block].arity
|
|
|
|
if block_arity == -1 || block_arity == args.size
|
|
|
|
instance_exec(*args, &cmd_def[:action_block])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class_methods do
|
|
|
|
# This method is used to generate the autocompletion menu.
|
|
|
|
# It returns no-op slash commands (such as `/cc`).
|
2016-08-10 11:51:01 -04:00
|
|
|
def command_definitions(opts = {})
|
2016-08-12 05:19:29 -04:00
|
|
|
self.definitions.map do |cmd_def|
|
2016-08-11 12:51:37 -04:00
|
|
|
context = OpenStruct.new(opts)
|
2016-08-12 05:19:29 -04:00
|
|
|
next if command_unavailable?(cmd_def, context)
|
2016-08-10 11:51:01 -04:00
|
|
|
|
|
|
|
cmd_def = cmd_def.dup
|
2016-06-30 11:34:19 -04:00
|
|
|
|
2016-08-12 05:19:29 -04:00
|
|
|
if cmd_def[:description].respond_to?(:call)
|
2016-08-11 12:51:37 -04:00
|
|
|
cmd_def[:description] = context.instance_exec(&cmd_def[:description]) rescue ''
|
2016-08-10 08:12:09 -04:00
|
|
|
end
|
2016-08-10 11:51:01 -04:00
|
|
|
|
|
|
|
cmd_def
|
|
|
|
end.compact
|
|
|
|
end
|
|
|
|
|
2016-08-11 12:51:37 -04:00
|
|
|
# This method is used to generate a list of valid commands in the current
|
|
|
|
# context of `opts`.
|
|
|
|
# It excludes no-op slash commands (such as `/cc`).
|
|
|
|
# This list can then be given to `Gitlab::SlashCommands::Extractor`.
|
2016-08-10 11:51:01 -04:00
|
|
|
def command_names(opts = {})
|
2016-08-12 05:19:29 -04:00
|
|
|
self.definitions.flat_map do |cmd_def|
|
|
|
|
next if cmd_def[:opts].fetch(:noop, false)
|
2016-08-10 11:51:01 -04:00
|
|
|
|
2016-08-12 05:19:29 -04:00
|
|
|
context = OpenStruct.new(opts)
|
|
|
|
next if command_unavailable?(cmd_def, context)
|
|
|
|
|
|
|
|
command_name_and_aliases(cmd_def)
|
2016-08-10 08:12:09 -04:00
|
|
|
end.compact
|
2016-06-30 11:34:19 -04:00
|
|
|
end
|
|
|
|
|
2016-08-12 05:19:29 -04:00
|
|
|
def command_unavailable?(cmd_def, context)
|
|
|
|
cmd_def[:condition_block] && !context.instance_exec(&cmd_def[:condition_block])
|
|
|
|
end
|
|
|
|
|
|
|
|
def command_name_and_aliases(cmd_def)
|
|
|
|
[cmd_def[:name], *cmd_def[:aliases]]
|
|
|
|
end
|
|
|
|
|
2016-08-11 12:51:37 -04:00
|
|
|
# Allows to give a description to the next slash command.
|
|
|
|
# This description is shown in the autocomplete menu.
|
|
|
|
# It accepts a block that will be evaluated with the context given to
|
|
|
|
# `.command_definitions` or `.command_names`.
|
|
|
|
#
|
|
|
|
# Example:
|
|
|
|
#
|
|
|
|
# desc do
|
|
|
|
# "This is a dynamic description for #{noteable.to_ability_name}"
|
|
|
|
# end
|
|
|
|
# command :command_key do |arguments|
|
|
|
|
# # Awesome code block
|
|
|
|
# end
|
|
|
|
def desc(text = '', &block)
|
|
|
|
@description = block_given? ? block : text
|
2016-06-30 11:34:19 -04:00
|
|
|
end
|
|
|
|
|
2016-08-11 12:51:37 -04:00
|
|
|
# Allows to define params for the next slash command.
|
|
|
|
# These params are shown in the autocomplete menu.
|
|
|
|
#
|
|
|
|
# Example:
|
|
|
|
#
|
|
|
|
# params "~label ~label2"
|
|
|
|
# command :command_key do |arguments|
|
|
|
|
# # Awesome code block
|
|
|
|
# end
|
2016-06-30 11:34:19 -04:00
|
|
|
def params(*params)
|
|
|
|
@params = params
|
|
|
|
end
|
|
|
|
|
2016-08-11 12:51:37 -04:00
|
|
|
# Allows to define conditions that must be met in order for the command
|
|
|
|
# to be returned by `.command_names` & `.command_definitions`.
|
|
|
|
# It accepts a block that will be evaluated with the context given to
|
|
|
|
# `.command_definitions`, `.command_names`, and the actual command method.
|
|
|
|
#
|
2016-06-30 11:34:19 -04:00
|
|
|
# Example:
|
|
|
|
#
|
2016-08-11 12:51:37 -04:00
|
|
|
# condition do
|
|
|
|
# project.public?
|
|
|
|
# end
|
2016-06-30 11:34:19 -04:00
|
|
|
# command :command_key do |arguments|
|
|
|
|
# # Awesome code block
|
|
|
|
# end
|
2016-08-11 12:51:37 -04:00
|
|
|
def condition(&block)
|
2016-08-12 05:19:29 -04:00
|
|
|
@condition_block = block
|
2016-08-11 12:51:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
# Registers a new command which is recognizeable from body of email or
|
|
|
|
# comment.
|
|
|
|
# It accepts aliases and takes a block.
|
2016-06-30 11:34:19 -04:00
|
|
|
#
|
2016-08-11 12:51:37 -04:00
|
|
|
# Example:
|
|
|
|
#
|
|
|
|
# command :my_command, :alias_for_my_command do |arguments|
|
|
|
|
# # Awesome code block
|
|
|
|
# end
|
2016-06-30 11:34:19 -04:00
|
|
|
def command(*command_names, &block)
|
2016-08-11 12:51:37 -04:00
|
|
|
opts = command_names.extract_options!
|
2016-08-12 05:19:29 -04:00
|
|
|
name, *aliases = command_names
|
2016-06-30 11:34:19 -04:00
|
|
|
|
2016-08-12 05:19:29 -04:00
|
|
|
self.definitions ||= []
|
|
|
|
self.definitions << {
|
|
|
|
name: name,
|
2016-06-30 11:34:19 -04:00
|
|
|
aliases: aliases,
|
|
|
|
description: @description || '',
|
2016-08-12 05:19:29 -04:00
|
|
|
params: @params || [],
|
|
|
|
condition_block: @condition_block,
|
|
|
|
action_block: block,
|
|
|
|
opts: opts
|
2016-06-30 11:34:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@description = nil
|
|
|
|
@params = nil
|
2016-08-12 05:19:29 -04:00
|
|
|
@condition_block = nil
|
2016-06-30 11:34:19 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|