cksh_commander 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 921acc643549e603aa612742d7fd73c4c6e49389
4
+ data.tar.gz: 64670007c67675f60e1cbd7359002df75d8c8c61
5
+ SHA512:
6
+ metadata.gz: 06cb6b5a144fb04bf23a189f92f7d40b88f2ab79c1d568d3927138e0e704fc07eda38030ff16eadb034c4e65bcff6e6028d8da36e5fc410253571c184af07045
7
+ data.tar.gz: 7a09443da85a65eae2f75ffa9008bb02cdd36fb39d7050af90892c50e58afd010ee5bfc36113e8df40778dedbb90fa0c85862b2dc140edd66fea63c8851498b8
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cksh_commander.gemspec
4
+ gemspec
@@ -0,0 +1,122 @@
1
+ ## :stars: Sla[CK] Sla[SH] Commander
2
+
3
+ [![License](https://img.shields.io/packagist/l/doctrine/orm.svg)]()
4
+
5
+ CKSHCommander is a Ruby gem that simplifies the task of processing [Slack slash
6
+ commands](https://api.slack.com/slash-commands). It provides a class-based
7
+ convention for authoring command handlers, and it supports basic subcommands and
8
+ arguments.
9
+
10
+ ## Installation
11
+
12
+ You can install `cksh_commander` via RubyGems.
13
+
14
+ ```ruby
15
+ gem 'cksh_commander'
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Defining a custom command is easy. First, create a `commands/` directory and tell
21
+ `CKSHCommander` where it can find your commands. Let's say we've created a `commands/`
22
+ directory at the root of our project. Our configuration is as follows:
23
+
24
+ ```ruby
25
+ # app.rb
26
+
27
+ require 'cksh_commander'
28
+
29
+ CKSHCommander.configure do |c|
30
+ c.commands_path = File.expand_path("../commands", __FILE__)
31
+ end
32
+ ```
33
+
34
+ Now, let's assume that we've set up an `/example` Slack command. We want to create
35
+ a directory-based container for everything that our command might interact
36
+ with. (For example, we may wish to log some command-specific data in a text file.)
37
+ All that we have to do is to create a directory under our `commands/` directory
38
+ that matches the name of the slash command. In this case:
39
+
40
+ ```
41
+ root/
42
+ app.rb
43
+ commands/
44
+ example/
45
+ command.rb
46
+ ...
47
+ ```
48
+
49
+ The last step is to create our custom command class. We name this class `Command` to
50
+ abide by the convention. We define our class as follows. Note that the class must inherit from
51
+ `CKSHCommander::Command`, and it should reside within a module that shares
52
+ its name with the command.
53
+
54
+ ```ruby
55
+ # commands/example/command.rb
56
+
57
+ require "cksh_commander"
58
+
59
+ module Example
60
+ class Command < CKSHCommander::Command
61
+ set token: "gIkuvaNzQIHg97ATvDxqgjtO"
62
+
63
+ # Subcommand with no arguments
64
+ # SLACK: /example test0
65
+ def test0
66
+ set_response_text("Subcommand: test0")
67
+ end
68
+
69
+ # Subcommand with one argument
70
+ # SLACK: /example test1 text
71
+ def test1(text)
72
+ set_response_text("Subcommand: test1; Text: #{text}")
73
+ end
74
+
75
+ # No subcommand with one argument
76
+ # SLACK: /example text
77
+ def ___(text)
78
+ set_response_text("Root command; Text: #{text}")
79
+ end
80
+ end
81
+ end
82
+ ```
83
+
84
+ We set our slash command authentication token at the class level, and define
85
+ methods for processing subcommands. Attachments (which take the form of a hash)
86
+ can be added using `add_response_attachment(attachment)`. You can also set the
87
+ response to `'in_channel'` at the method level with `respond_in_channel!`. See
88
+ `CKSHCommander::Command` for the full implementation.
89
+
90
+ To run a command, we use `CKSHCommander::Runner`. In the example below, we've
91
+ created a simple Sinatra app to illustrate its usage with the standard Slack
92
+ slash command payload.
93
+
94
+ ```ruby
95
+ # app.rb
96
+
97
+ require "sinatra"
98
+ require "json"
99
+ require "cksh_commander"
100
+
101
+ CKSHCommander.configure do |c|
102
+ c.commands_path = File.expand_path("../commands", __FILE__)
103
+ end
104
+
105
+ post "/" do
106
+ content_type :json
107
+
108
+ command = params["command"][1..-1]
109
+ response = CKSHCommander::Runner.run(command, params)
110
+ JSON.dump(response.serialize)
111
+ end
112
+ ```
113
+
114
+ ## Development
115
+
116
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
117
+
118
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
119
+
120
+ ## Contributing
121
+
122
+ Bug reports and pull requests are welcome on GitHub at https://github.com/openarcllc/cksh_commander.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cksh_commander"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cksh_commander/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cksh_commander"
8
+ spec.version = CKSHCommander::VERSION
9
+ spec.authors = ["Travis Loncar"]
10
+ spec.email = ["travis@openarc.net"]
11
+ spec.license = 'MIT'
12
+
13
+ spec.summary = %q{Process Slack slash commands with ease.}
14
+ spec.description = %q{Process Slack slash commands with ease.}
15
+ spec.homepage = "https://github.com/openarcllc/cksh_commander"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,31 @@
1
+ require "cksh_commander/command"
2
+ require "cksh_commander/data"
3
+ require "cksh_commander/response"
4
+ require "cksh_commander/runner"
5
+ require "cksh_commander/version"
6
+
7
+ module CKSHCommander
8
+ class << self
9
+ attr_accessor :config
10
+
11
+ def config
12
+ @config ||= Configuration.new
13
+ end
14
+
15
+ def configure
16
+ yield config
17
+ end
18
+ end
19
+
20
+ class Configuration
21
+ attr_accessor :commands_path
22
+
23
+ def initialize
24
+ @commands_path = nil
25
+ end
26
+
27
+ def commands_path
28
+ @commands_path || "../commands"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,91 @@
1
+ require "cksh_commander/response"
2
+
3
+ module CKSHCommander
4
+ class Command
5
+ class << self
6
+ attr_accessor :token
7
+
8
+ def set(opts)
9
+ opts.each do |k,v|
10
+ send("#{k}=", v) if respond_to?(k)
11
+ end
12
+ end
13
+ end
14
+
15
+ attr_accessor :data
16
+
17
+ def initialize(data = nil)
18
+ @data = data
19
+ @response = Response.new
20
+ end
21
+
22
+ def run
23
+ # Find subcommands that match the provided text.
24
+ underscored_text = @data.text.gsub(/\s/, '_')
25
+ matched_subcommands = subcommand_methods.select { |command|
26
+ underscored_text.match(Regexp.new("\\A#{command}"))
27
+ }
28
+
29
+ # Add the "missing" subcommand if our command object
30
+ # responds to it and we have no matched subcommands.
31
+ matched_subcommands << "___" if respond_to?("___") && matched_subcommands.empty?
32
+
33
+ if matched_subcommands.empty?
34
+ return Response.new("Cannot find the provided subcommand!")
35
+ end
36
+
37
+ # Use the longest matching subcommand. Fetch the subcommand
38
+ # method and its arity. Represent the original subcommand string.
39
+ subcommand = matched_subcommands.max_by(&:size).to_s
40
+ subcommand_m = method(subcommand)
41
+ subcommand_a = subcommand_m.arity
42
+ subcommand_o = subcommand.gsub(/_/, ' ')
43
+
44
+ # Get argument string and slurp up arguments using arity. We
45
+ # assume that only trailing arguments can be space-delimited.
46
+ argstr, args = @data.text.gsub(subcommand_o, '').strip, []
47
+ subcommand_a.times do |i|
48
+ if i + 1 == subcommand_a
49
+ args << argstr
50
+ else
51
+ arg = argstr.split(' ')[0]
52
+ argstr.gsub!(Regexp.new("\\A#{arg} "), '')
53
+ args << arg
54
+ end
55
+ end
56
+
57
+ if args.size != subcommand_a
58
+ return Response.new("Wrong number of arguments (#{args.size} for #{subcommand_a})")
59
+ end
60
+
61
+ send(subcommand, *args)
62
+ @response
63
+ end
64
+
65
+ def authenticated?
66
+ self.class.token && self.class.token == data.token
67
+ end
68
+
69
+ def set_response_text(text)
70
+ @response.text = text
71
+ end
72
+
73
+ def add_response_attachment(attachment)
74
+ unless attachment.is_a?(Hash)
75
+ raise ArgumentError, "Attachment must be a Hash"
76
+ end
77
+
78
+ @response.attachments << attachment
79
+ end
80
+
81
+ def respond_in_channel!
82
+ @response.type = 'in_channel'
83
+ end
84
+
85
+ private
86
+
87
+ def subcommand_methods
88
+ methods - CKSHCommander::Command.new.methods
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,27 @@
1
+ module CKSHCommander
2
+ class Data
3
+ def initialize(params)
4
+ command_args.each do |arg|
5
+ instance_variable_set("@#{arg}", params.fetch(arg))
6
+ self.class.send(:attr_reader, arg)
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ def command_args
13
+ %w[
14
+ token
15
+ team_id
16
+ team_domain
17
+ channel_id
18
+ channel_name
19
+ user_id
20
+ user_name
21
+ command
22
+ text
23
+ response_url
24
+ ]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ module CKSHCommander
2
+ class Response
3
+ attr_accessor :text, :type, :attachments
4
+
5
+ def initialize(text = "")
6
+ @text = text
7
+ @type = ""
8
+ @attachments = []
9
+ end
10
+
11
+ def serialize
12
+ {
13
+ text: @text,
14
+ response_type: @type,
15
+ attachments: @attachments
16
+ }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ require "cksh_commander/data"
2
+ require "cksh_commander/response"
3
+
4
+ module CKSHCommander
5
+ class Runner
6
+ def self.run(command, params)
7
+ require_constants!
8
+ data = CKSHCommander::Data.new(params)
9
+
10
+ begin
11
+ name = "#{command.capitalize}::Command"
12
+ cmd = Kernel.const_get(name).new(data)
13
+
14
+ response = cmd.authenticated? ? cmd.run : Response.new("Invalid token!")
15
+ rescue NameError
16
+ response = Response.new("Command not found...")
17
+ end
18
+
19
+ response
20
+ end
21
+
22
+ private_class_method def self.require_constants!
23
+ commands_path = CKSHCommander.config.commands_path
24
+ Dir["#{commands_path}/**/*.rb"].each {|file| require file }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module CKSHCommander
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cksh_commander
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Travis Loncar
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-12-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Process Slack slash commands with ease.
56
+ email:
57
+ - travis@openarc.net
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - cksh_commander.gemspec
71
+ - lib/cksh_commander.rb
72
+ - lib/cksh_commander/command.rb
73
+ - lib/cksh_commander/data.rb
74
+ - lib/cksh_commander/response.rb
75
+ - lib/cksh_commander/runner.rb
76
+ - lib/cksh_commander/version.rb
77
+ homepage: https://github.com/openarcllc/cksh_commander
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.4.5
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Process Slack slash commands with ease.
101
+ test_files: []