lib/cogy.rb in cogy-0.0.3 vs lib/cogy.rb in cogy-0.1.0

- old
+ new

@@ -1,46 +1,106 @@ require "cogy/engine" -require "cogy/handler" require "cogy/command" +require "cogy/context" module Cogy + # The supported Cog bundle config version. + # + # @see http://docs.operable.io/docs/bundle-configs COG_BUNDLE_VERSION = 4 - # Holds all the registered Commands. Not to be messed with. - mattr_accessor :commands + # Holds all the registered {Command} objects. Not to be messed with. @@commands = {} + mattr_accessor :commands - # Bundle config-related stuff - mattr_accessor :bundle_name + # The Cog bundle name. + # + # Used by {Cogy.bundle_config}. @@bundle_name = "cogy" + mattr_accessor :bundle_name - mattr_accessor :bundle_description + # The Cog bundle description. + # + # Used by {Cogy.bundle_config}. @@bundle_description = "Cogy-generated commands" + mattr_accessor :bundle_description - # Can be either a string or an object that responds to `#call` and returns - # a string. + # The Cog bundle version. Can be either a string or an object that responds + # to `#call` and returns a string. Used by {Cogy.bundle_config}. # - # Must be set explicitly + # Used by {Cogy.bundle_config}. + # + # @example + # bundle_version = -> { rand(2).to_s } + @@bundle_version = "0.0.1" mattr_accessor :bundle_version - @@bundle_version = nil - # The path in the Cog Relay where the command executable is located. + # The path in the Cog Relay where the cogy executable + # (ie. https://github.com/skroutz/cogy-bundle/blob/master/commands/cogy) is + # located. # - # Must be set explicitly. + # Used by {Cogy.bundle_config}. + @@executable_path = "/usr/bin/cogy" mattr_accessor :executable_path - @@executable_path = nil - # Paths where the files that define the commands will be searched in - mattr_accessor :command_load_paths + # Paths where the files that define the commands will be searched in the + # host application. @@command_load_paths = ["cogy"] + mattr_accessor :command_load_paths - def self.on(cmd_name, opts = {}, &blk) - cmd = Command.new(cmd_name, opts) - handler = Handler.new(blk) - cmd.register!(handler) + # Registers a command to Cogy. All the options passed are used solely for + # generating the bundle config (ie. {Cogy.bundle_config}). The passed block + # is the code that will get executed when the command is invoked. + # + # The last value of the block is what will get printed as the result of the + # command. It should be a string. If you want to return early in a point + # inside the block, use `next` instead of `return`. + # + # Inside the command block, there are the public attributes of {Context} + # available. + # + # @param cmd_name [String, Symbol] the name of the command. This is how the + # command will be invoked in the chat. + # @param [Hash] opts the options to create the command with. All these options + # are used solely for generating the bundle config for Cog, thus they map + # directly to Cog's bundle config format. + # See https://cog-book.operable.io/#_the_config_file for more information. + # @option opts [Array<Symbol, String>, Symbol, String] :args ([]) + # @option opts [Hash{Symbol=>Hash}] :opts ({}) + # @option opts [String] :desc required + # @option opts [String] :long_desc (nil) + # @option opts [String] :examples (nil) + # @option opts [Array] :rules (["allow"]) + # + # @example + # Cogy.on "calc", + # args: [:a, :b], + # opts: { op: { description: "The operation to perform", type: "string" } }, + # desc: "Perform an arithmetic operation between two numbers", + # long_desc: "Operations supported are provided with their respective symbols + # passed as the --op option.", + # examples: "Addition: !calc --op + 1 2\n" \ + # "Subtraction: !calc --op - 5 3\n" \ + # "Multiplication: !calc --op * 2 5\n" \ + # "Division: !calc --op / 3 2\", + # rules: ["allow"] do + # result = args.map(&:to_i).inject(&opts["op"].to_sym) + # "Hello #{user}, the answer is: #{result}" + # end + # + # @return [void] + # + # @note If you want to return early in a point inside a command block, + # `next` should be used instead of `return`, due to the way Proc objects + # work in Ruby. + def self.on(cmd_name, opts = {}, &handler) + cmd = Command.new(cmd_name, handler, opts) + cmd.register! end + # Generates the bundle config + # # @return [Hash] def self.bundle_config version = if bundle_version.respond_to?(:call) bundle_version.call else @@ -50,13 +110,14 @@ config = { "cog_bundle_version" => COG_BUNDLE_VERSION, "name" => bundle_name, "description" => bundle_description, "version" => version, - "commands" => {} } + config["commands"] = {} if commands.present? + commands.each do |name, cmd| config["commands"][name] = { "executable" => executable_path, "description" => cmd.desc, "rules" => cmd.rules @@ -80,9 +141,39 @@ end config end + # Configures Cogy according to the passed block. + # + # @example + # Cogy.configure do |c| + # c.bundle_name = "foo" + # end + # + # @yield [self] yields {Cogy} + # @return [void] def self.configure yield self + end + + # Defines a user helper method that can be used throughout commands. + # + # @param [Symbol] name the name of the helper + # @param [Proc] blk the helper body + # + # @return [void] + # + # @note User helpers also have access to the default helpers like `user`, `env` + # etc. + # + # @example + # Cogy.configure do |c| + # helper(:user) { User.find_by(slack_handle: handle) } + # + # # a helper that accepts an argument + # helper(:format) { |answer| answer.titleize } + # end + def self.helper(name, &blk) + Context.class_eval { define_method(name, blk) } end end