Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/skroutz/cogy
Cog commands from your Rails app
https://github.com/skroutz/cogy
chat chat-bot chatbot chatbots chatops cog rails
Last synced: 3 months ago
JSON representation
Cog commands from your Rails app
- Host: GitHub
- URL: https://github.com/skroutz/cogy
- Owner: skroutz
- License: mit
- Created: 2016-11-02T10:32:19.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2023-01-30T13:20:21.000Z (almost 2 years ago)
- Last Synced: 2024-07-20T15:23:04.595Z (4 months ago)
- Topics: chat, chat-bot, chatbot, chatbots, chatops, cog, rails
- Language: Ruby
- Homepage: https://github.com/operable/cog
- Size: 816 KB
- Stars: 21
- Watchers: 14
- Forks: 1
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Cogy
[![Build Status](https://api.travis-ci.org/skroutz/cogy.svg?branch=master)](https://travis-ci.org/skroutz/cogy)
[![Gem Version](https://badge.fury.io/rb/cogy.svg)](https://badge.fury.io/rb/cogy)
[![Documentation](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/skroutz/cogy)Cogy integrates [Cog](https://operable.io/) with Rails
in a way that writing & deploying commands from your application is a breeze.See the API documentation [here](http://www.rubydoc.info/github/skroutz/cogy).
Refer to the [Changelog](CHANGELOG.md) to see what's changed between releases.
## Features
- Define commands from your Rails app (see [_Usage_](#usage))
- Bundle config is generated automatically
- Commands are installed _automatically_ when you deploy (see [_Deployment_](#deployment))
- Supports JSON responses and Cog Templates (see [_Returning JSON to COG_](#returning-json-to-cog))
- Customizable error template (see [_Error template_](#error-template))...and [more on the way](https://github.com/skroutz/cogy/issues?q=is%3Aopen+is%3Aissue+label%3Afeature)!
## Why
Creating ChatOps commands that talk with a Rails app typically involves writing
a route, maybe a controller, an action and code to handle the command arguments
and options.This is a tedious and repetitive task and involves a lot of boilerplate
code each time someone wants to add a new command.Cogy is an opinionated library that provides a way to get rid of all the
boilerplate stuff so you can focus on just the actual commands.Deploying a new command is as simple as writing:
```ruby
# in cogy/my_commands.rbon "foo", desc: "Echo a foo bar back at you!" do
"@#{handle}: foo bar"
end
```...and deploying!
## How it works
Cogy is essentially three things:
1. An opinionated way to write, manage & ship commands: All Cogy commands are
defined in your Rails app and end up invoking a single executable within the
Relay (see below). Cogy also provides bundle versioning and dynamically generates the
installable bundle config, which is also served by your Rails application
and consumed by the [`cogy:install`](https://github.com/skroutz/cogy-bundle)
command that installs the new Cogy-generated bundle when you deploy your
application.
2. A library that provides the API for defining the commands. This library
is integrated in your application via a Rails Engine that routes the incoming
requests to their respective handlers. It also creates the `/inventory`
endpoint, which serves the installable bundle configuration in YAML and can be
consumed directly by the [`cogy:install`](https://github.com/skroutz/cogy-bundle) command.
3. A [Cog bundle](https://github.com/skroutz/cogy-bundle) that contains the
[executable](https://github.com/skroutz/cogy-bundle/blob/master/commands/cogy)
that all the commands end up invoking.
It is placed inside the Relays and performs the requests to your application
when a user invokes a command in the chat. It then posts the result back
to the user. It also contains the `cogy:install` command for automating
the task of installing the new bundle when a command is added/modified.Take a look at the relevant [diagrams](diagrams/) for a detailed illustration.
## Requirements
* Cog 1.0.0.beta2 or later
* [cogy bundle](https://github.com/skroutz/cogy-bundle) 0.4.0 or later
* Ruby 2.1 or later
* Rails 4.2 or later## Status
Cogy is still in public alpha.
While we use it in production, it's still under heavy development.
This means that there are a few rough edges and things change fast.However we'd love any [feedback, suggestions or ideas](https://github.com/skroutz/cogy/issues/new).
## Install
Add it to your Gemfile:
```ruby
gem "cogy"
```Then run `bundle install`
Next, run the generator:
```shell
$ bin/rails g cogy:install
```This will create a sample command, mount the engine and add a sample
configuration initializer in your application.## Usage
Defining a new command:
```ruby
# in cogy/my_commands.rbon "foo", desc: "Echo a bar" do
"bar"
end
```This will print "bar" back to the user who calls `!foo` in Slack.
Let's define a command that simply adds the numbers passed as arguments:
```ruby
# in cogy/calculations.rbon "add", args: [:a, :b], desc: "Add two numbers" do
a.to_i + b.to_i
end
```Inside the block there are the following helpers available:
* `args`: an array containing the arguments passed to the command
* arguments can also be accessed by their names as local variables
* `opts`: a hash containing the options passed to the command
* `handle`: the chat handle of the user who called the command
* `env`: a hash containing the Relay environment as available in the cogy
bundleFor instructions on defining your own helpers, see [Helpers](#helpers).
A more complete example:
```ruby
# in cogy/commands.rb
on "calc",
args: [:a, :b],
opts: { op: { type: "string", required: true } },
desc: "Performs a calculation between numbers and ",
examples: ["myapp:calc sum 1 2", "myapp:calc sum -1 -2"] do
op = opts[:op].to_sym
result = args.map(&:to_i).inject(&op)
"Hello @#{user}, the result is: #{result}"
end
```For more examples see the [test commands](https://github.com/skroutz/cogy/tree/master/test/dummies/cogy).
### Returning JSON to Cog
You can return JSON to Cog by just returning a `Hash`:
```ruby
on "foo", desc: "Just a JSON" do
{ a: 3 }
end
```The hash is automatically converted to JSON. The above command would return
the following response to Cog:```
COG_TEMPLATE: foo
JSON
{"a":3}
```To customize the Cog [template](#Templates) to be used, use the `template`
option:```ruby
on "foo", desc: "Just a JSON", template: "bar" do
{ a: 3 }
end
```Info on how Cog handles JSON can be found in the [official documentation](https://cog-book.operable.io/#_returning_data_from_cog).
### Templates
[Templates](https://cog-book.operable.io/#_templates) are defined in their own files under `templates/` inside any of
the [command load paths](#Configuration). For example:```
$ tree
.
├── README.rdoc
├── <..>
├── cogy
│ ├── some_commands.rb
│ └── templates
│ └── foo # <--- a template named 'foo'
|── <...>
```Given the following template:
```
# in cogy/templates/foo
~ hello world ~
```the resulting bundle config would look like this:
```yaml
---
cog_bundle_version: 4
name: foo
description: The bundle you really need
version: 0.0.1
commands:
<...>
templates:
foo:
body: |-
~ hello world ~
```Refer to the [Cog book](https://cog-book.operable.io/#_templates) for more on
templates.## Configuration
The configuration options provided are the following:
```ruby
# in config/initializers/cogy.rbCogy.configure do |config|
# Configuration related to the generated Cog bundle. Will be used when
# generating the bundle config YAML to be installed.
config.bundle = {
# The bundle name.
#
# Default: "myapp"
name: "myapp",# The bundle description
#
# Default: "Cog commands generated from Cogy"
description: "myapp-generated commands from Cogy",# The bundle version.
#
# Can be either a string or an object that responds to `#call` and returns
# a string.
#
# Default: "0.0.1"
version: "0.0.1",# If you used a callable object, it will be evaluated each time the inventory
# is called. This can be useful if you want the version to change
# automatically.
#
# For example, this will change the version only when a command is
# added or is modified (uses the 'grit' gem).
version: -> {
repo = Grit::Repo.new(Rails.root.to_s)
repo.log("HEAD", "cogy/", max_count: 1).first.date.strftime("%y%m%d.%H%M%S")
},# The path in the Relay where the cogy command executable is located.
cogy_executable: "/cogcmd/cogy"# The endpoint where Cogy is reachable at. This depends on where you've
# mounted the Cogy engine at.
cogy_endpoint: "http://www.example.com/cogy"
}# Paths in your application where the files that define the commands live in.
# For example the default value will search for all `*.rb` files in the `cogy/`
# directory relative to the root of your application.
#
# Default: ["cogy"]
config.command_load_paths = "cogy"
end```
You can use the generator to quickly create a config initializer in your app:
```shell
$ bin/rails g cogy:config
```### Helpers
It is possible to define helpers that can be used throughout commands. This is
useful for DRYing repetitive code.They are defined during configuration and may also accept arguments.
Let's define a helper that fetches the `address` of a `Shop` record:
command:```ruby
Cogy.configure do |c|
c.helper(:shop_address) { Shop.find_by(owner: handle).address }
end
```*(Note that custom helpers also have access to the default helpers like
`handle`, `args` etc.)*Then we could have a command that makes use of the helper:
```ruby
on "shop_address", desc: "Returns the user's Shop address" do
"@#{handle}: Your shop's address is #{shop_address}"
end
```Helpers may also accept arguments:
```ruby
Cogy.configure do |c|
c.helper(:format) { |answer| answer.titleize }
end
```This helper could be called like so:
```ruby
on "foo", desc: "Nothing special" do
format "hello there, how are you today?"
end
```Rails URL helpers (ie. `foo_url`) are also available inside the commands.
## Error template
When a command throws an error the
[default error template](https://github.com/skroutz/cogy/blob/master/app/views/cogy/error.text.erb) is rendered, which
is the following:@<%= @user %>: Command '<%= @cmd %>' returned an error.
```
<%= @exception.class %>:<%= @exception.message %>
```It can be overriden in the application by creating a view in
`app/views/cogy/error.text.erb`.## Testing commands
We don't yet provide means to write tests for the commands, but you can easily
test them by executing a request to your development server. For example,
if you mounted the engine like so:```ruby
mount Cogy::Engine, at: "cogy"
```you can test a `foo` command like this:
```shell
$ curl -XPOST --data "COG_ARGV_0=foo" http://localhost:3000/cogy/cmd/foo
```In the request body you may pass the complete or any part of the
[Cog environment](https://cog-book.operable.io/#_command_environment_variables) you need.This is essentially what the [cogy executable](https://github.com/skroutz/cogy-bundle)
also does.## Deployment
Cogy provides integration with Capistrano 2 & 3.
There is just one task, `cogy:notify_cog`, which executes the
[installation Trigger](#installation-trigger).The task should run
*after* the application server is restarted, so that the new commands
are picked up and served by the Inventory endpoint. In Capistrano 2 for
example, it should run after the built-in `deploy:restart` task.The following options need to be set:
* `cogy_release_trigger_url`: This is the URL of the Cog Trigger that will
install the newly deployed bundle (ie. `!cogy:install`)
* `cogy_endpoint`: Where the Cogy Engine is mounted at.
For example `http://myapp.com/cogy`.You can also configure the timeout value for the request to the Trigger by
setting the `cogy_trigger_timeout` option (default: 7).The code of the task can be found [here](https://github.com/skroutz/cogy/blob/master/lib/cogy/capistrano/cogy.rake).
### Capistrano 2
Add the following in `config/deploy.rb`:
```ruby
# in config/deploy.rb
require "cogy/capistrano"set :cogy_release_trigger_url, ""
set :cogy_endpoint, ""after "deploy:restart", "cogy:notify_cog"
```### Capistrano 3
Add the following in your Capfile:
```ruby
require "cogy/capistrano"
```Then configure the task and hook it:
```ruby
# in config/deploy.rbset :cogy_release_trigger_url, ""
set :cogy_endpoint, ""after "", "cogy:notify_cog"
```### Installation Trigger
In order to automate the process of installing the new bundle versions
(eg. after a new command is added), you must create a [Cog Trigger](https://cog-book.operable.io/#_developing_a_trigger)
that will perform the installation, which will be called when you deploy your
app.The trigger will look this:
```shell
$ cogctl triggers
Name ID Enabled Pipeline
ReleaseUrlTrigger d10df83b-a737-4fc4-8d9b-bf9627412d0a true cogy:install --url $body.url > chat://#general
```It essentially uses the [cogy bundle](https://github.com/skroutz/cogy-bundle)
and installs the bundle config which is served by your application
(ie. http://your-app.com/cogy/inventory).See [_Deployment_](#deployment) on information about how this trigger is
invoked.## Development
Running the tests and RuboCop for the latest Rails version:
```shell
$ rake
```Running just the tests:
```shell
$ rake test
```Running RuboCop:
```shell
$ rake rubocop
```Generating documentation:
```shell
$ rake yard
```Running the tests for all the supported Rails versions:
```shell
$ appraisal rake
```Or for a specific version:
```shell
$ appraisal 4.2 rake test
```## Authors
* [Agis Anastasopoulos](https://github.com/agis-)
* [Mpampis Kostas](https://github.com/charkost)## License
Cogy is licensed under MIT. See [LICENSE](LICENSE).