hk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+
2
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
3
+ #
4
+ # If you find yourself ignoring temporary files generated by your text editor
5
+ # or operating system, you probably want to add a global ignore instead:
6
+ # git config --global core.excludesfile ~/.gitignore_global
7
+
8
+ /tmp
9
+ *.swp
10
+ *.rbc
11
+ .DS_Store
12
+ .vimrc
13
+ .jhw-cache
14
+ results.html
15
+ pkg
@@ -0,0 +1 @@
1
+ hk
@@ -0,0 +1 @@
1
+ 1.9.3-p429
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,60 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hk (0.0.1)
5
+ gem-man
6
+ gli (~> 2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ aruba (0.5.3)
12
+ childprocess (>= 0.3.6)
13
+ cucumber (>= 1.1.1)
14
+ rspec-expectations (>= 2.7.0)
15
+ builder (3.2.2)
16
+ childprocess (0.3.9)
17
+ ffi (~> 1.0, >= 1.0.11)
18
+ clean_test (1.0.0)
19
+ faker
20
+ cucumber (1.3.5)
21
+ builder (>= 2.1.2)
22
+ diff-lcs (>= 1.1.3)
23
+ gherkin (~> 2.12.0)
24
+ multi_json (~> 1.7.5)
25
+ multi_test (>= 0.0.2)
26
+ diff-lcs (1.2.4)
27
+ faker (1.2.0)
28
+ i18n (~> 0.5)
29
+ ffi (1.9.0)
30
+ gem-man (0.3.0)
31
+ gherkin (2.12.0)
32
+ multi_json (~> 1.3)
33
+ gli (2.7.0)
34
+ hpricot (0.8.6)
35
+ i18n (0.6.4)
36
+ json (1.8.0)
37
+ multi_json (1.7.8)
38
+ multi_test (0.0.2)
39
+ mustache (0.99.4)
40
+ rake (10.1.0)
41
+ rdiscount (2.1.6)
42
+ rdoc (4.0.1)
43
+ json (~> 1.4)
44
+ ronn (0.7.3)
45
+ hpricot (>= 0.8.2)
46
+ mustache (>= 0.7.0)
47
+ rdiscount (>= 1.5.8)
48
+ rspec-expectations (2.14.0)
49
+ diff-lcs (>= 1.1.3, < 2.0)
50
+
51
+ PLATFORMS
52
+ ruby
53
+
54
+ DEPENDENCIES
55
+ aruba
56
+ clean_test
57
+ hk!
58
+ rake
59
+ rdoc
60
+ ronn
@@ -0,0 +1,116 @@
1
+ hk(1) -- Simplify your access to Heroku on the command-line
2
+ ===========================================================
3
+
4
+ ## SYNOPSIS
5
+
6
+ `hk **alias** _app_string_`
7
+
8
+ `hk **any heroku invocation** _app_string_`
9
+
10
+ ## INSTALL
11
+
12
+ $ gem install hk
13
+
14
+ ## DESCRIPTION
15
+
16
+ **hk** is an alias system for Heroku's command-line app that does three things:
17
+
18
+ * Wrap complex or cumbersome commands and options into single-word (or single-letter) aliases (e.g. `hk logs` will do `heroku logs --tail`)
19
+ * Derive application names from shorter-strings (e.g. `hk logs f-p` will run `heroku logs --tail --app=foo-production`)
20
+ * Alleviates you from typing `heroku`, which is hard to type and prone to misspellings
21
+
22
+ **hk** can easily be extended with whatever command aliases you would like. It also passes through any unknown command to the underlying `heroku` command, but *does do* the app name derivation.
23
+
24
+ ## COMMAND ALIASING
25
+
26
+ Command aliasing is fairly straightforward - a string is used to generate a more complex heroku command invocation. You can create your own in `~/.hk/commands/commands.rb` (boilerplate which can be created via `hk init`).
27
+
28
+ desc 'Get app info'
29
+ arg_name Hk::ARG_NAME
30
+ command :info do |c|
31
+ c.action do |_,_,args|
32
+ Hk.run("apps:info",args)
33
+ end
34
+ end
35
+
36
+ This code is the GLI DSL, so you can use that entire API if you want to, however the above is a minimal example.
37
+
38
+ You'll note that your command now shows up if you do `hk help`.
39
+
40
+ ## APP NAME DERIVATION
41
+
42
+ If your team is maintaining various apps on heroku, and using it for staging, it can be quite cumbersome to be constantly typing out the names of apps. It's also somewhat dangerous to rely on the `.git/config` because it obfuscates what's going to happen behind your current directory.
43
+
44
+ **hk** will derive your application name, assuming that you use a canonical naming structure that is **app_name-environment_name**. **app_name** is a logical name for your app, e.g. "foobar". **environment_name** is a logical name for an environment, like "staging" or "production".
45
+
46
+ So, instead of typing `--app=foobar-production`, you can simply type `f-p`. As long as you have no other app that starts with "f", and no other environment that starts with "p" (based on the output of `heroku apps`), then **hk** will derive the name. You can add more letters to disambiguate, so if you had an app call "frob-production", you would use "fo-p" for "foobar-production" and "fr-p" for "frob-production"
47
+
48
+ ## OPTIONS
49
+
50
+ * `--heroku PATH_TO_HEROKU`:
51
+ Specify where the heroku command line app is. If omitted, will just use your path
52
+ * `--verbose`:
53
+ If specified, will output the heroku command being executed
54
+ * `-n`,`--no-app-derivation`:
55
+ If specified, will skip deriving the app and tacking on a `--app` option
56
+
57
+ ## EXAMPLES
58
+
59
+ There are three builtin aliases (which you can disable by setting SKIP_DEFAULTS=true in your customizations file): console, logs, and psql:
60
+
61
+ hk logs f-p
62
+ # heroku logs --tail --app=foo-production
63
+
64
+ hk console f-p
65
+ # heroku run rails c --app=foo-production
66
+
67
+ hk psql f-p
68
+ # heroku pg:psql --app=foo-production
69
+
70
+ Unknown commands will pass through, but app derivation will still take place:
71
+
72
+ hk run rake db:migrate f-s
73
+ # heroku run rake db:migrate --app=foo-staging
74
+
75
+ You can also be explicit about where `heroku` is you don't want to rely on your PATH:
76
+
77
+ hk --heroku=/sbin/herkou logs f-p
78
+ # /sbin/heroku logs --tail --app=foo-production
79
+
80
+ And, you can skip all the magic if you like:
81
+
82
+ hk -n --heroku=/sbin/herkou pg:info --app=hellblazer
83
+ # sbin/herkou pg:info --app=hellblazer
84
+
85
+ Note that because this is backed by GLI, you only need to specify the command-name with enough characters to disambiguate it. For example, given the three builtins, these two are equivalent:
86
+
87
+
88
+ hk logs f-p
89
+ hk l f-p
90
+
91
+ ## AUTHOR
92
+
93
+ David Copeland, davec (at) naildrivin5.com
94
+
95
+ ## COPYRIGHT
96
+
97
+ hk is copyright(c) 2012 by David Copeland, released under the Apache license.
98
+
99
+ ## SEE ALSO
100
+
101
+ * Source on github: https://github.com/davetron5000/hk
102
+
103
+
104
+ [SYNOPSIS]: #SYNOPSIS "SYNOPSIS"
105
+ [INSTALL]: #INSTALL "INSTALL"
106
+ [DESCRIPTION]: #DESCRIPTION "DESCRIPTION"
107
+ [COMMAND ALIASING]: #COMMAND-ALIASING "COMMAND ALIASING"
108
+ [APP NAME DERIVATION]: #APP-NAME-DERIVATION "APP NAME DERIVATION"
109
+ [OPTIONS]: #OPTIONS "OPTIONS"
110
+ [EXAMPLES]: #EXAMPLES "EXAMPLES"
111
+ [AUTHOR]: #AUTHOR "AUTHOR"
112
+ [COPYRIGHT]: #COPYRIGHT "COPYRIGHT"
113
+ [SEE ALSO]: #SEE-ALSO "SEE ALSO"
114
+
115
+
116
+ [hk(1)]: hk.1.html
@@ -0,0 +1,9 @@
1
+ $: << File.dirname(__FILE__)
2
+
3
+ require 'rake/clean'
4
+ require 'lib/rake/cucumber'
5
+ require 'lib/rake/test'
6
+ require 'lib/rake/man'
7
+ require 'lib/rake/bundler'
8
+
9
+ task :default => [:test,:features,:man]
data/bin/hk ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'hk'
4
+
5
+ include GLI::App
6
+
7
+ program_desc 'Expedient interface to your heroku apps'
8
+ program_long_desc %{The heroku command-line app does a lot of stuff, however it's not geared toward making common things simple. Further, it doesn't know about environments, and it can be a pain to type app names in all the time.
9
+
10
+ hk aims to make that simpler by allowing expedient access to commonly-needed commands as well as a simpler way to specify which app - and which environment - you want to operate on}
11
+
12
+ preserve_argv
13
+
14
+ version Hk::VERSION
15
+
16
+ Hk::GlobalOptions.declare_gli_options(self)
17
+
18
+ desc "Bootstrap your extensions/customizations directory"
19
+ long_desc "You can create your own customizations and aliases for complex Heroku commands, by placing Ruby code in ~/.hk/commands. `init` will set that up and give you basic instructions on how to do it"
20
+
21
+ command :init do |c|
22
+ c.action do |_,_,_|
23
+ commands_dir = File.join(ENV["HOME"],".hk","commands")
24
+ FileUtils.mkdir_p commands_dir
25
+ filename = File.join(commands_dir,"commands.rb")
26
+ exit_now!("you already have commands defined in #{commands_dir.gsub(ENV['HOME'],'~')}") if File.exists?(filename)
27
+ File.open(filename,"w") do |file|
28
+ file.puts <<EOS
29
+ # This file gets require'ed into hk directly, so you can put whatever Ruby
30
+ # code you want here. It follows the GLI syntax, but there are additional
31
+ # helpers for making an hk-style command.
32
+
33
+
34
+ desc 'Get app info' # This shows up in `hk help`
35
+ arg_name Hk::ARG_NAME # This makes your usage string canonical, so just leave verbatim
36
+ command :info do |c| # You can also an array for multiple names
37
+ c.action do |_,_,args|
38
+ Hk.run("apps:info",args) # This will run heroku apps:info --app=<derived app name>
39
+ end
40
+ end
41
+
42
+ # You can add as many of these as you like
43
+
44
+ SKIP_DEFAULTS=false # set this to true to NOT create the default aliases that come with hk
45
+ # Useful if you aren't a Rails developer or Postgres user
46
+ EOS
47
+ end
48
+ end
49
+ end
50
+
51
+ commands_from File.join(ENV["HOME"],".hk","commands")
52
+
53
+ skip_defaults = defined?(SKIP_DEFAULTS) && SKIP_DEFAULTS == true
54
+ unless skip_defaults
55
+ {
56
+ :logs => {
57
+ desc: "Tail logs for app",
58
+ command: "logs --tail",
59
+ },
60
+ :console => {
61
+ desc: "Get a rails console for app",
62
+ command: "run rails c",
63
+ },
64
+ :psql => {
65
+ desc: "Get a database console for app",
66
+ command: "pg:psql ",
67
+ }
68
+ }.each do |name,metadata|
69
+ desc metadata[:desc]
70
+ arg_name Hk::ARG_NAME
71
+ command name do |c|
72
+ c.action do |_,_,args|
73
+ Hk.run(metadata[:command],args)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ pre do |global,command,options,args|
80
+ $heroku = Hk::HerokuCommand.new(global[:heroku],global[:verbose],global["no-app-derivation"])
81
+ true
82
+ end
83
+
84
+ on_error do |exception|
85
+ case exception
86
+ when GLI::CustomExit
87
+ true
88
+ else
89
+ opts = Hk::GlobalOptions.option_parser
90
+
91
+ args = opts.parse_and_get_args(ARGV)
92
+
93
+ heroku = Hk::HerokuCommand.new(opts.heroku,opts.verbose,opts.no_app_derivation)
94
+ heroku.run(args[0..-2].map {|_| "'#{_}'"}.join(" "),args[-1])
95
+ false
96
+ end
97
+ end
98
+
99
+ exit run(ARGV)
@@ -0,0 +1,52 @@
1
+ Feature: Basic UI
2
+ In order to stop typing so much
3
+ And mis-spelling heroku as "heroky"
4
+ I want a configurable shortcut app
5
+
6
+ Scenario: App has a help system
7
+ When I get help for "hk"
8
+ Then the exit status should be 0
9
+ And there should be an option to specify where the heroku command is
10
+
11
+ Scenario: arbitrary heroku command derives the app name
12
+ Given I have the heroku apps "foo-production,foo-staging,bar-production"
13
+ When I run the app with "blah f-p"
14
+ Then the heroku command-line app should be executed with the args "blah --app=foo-production"
15
+
16
+ Scenario: can avoid deriving the app name
17
+ Given I have the heroku apps "foo-production,foo-staging,bar-production"
18
+ When I run the app with "-n blah --app=f-p"
19
+ Then the heroku command-line app should be executed with the args "blah --app=f-p"
20
+
21
+ Scenario: logs command is helpful
22
+ Given I have the heroku apps "foo-production,foo-staging,bar-production"
23
+ When I run the app with "logs f-p"
24
+ Then the heroku command-line app should be executed with the args "logs --tail --app=foo-production"
25
+
26
+ Scenario: console command is helpful
27
+ Given I have the heroku apps "foo-production,foo-staging,bar-production"
28
+ When I run the app with "console f-p"
29
+ Then the heroku command-line app should be executed with the args "run rails c --app=foo-production"
30
+
31
+ Scenario: Can add my own commands
32
+ Given I have the heroku apps "foo-production,foo-staging,bar-production"
33
+ And I have the custom command "info" defined to run "apps:info"
34
+ When I run the app with "info f-p"
35
+ Then the heroku command-line app should be executed with the args "apps:info --app=foo-production"
36
+
37
+ Scenario: Can bootstrap the plugins
38
+ When I run the app with "init"
39
+ Then the file ".hk/commands/commands.rb" should exist in my home directory
40
+
41
+ Scenario: Won't squash existing file
42
+ Given I run the app with "init"
43
+ When I run the app with "init"
44
+ Then the exit status should not be 0
45
+ And the stderr should contain "error: you already have commands defined in ~/.hk/commands"
46
+
47
+ Scenario: Can skip the built-in aliases
48
+ Given my plugins specify SKIP_DEFAULTS=true
49
+ When I run the app with "help"
50
+ Then I should not see help for "logs"
51
+ And I should not see help for "console"
52
+ And I should not see help for "psql"
@@ -0,0 +1,52 @@
1
+ When /^I get help for "([^"]*)"$/ do |app_name|
2
+ @app_name = app_name
3
+ step %(I run `#{app_name} help`)
4
+ end
5
+
6
+ And /^there should be an option to specify where the heroku command is$/ do
7
+ step %{the output should contain "--heroku"}
8
+ end
9
+
10
+ Given(/^I have the heroku apps "(.*?)"$/) do |app_list|
11
+ end
12
+
13
+ When(/^I run the app with "(.*?)"$/) do |app_args|
14
+ fake_heroku = File.join(File.dirname(__FILE__),'..','support','fake_heroku.rb')
15
+ step %{I run `hk --heroku=#{fake_heroku} #{app_args}`}
16
+ end
17
+
18
+ Then(/^the heroku command\-line app should be executed with the args "(.*?)"$/) do |args|
19
+ step %{the output should contain "#{args}"}
20
+ end
21
+
22
+ Given(/^I have the custom command "(.*?)" defined to run "(.*?)"$/) do |command, args|
23
+ commands_file_dir = File.join(FAKE_HOME,".hk","commands")
24
+ FileUtils.mkdir_p commands_file_dir
25
+ File.open(File.join(commands_file_dir,"commands.rb"),"w") do |file|
26
+ file.puts <<EOS
27
+ desc 'Get app info'
28
+ arg_name Hk::ARG_NAME
29
+ command "#{command}" do |c|
30
+ c.action do |_,_,args|
31
+ Hk.run("#{args}",args)
32
+ end
33
+ end
34
+ EOS
35
+ end
36
+ end
37
+
38
+ Then(/^the file "(.*?)" should exist in my home directory$/) do |file_name|
39
+ File.exists?(File.join(ENV["HOME"],file_name)).should == true
40
+ end
41
+
42
+ Given(/^my plugins specify SKIP_DEFAULTS=true$/) do
43
+ commands_file_dir = File.join(FAKE_HOME,".hk","commands")
44
+ FileUtils.mkdir_p commands_file_dir
45
+ File.open(File.join(commands_file_dir,"commands.rb"),"w") do |file|
46
+ file.puts "SKIP_DEFAULTS=true"
47
+ end
48
+ end
49
+
50
+ Then(/^I should not see help for "(.*?)"$/) do |arg1|
51
+ step %{the output should not match /#{arg1}\s+-/}
52
+ end
@@ -0,0 +1,21 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
5
+ FAKE_HOME = "/tmp/fake_home"
6
+
7
+ Before do
8
+ # Using "announce" causes massive warnings on 1.9.2
9
+ @puts = true
10
+ @original_rubylib = ENV['RUBYLIB']
11
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
12
+ FileUtils.rm_rf FAKE_HOME
13
+ FileUtils.mkdir_p FAKE_HOME
14
+ @original_home = ENV['HOME']
15
+ ENV['HOME'] = FAKE_HOME
16
+ end
17
+
18
+ After do
19
+ ENV['RUBYLIB'] = @original_rubylib
20
+ ENV['HOME'] = @original_home
21
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV[0] == "apps"
4
+ puts <<-EOF
5
+
6
+ ===
7
+ foo-production blah@herokumanager.com
8
+ foo-staging blah@herokumanager.com
9
+ bar-production blah@herokumanager.com
10
+ EOF
11
+ else
12
+ puts ARGV.join(" ")
13
+ end
@@ -0,0 +1,26 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__),'lib','hk','version.rb'])
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'hk'
5
+ s.version = Hk::VERSION
6
+ s.author = 'Your Name Here'
7
+ s.email = 'your@email.address.com'
8
+ s.homepage = 'http://your.website.com'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'A description of your project'
11
+ # Add your other files here if you make them
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ s.require_paths = ["lib"]
16
+ s.has_rdoc = false
17
+ s.bindir = 'bin'
18
+ s.executables << 'hk'
19
+ s.add_development_dependency('clean_test')
20
+ s.add_development_dependency('rake')
21
+ s.add_development_dependency('rdoc')
22
+ s.add_development_dependency('aruba')
23
+ s.add_development_dependency('ronn')
24
+ s.add_runtime_dependency('gli','~> 2')
25
+ s.add_runtime_dependency('gem-man')
26
+ end
@@ -0,0 +1,13 @@
1
+ require 'hk/version'
2
+ require 'hk/global_options'
3
+ require 'hk/heroku_runner'
4
+ require 'hk/app_lister'
5
+ require 'hk/app_name_deriver'
6
+ require 'hk/heroku_command'
7
+ module Hk
8
+ ARG_NAME = "app_name-environment"
9
+
10
+ def self.run(heroku_command_args,args)
11
+ $heroku.run(heroku_command_args,args.first)
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module Hk
2
+ class AppLister
3
+ def initialize(heroku_runner)
4
+ @heroku_runner = heroku_runner
5
+ end
6
+
7
+ def list_apps
8
+ @heroku_runner.run_internal("apps | cut -d ' ' -f1 | grep -v '==='").split(/\n/).map(&:strip)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ module Hk
2
+ class AppNameDeriver
3
+ def initialize(app_lister)
4
+ @app_lister = app_lister
5
+ end
6
+
7
+ def derive(app_string)
8
+ app,environment = app_string.split(/\-/)
9
+ apps = @app_lister.list_apps
10
+
11
+ return app_string if apps.include?(app_string)
12
+
13
+ apps = apps.map { |_| _.split(/\-/)}.select { |(a,_)|
14
+ a =~ /^#{app}/
15
+ }.select { |(_,e)|
16
+ if e.nil? && environment.nil?
17
+ true
18
+ elsif e.nil?
19
+ false
20
+ elsif environment.nil?
21
+ false
22
+ else
23
+ e =~ /^#{environment}/
24
+ end
25
+ }
26
+ if apps.size == 1
27
+ apps.first.join("-")
28
+ elsif apps.size == 0
29
+ raise "No such app name matched #{app_string}"
30
+ else
31
+ raise "Ambiguous app name for #{app_string}. Matches: #{apps.map {|_| _.join("-") }.join(',')}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ module Hk
2
+ # Since we have to do some hacking to handle arbitrary commands,
3
+ # this serves as a central location for the global options that enable that.
4
+ class GlobalOptions
5
+ def self.declare_gli_options(app)
6
+ app.desc "Explicitly set where to find the herkou CLI app"
7
+ app.default_value "heroku"
8
+ app.arg_name "path_to_heroku"
9
+ app.flag :heroku
10
+
11
+ app.desc "Be verbose - show the heroku commands being run"
12
+ app.switch :verbose
13
+
14
+ app.desc "Do not derive the app name or append --app to the command"
15
+ app.switch [:n,"no-app-derivation"], negatable: false
16
+
17
+ end
18
+
19
+ def self.option_parser
20
+ OptionParser.new.tap { |opts|
21
+ class << opts
22
+ def heroku=(h)
23
+ @heroku = h
24
+ end
25
+ def heroku; @heroku || "heroku"; end
26
+ def verbose=(v)
27
+ @verbose = v
28
+ end
29
+ def verbose; @verbose; end
30
+ def no_app_derivation=(v)
31
+ @no_app_derivation = v
32
+ end
33
+ def no_app_derivation; @no_app_derivation; end
34
+ def parse_and_get_args(args)
35
+ args = args.dup
36
+ self.order!(args) do |unknown_option|
37
+ args.unshift(unknown_option)
38
+ break
39
+ end
40
+ args
41
+ end
42
+ end
43
+ opts.on("--heroku HEROKU") { |path| opts.heroku = path }
44
+ opts.on("--verbose") { opts.verbose = true }
45
+ opts.on("-n","--no-app-derivation") { opts.no_app_derivation = true }
46
+ }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ module Hk
2
+ class HerokuCommand
3
+ def initialize(path_to_heroku,verbose,no_app_derivation)
4
+ @heroku = Hk::HerokuRunner.new(path_to_heroku,verbose)
5
+ @app_name_deriver = if no_app_derivation
6
+ ->(app_name_string) { app_name_string }
7
+ else
8
+ ->(app_name_string) {
9
+ "--app=#{Hk::AppNameDeriver.new(Hk::AppLister.new(@heroku)).derive(app_name_string)}"
10
+ }
11
+ end
12
+ end
13
+
14
+ def run(heroku_args,app_name_string)
15
+ @heroku.("#{heroku_args} #{@app_name_deriver.(app_name_string)}")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ module Hk
2
+ class HerokuRunner
3
+ def initialize(heroku_command,verbose)
4
+ @heroku_command = heroku_command
5
+ @verbose = verbose
6
+ end
7
+
8
+ def run(args)
9
+ command = "#{@heroku_command} #{args}"
10
+ puts command if @verbose
11
+ system(command)
12
+ end
13
+
14
+ def call(args)
15
+ run(args)
16
+ end
17
+
18
+ def run_internal(args)
19
+ command = "#{@heroku_command} #{args}"
20
+ puts command if @verbose
21
+ `#{command}`
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Hk
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,25 @@
1
+ require 'cucumber'
2
+ require 'cucumber/rake/task'
3
+
4
+ CUKE_RESULTS = 'results.html'
5
+ CLEAN << CUKE_RESULTS
6
+
7
+ desc 'Run features'
8
+ Cucumber::Rake::Task.new(:features) do |t|
9
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
10
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
11
+ t.cucumber_opts = opts
12
+ t.fork = false
13
+ end
14
+
15
+ desc 'Run features tagged as work-in-progress (@wip)'
16
+ Cucumber::Rake::Task.new('features:wip') do |t|
17
+ tag_opts = ' --tags ~@pending'
18
+ tag_opts = ' --tags @wip'
19
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
20
+ t.fork = false
21
+ end
22
+
23
+ task :cucumber => :features
24
+ task 'cucumber:wip' => 'features:wip'
25
+ task :wip => 'features:wip'
@@ -0,0 +1,8 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ desc "Generate man pages and README"
5
+ task :man do
6
+ sh 'ronn --markdown --roff man/hk.1.ronn'
7
+ mv 'man/hk.1.markdown','README.md'
8
+ end
@@ -0,0 +1,6 @@
1
+ require 'rake/testtask'
2
+ Rake::TestTask.new do |t|
3
+ t.libs << "test"
4
+ t.test_files = FileList['test/*_test.rb']
5
+ end
6
+
@@ -0,0 +1,176 @@
1
+ .\" generated with Ronn/v0.7.3
2
+ .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
+ .
4
+ .TH "HK" "1" "August 2013" "" ""
5
+ .
6
+ .SH "NAME"
7
+ \fBhk\fR \- Simplify your access to Heroku on the command\-line
8
+ .
9
+ .SH "SYNOPSIS"
10
+ \fBhk **alias** _app_string_\fR
11
+ .
12
+ .P
13
+ \fBhk **any heroku invocation** _app_string_\fR
14
+ .
15
+ .SH "INSTALL"
16
+ .
17
+ .nf
18
+
19
+ $ gem install hk
20
+ .
21
+ .fi
22
+ .
23
+ .SH "DESCRIPTION"
24
+ \fBhk\fR is an alias system for Heroku\'s command\-line app that does three things:
25
+ .
26
+ .IP "\(bu" 4
27
+ Wrap complex or cumbersome commands and options into single\-word (or single\-letter) aliases (e\.g\. \fBhk logs\fR will do \fBheroku logs \-\-tail\fR)
28
+ .
29
+ .IP "\(bu" 4
30
+ Derive application names from shorter\-strings (e\.g\. \fBhk logs f\-p\fR will run \fBheroku logs \-\-tail \-\-app=foo\-production\fR)
31
+ .
32
+ .IP "\(bu" 4
33
+ Alleviates you from typing \fBheroku\fR, which is hard to type and prone to misspellings
34
+ .
35
+ .IP "" 0
36
+ .
37
+ .P
38
+ \fBhk\fR can easily be extended with whatever command aliases you would like\. It also passes through any unknown command to the underlying \fBheroku\fR command, but \fIdoes do\fR the app name derivation\.
39
+ .
40
+ .SH "COMMAND ALIASING"
41
+ Command aliasing is fairly straightforward \- a string is used to generate a more complex heroku command invocation\. You can create your own in \fB~/\.hk/commands/commands\.rb\fR (boilerplate which can be created via \fBhk init\fR)\.
42
+ .
43
+ .IP "" 4
44
+ .
45
+ .nf
46
+
47
+ desc \'Get app info\'
48
+ arg_name Hk::ARG_NAME
49
+ command :info do |c|
50
+ c\.action do |_,_,args|
51
+ Hk\.run("apps:info",args)
52
+ end
53
+ end
54
+ .
55
+ .fi
56
+ .
57
+ .IP "" 0
58
+ .
59
+ .P
60
+ This code is the GLI DSL, so you can use that entire API if you want to, however the above is a minimal example\.
61
+ .
62
+ .P
63
+ You\'ll note that your command now shows up if you do \fBhk help\fR\.
64
+ .
65
+ .SH "APP NAME DERIVATION"
66
+ If your team is maintaining various apps on heroku, and using it for staging, it can be quite cumbersome to be constantly typing out the names of apps\. It\'s also somewhat dangerous to rely on the \fB\.git/config\fR because it obfuscates what\'s going to happen behind your current directory\.
67
+ .
68
+ .P
69
+ \fBhk\fR will derive your application name, assuming that you use a canonical naming structure that is \fBapp_name\-environment_name\fR\. \fBapp_name\fR is a logical name for your app, e\.g\. "foobar"\. \fBenvironment_name\fR is a logical name for an environment, like "staging" or "production"\.
70
+ .
71
+ .P
72
+ So, instead of typing \fB\-\-app=foobar\-production\fR, you can simply type \fBf\-p\fR\. As long as you have no other app that starts with "f", and no other environment that starts with "p" (based on the output of \fBheroku apps\fR), then \fBhk\fR will derive the name\. You can add more letters to disambiguate, so if you had an app call "frob\-production", you would use "fo\-p" for "foobar\-production" and "fr\-p" for "frob\-production"
73
+ .
74
+ .SH "OPTIONS"
75
+ .
76
+ .TP
77
+ \fB\-\-heroku PATH_TO_HEROKU\fR
78
+ Specify where the heroku command line app is\. If omitted, will just use your path
79
+ .
80
+ .TP
81
+ \fB\-\-verbose\fR
82
+ If specified, will output the heroku command being executed
83
+ .
84
+ .TP
85
+ \fB\-n\fR,\fB\-\-no\-app\-derivation\fR
86
+ If specified, will skip deriving the app and tacking on a \fB\-\-app\fR option
87
+ .
88
+ .SH "EXAMPLES"
89
+ There are three builtin aliases (which you can disable by setting SKIP_DEFAULTS=true in your customizations file): console, logs, and psql:
90
+ .
91
+ .IP "" 4
92
+ .
93
+ .nf
94
+
95
+ hk logs f\-p
96
+ # heroku logs \-\-tail \-\-app=foo\-production
97
+
98
+ hk console f\-p
99
+ # heroku run rails c \-\-app=foo\-production
100
+
101
+ hk psql f\-p
102
+ # heroku pg:psql \-\-app=foo\-production
103
+ .
104
+ .fi
105
+ .
106
+ .IP "" 0
107
+ .
108
+ .P
109
+ Unknown commands will pass through, but app derivation will still take place:
110
+ .
111
+ .IP "" 4
112
+ .
113
+ .nf
114
+
115
+ hk run rake db:migrate f\-s
116
+ # heroku run rake db:migrate \-\-app=foo\-staging
117
+ .
118
+ .fi
119
+ .
120
+ .IP "" 0
121
+ .
122
+ .P
123
+ You can also be explicit about where \fBheroku\fR is you don\'t want to rely on your PATH:
124
+ .
125
+ .IP "" 4
126
+ .
127
+ .nf
128
+
129
+ hk \-\-heroku=/sbin/herkou logs f\-p
130
+ # /sbin/heroku logs \-\-tail \-\-app=foo\-production
131
+ .
132
+ .fi
133
+ .
134
+ .IP "" 0
135
+ .
136
+ .P
137
+ And, you can skip all the magic if you like:
138
+ .
139
+ .IP "" 4
140
+ .
141
+ .nf
142
+
143
+ hk \-n \-\-heroku=/sbin/herkou pg:info \-\-app=hellblazer
144
+ # sbin/herkou pg:info \-\-app=hellblazer
145
+ .
146
+ .fi
147
+ .
148
+ .IP "" 0
149
+ .
150
+ .P
151
+ Note that because this is backed by GLI, you only need to specify the command\-name with enough characters to disambiguate it\. For example, given the three builtins, these two are equivalent:
152
+ .
153
+ .IP "" 4
154
+ .
155
+ .nf
156
+
157
+ hk logs f\-p
158
+ hk l f\-p
159
+ .
160
+ .fi
161
+ .
162
+ .IP "" 0
163
+ .
164
+ .SH "AUTHOR"
165
+ David Copeland, davec (at) naildrivin5\.com
166
+ .
167
+ .SH "COPYRIGHT"
168
+ hk is copyright(c) 2012 by David Copeland, released under the Apache license\.
169
+ .
170
+ .SH "SEE ALSO"
171
+ .
172
+ .IP "\(bu" 4
173
+ Source on github: https://github\.com/davetron5000/hk
174
+ .
175
+ .IP "" 0
176
+
@@ -0,0 +1,101 @@
1
+ hk(1) -- Simplify your access to Heroku on the command-line
2
+ ===========================================================
3
+
4
+ ## SYNOPSIS
5
+
6
+ `hk **alias** _app_string_`
7
+
8
+ `hk **any heroku invocation** _app_string_`
9
+
10
+ ## INSTALL
11
+
12
+ $ gem install hk
13
+
14
+ ## DESCRIPTION
15
+
16
+ **hk** is an alias system for Heroku's command-line app that does three things:
17
+
18
+ * Wrap complex or cumbersome commands and options into single-word (or single-letter) aliases (e.g. `hk logs` will do `heroku logs --tail`)
19
+ * Derive application names from shorter-strings (e.g. `hk logs f-p` will run `heroku logs --tail --app=foo-production`)
20
+ * Alleviates you from typing `heroku`, which is hard to type and prone to misspellings
21
+
22
+ **hk** can easily be extended with whatever command aliases you would like. It also passes through any unknown command to the underlying `heroku` command, but *does do* the app name derivation.
23
+
24
+ ## COMMAND ALIASING
25
+
26
+ Command aliasing is fairly straightforward - a string is used to generate a more complex heroku command invocation. You can create your own in `~/.hk/commands/commands.rb` (boilerplate which can be created via `hk init`).
27
+
28
+ desc 'Get app info'
29
+ arg_name Hk::ARG_NAME
30
+ command :info do |c|
31
+ c.action do |_,_,args|
32
+ Hk.run("apps:info",args)
33
+ end
34
+ end
35
+
36
+ This code is the GLI DSL, so you can use that entire API if you want to, however the above is a minimal example.
37
+
38
+ You'll note that your command now shows up if you do `hk help`.
39
+
40
+ ## APP NAME DERIVATION
41
+
42
+ If your team is maintaining various apps on heroku, and using it for staging, it can be quite cumbersome to be constantly typing out the names of apps. It's also somewhat dangerous to rely on the `.git/config` because it obfuscates what's going to happen behind your current directory.
43
+
44
+ **hk** will derive your application name, assuming that you use a canonical naming structure that is **app_name-environment_name**. **app_name** is a logical name for your app, e.g. "foobar". **environment_name** is a logical name for an environment, like "staging" or "production".
45
+
46
+ So, instead of typing `--app=foobar-production`, you can simply type `f-p`. As long as you have no other app that starts with "f", and no other environment that starts with "p" (based on the output of `heroku apps`), then **hk** will derive the name. You can add more letters to disambiguate, so if you had an app call "frob-production", you would use "fo-p" for "foobar-production" and "fr-p" for "frob-production"
47
+
48
+ ## OPTIONS
49
+
50
+ * `--heroku PATH_TO_HEROKU`:
51
+ Specify where the heroku command line app is. If omitted, will just use your path
52
+ * `--verbose`:
53
+ If specified, will output the heroku command being executed
54
+ * `-n`,`--no-app-derivation`:
55
+ If specified, will skip deriving the app and tacking on a `--app` option
56
+
57
+ ## EXAMPLES
58
+
59
+ There are three builtin aliases (which you can disable by setting SKIP_DEFAULTS=true in your customizations file): console, logs, and psql:
60
+
61
+ hk logs f-p
62
+ # heroku logs --tail --app=foo-production
63
+
64
+ hk console f-p
65
+ # heroku run rails c --app=foo-production
66
+
67
+ hk psql f-p
68
+ # heroku pg:psql --app=foo-production
69
+
70
+ Unknown commands will pass through, but app derivation will still take place:
71
+
72
+ hk run rake db:migrate f-s
73
+ # heroku run rake db:migrate --app=foo-staging
74
+
75
+ You can also be explicit about where `heroku` is you don't want to rely on your PATH:
76
+
77
+ hk --heroku=/sbin/herkou logs f-p
78
+ # /sbin/heroku logs --tail --app=foo-production
79
+
80
+ And, you can skip all the magic if you like:
81
+
82
+ hk -n --heroku=/sbin/herkou pg:info --app=hellblazer
83
+ # sbin/herkou pg:info --app=hellblazer
84
+
85
+ Note that because this is backed by GLI, you only need to specify the command-name with enough characters to disambiguate it. For example, given the three builtins, these two are equivalent:
86
+
87
+
88
+ hk logs f-p
89
+ hk l f-p
90
+
91
+ ## AUTHOR
92
+
93
+ David Copeland, davec (at) naildrivin5.com
94
+
95
+ ## COPYRIGHT
96
+
97
+ hk is copyright(c) 2012 by David Copeland, released under the Apache license.
98
+
99
+ ## SEE ALSO
100
+
101
+ * Source on github: https://github.com/davetron5000/hk
@@ -0,0 +1,86 @@
1
+ require 'test_helper'
2
+
3
+ class DefaultTest < Clean::Test::TestCase
4
+
5
+ test_that "we can derive the app name when it's unambiguous" do
6
+ Given {
7
+ @existing_apps = %w(foo-production foo-staging blah-production)
8
+ }
9
+ Given :an_app_name_deriver
10
+ When {
11
+ @app_name = @app_name_deriver.derive("f-p")
12
+ }
13
+ Then {
14
+ assert_equal @app_name,"foo-production"
15
+ }
16
+ end
17
+
18
+ test_that "if there are no matches, we get an exception" do
19
+ Given {
20
+ @existing_apps = %w(foo-production foo-staging blah-production)
21
+ }
22
+ Given :an_app_name_deriver
23
+ When {
24
+ @code = ->() { @app_name_deriver.derive("asdfasdf") }
25
+ }
26
+ Then {
27
+ exception = assert_raises(RuntimeError,&@code)
28
+ assert_match exception.message,/No such app/
29
+ }
30
+ end
31
+
32
+ test_that "if there are multiple matches, we get an exception" do
33
+ Given {
34
+ @existing_apps = %w(foo-production foo-staging foobar-production)
35
+ }
36
+ Given :an_app_name_deriver
37
+ When {
38
+ @code = ->() { @app_name_deriver.derive("f-p") }
39
+ }
40
+ Then {
41
+ exception = assert_raises(RuntimeError,&@code)
42
+ assert_match exception.message,/Ambiguous app name/
43
+ }
44
+ end
45
+
46
+ test_that "an exact match always succeeds" do
47
+ Given {
48
+ @existing_apps = %w(foo-production foobar-production)
49
+ }
50
+ Given :an_app_name_deriver
51
+ When {
52
+ @app_name = @app_name_deriver.derive("foo-production")
53
+ }
54
+ Then {
55
+ assert_equal @app_name,"foo-production"
56
+ }
57
+ end
58
+
59
+ test_that "a match without the environment works" do
60
+ Given {
61
+ @existing_apps = %w(foo-production foobar)
62
+ }
63
+ Given :an_app_name_deriver
64
+ When {
65
+ @app_name = @app_name_deriver.derive("f")
66
+ }
67
+ Then {
68
+ assert_equal @app_name,"foobar"
69
+ }
70
+ end
71
+
72
+ private
73
+
74
+ def an_app_name_deriver
75
+ @app_name_deriver = Hk::AppNameDeriver.new(FakeLister.new(@existing_apps))
76
+ end
77
+
78
+ class FakeLister
79
+ def initialize(apps)
80
+ @apps = apps
81
+ end
82
+ def list_apps(*)
83
+ @apps
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,11 @@
1
+ require 'test/unit'
2
+ require 'clean_test/test_case'
3
+ require 'hk'
4
+
5
+ # Add test libraries you want to use here, e.g. mocha
6
+
7
+ class Test::Unit::TestCase
8
+
9
+ # Add global extensions to the test case class here
10
+
11
+ end
metadata ADDED
@@ -0,0 +1,232 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Your Name Here
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: clean_test
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rdoc
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: aruba
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: ronn
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: gli
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '2'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '2'
110
+ - !ruby/object:Gem::Dependency
111
+ name: gem-man
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description:
127
+ email: your@email.address.com
128
+ executables:
129
+ - !binary |-
130
+ aGs=
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - !binary |-
135
+ LmdpdGlnbm9yZQ==
136
+ - !binary |-
137
+ LnJ1YnktZ2Vtc2V0
138
+ - !binary |-
139
+ LnJ1YnktdmVyc2lvbg==
140
+ - !binary |-
141
+ R2VtZmlsZQ==
142
+ - !binary |-
143
+ R2VtZmlsZS5sb2Nr
144
+ - !binary |-
145
+ UkVBRE1FLm1k
146
+ - !binary |-
147
+ UmFrZWZpbGU=
148
+ - !binary |-
149
+ YmluL2hr
150
+ - !binary |-
151
+ ZmVhdHVyZXMvaGsuZmVhdHVyZQ==
152
+ - !binary |-
153
+ ZmVhdHVyZXMvc3RlcF9kZWZpbml0aW9ucy9oa19zdGVwcy5yYg==
154
+ - !binary |-
155
+ ZmVhdHVyZXMvc3VwcG9ydC9lbnYucmI=
156
+ - !binary |-
157
+ ZmVhdHVyZXMvc3VwcG9ydC9mYWtlX2hlcm9rdS5yYg==
158
+ - !binary |-
159
+ aGsuZ2Vtc3BlYw==
160
+ - !binary |-
161
+ bGliL2hrLnJi
162
+ - !binary |-
163
+ bGliL2hrL2FwcF9saXN0ZXIucmI=
164
+ - !binary |-
165
+ bGliL2hrL2FwcF9uYW1lX2Rlcml2ZXIucmI=
166
+ - !binary |-
167
+ bGliL2hrL2dsb2JhbF9vcHRpb25zLnJi
168
+ - !binary |-
169
+ bGliL2hrL2hlcm9rdV9jb21tYW5kLnJi
170
+ - !binary |-
171
+ bGliL2hrL2hlcm9rdV9ydW5uZXIucmI=
172
+ - !binary |-
173
+ bGliL2hrL3ZlcnNpb24ucmI=
174
+ - !binary |-
175
+ bGliL3Jha2UvYnVuZGxlci5yYg==
176
+ - !binary |-
177
+ bGliL3Jha2UvY3VjdW1iZXIucmI=
178
+ - !binary |-
179
+ bGliL3Jha2UvbWFuLnJi
180
+ - !binary |-
181
+ bGliL3Jha2UvdGVzdC5yYg==
182
+ - !binary |-
183
+ bWFuL2hrLjE=
184
+ - !binary |-
185
+ bWFuL2hrLjEucm9ubg==
186
+ - !binary |-
187
+ dGVzdC9hcHBfbmFtZV9kZXJpdmVyX3Rlc3QucmI=
188
+ - !binary |-
189
+ dGVzdC90ZXN0X2hlbHBlci5yYg==
190
+ homepage: http://your.website.com
191
+ licenses: []
192
+ post_install_message:
193
+ rdoc_options: []
194
+ require_paths:
195
+ - lib
196
+ required_ruby_version: !ruby/object:Gem::Requirement
197
+ none: false
198
+ requirements:
199
+ - - ! '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ segments:
203
+ - 0
204
+ hash: -3261507201375033656
205
+ required_rubygems_version: !ruby/object:Gem::Requirement
206
+ none: false
207
+ requirements:
208
+ - - ! '>='
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ segments:
212
+ - 0
213
+ hash: -3261507201375033656
214
+ requirements: []
215
+ rubyforge_project:
216
+ rubygems_version: 1.8.25
217
+ signing_key:
218
+ specification_version: 3
219
+ summary: A description of your project
220
+ test_files:
221
+ - !binary |-
222
+ ZmVhdHVyZXMvaGsuZmVhdHVyZQ==
223
+ - !binary |-
224
+ ZmVhdHVyZXMvc3RlcF9kZWZpbml0aW9ucy9oa19zdGVwcy5yYg==
225
+ - !binary |-
226
+ ZmVhdHVyZXMvc3VwcG9ydC9lbnYucmI=
227
+ - !binary |-
228
+ ZmVhdHVyZXMvc3VwcG9ydC9mYWtlX2hlcm9rdS5yYg==
229
+ - !binary |-
230
+ dGVzdC9hcHBfbmFtZV9kZXJpdmVyX3Rlc3QucmI=
231
+ - !binary |-
232
+ dGVzdC90ZXN0X2hlbHBlci5yYg==