clintegracon 0.4.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +158 -0
- data/Rakefile +60 -0
- data/clintegracon.gemspec +34 -0
- data/lib/CLIntegracon.rb +9 -0
- data/lib/CLIntegracon/adapter/bacon.rb +208 -0
- data/lib/CLIntegracon/configuration.rb +67 -0
- data/lib/CLIntegracon/diff.rb +81 -0
- data/lib/CLIntegracon/file_tree_spec.rb +213 -0
- data/lib/CLIntegracon/file_tree_spec_context.rb +180 -0
- data/lib/CLIntegracon/formatter.rb +77 -0
- data/lib/CLIntegracon/subject.rb +128 -0
- data/lib/CLIntegracon/version.rb +3 -0
- data/spec/bacon/execution_output.txt +72 -0
- data/spec/bacon/spec_helper.rb +60 -0
- data/spec/fixtures/bin/coffeemaker.rb +58 -0
- data/spec/integration/coffeemaker_help/after/execution_output.txt +23 -0
- data/spec/integration/coffeemaker_help/before/.gitkeep +0 -0
- data/spec/integration/coffeemaker_no_milk/after/BlackEye.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/CaPheSuaDa.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/Coffeemakerfile.yml +5 -0
- data/spec/integration/coffeemaker_no_milk/after/RedTux.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/execution_output.txt +6 -0
- data/spec/integration/coffeemaker_no_milk/before/Coffeemakerfile.yml +5 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/Affogato.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/BlackEye.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/Coffeemakerfile.yml +3 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/RedTux.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/execution_output.txt +4 -0
- data/spec/integration/coffeemaker_sweetner_honey/before/Coffeemakerfile.yml +3 -0
- data/spec/unit/adapter/bacon_spec.rb +187 -0
- data/spec/unit/configuration_spec.rb +72 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
data.tar.gz: 3bdace78261ca262aa53f9124c582f7e17adbf91
|
4
|
+
metadata.gz: ce8b35a77767eefa0b8e5f518edd292225bbf1cb
|
5
|
+
SHA512:
|
6
|
+
data.tar.gz: 4c055116f2d3f625bd423d2f28c11ebf808eac6983ddd19a3d52f303b011489dafffac365e81afdc35326cdf560ed4c70735b0925a773b8d97e7cd2842a161ea
|
7
|
+
metadata.gz: daca9326fc434414d5ed63836b3b743538dd56cb5ec8f49640a4171afc69a272c0b470e3a9f96d49d83587932ad1a6b7f9f3637bf6f98ee7cc3e97473d04ae07
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Marius Rackwitz
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# CLIntegracon
|
2
|
+
|
3
|
+
[](http://rubygems.org/gems/clintegracon)
|
4
|
+
[](https://travis-ci.org/mrackwitz/CLIntegracon)
|
5
|
+
[](https://codeclimate.com/github/mrackwitz/CLIntegracon)
|
6
|
+
[](https://codeclimate.com/github/mrackwitz/CLIntegracon)
|
7
|
+
|
8
|
+
CLIntegracon allows you to build *Integration* specs for your *CLI*,
|
9
|
+
independent if they are based on Ruby or another technology.
|
10
|
+
It is especially useful if your command modifies the file system.
|
11
|
+
Furthermore it provides an integration for *Bacon*.
|
12
|
+
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'clintegracon'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install clintegracon
|
27
|
+
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
This description assumes the following file system layout of your CLI project.
|
32
|
+
This is not fixed, but if yours differ, you have to change paths accordingly.
|
33
|
+
|
34
|
+
```
|
35
|
+
─┬─/ (root)
|
36
|
+
│
|
37
|
+
├─┬─spec
|
38
|
+
│ ├───spec_helper.rb
|
39
|
+
│ └─┬─integration
|
40
|
+
│ ├─┬─arg1
|
41
|
+
│ │ ├─┬─before
|
42
|
+
│ │ │ ├───source.h
|
43
|
+
│ │ │ └───source.c
|
44
|
+
│ │ └─┬─after
|
45
|
+
│ │ ├───execution_output.txt
|
46
|
+
│ │ ├───source.h
|
47
|
+
│ │ ├───source.c
|
48
|
+
│ │ └───source.o
|
49
|
+
│ └─┬─arg2
|
50
|
+
│ ├─┬─before
|
51
|
+
│ │ …
|
52
|
+
│ └─┬─after
|
53
|
+
│ …
|
54
|
+
└───tmp
|
55
|
+
```
|
56
|
+
|
57
|
+
### Bacon
|
58
|
+
|
59
|
+
1. Include CLIntegracon in your *spec_helper.rb*
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
require 'CLIntegracon'
|
63
|
+
```
|
64
|
+
|
65
|
+
2. Setup the basic configuration and hook into Bacon as test framework:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
CLIntegracon.configure do |c|
|
69
|
+
c.context.spec_path = File.expand_path('../integration', __FILE__)
|
70
|
+
c.context.temp_path = File.expand_path('../../tmp', __FILE__)
|
71
|
+
|
72
|
+
c.hook_into :bacon
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
3. Describe your specs with the extended DSL:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
# Ensure that all the helpers are included in this context
|
80
|
+
describe_cli 'coffee-maker' do
|
81
|
+
|
82
|
+
# Setup our subject
|
83
|
+
subject do |s|
|
84
|
+
# Provide a display name (optional, default would be 'subject')
|
85
|
+
s.name = 'coffee-maker'
|
86
|
+
|
87
|
+
# Provide the real command line (required)
|
88
|
+
s.executable = 'bundle exec ruby spec/fixtures/bin/coffeemaker.rb"'
|
89
|
+
|
90
|
+
# Set environments variables needed on execution
|
91
|
+
s.environment_vars = {
|
92
|
+
'COFFEE_MAKER_FILE' => 'Coffeemakerfile.yml'
|
93
|
+
}
|
94
|
+
|
95
|
+
# Define default arguments
|
96
|
+
s.default_args = [
|
97
|
+
'--verbose',
|
98
|
+
'--no-ansi'
|
99
|
+
]
|
100
|
+
|
101
|
+
# Replace special paths in execution output by a placeholder, so that the
|
102
|
+
# compared outputs doesn't differ dependent on the absolute location where
|
103
|
+
# your tested CLI was executed.
|
104
|
+
s.has_special_path ROOT.to_s, 'ROOT'
|
105
|
+
end
|
106
|
+
|
107
|
+
context do |c|
|
108
|
+
# Ignore certain files ...
|
109
|
+
c.ignores '.gitkeep'
|
110
|
+
|
111
|
+
# ... or explicitly ignore all hidden files. (While the default is that they
|
112
|
+
# are included in the file tree diff.)
|
113
|
+
c.include_hidden_files = true
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'Brew recipes' do
|
117
|
+
|
118
|
+
describe 'without milk' do
|
119
|
+
# +behaves_like+ is provided by bacon.
|
120
|
+
# +cli_spec+ expects as first argument the directory of the spec, and
|
121
|
+
# as second argument the arguments passed to the subject on launch.
|
122
|
+
# The defined default arguments will be appended after that.
|
123
|
+
# If you need to append arguments after the default arguments, because
|
124
|
+
# of the way your command line interface is defined and how its option
|
125
|
+
# parser works, you can pass them as third argument to +cli_spec+.
|
126
|
+
behaves_like cli_spec('coffeemaker_no_milk', '--no-milk')
|
127
|
+
|
128
|
+
# Implementation details:
|
129
|
+
# +cli_spec+ will define on-the-fly a new shared set of expectations
|
130
|
+
# and will return its name and pass it to +behaves_like+, so that it
|
131
|
+
# will be immediately executed.
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'with honey as sweetner' do
|
135
|
+
behaves_like cli_spec('coffeemaker_sweetner_honey', '--sweetner=honey')
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
describe 'Get help' do
|
141
|
+
behaves_like cli_spec('coffeemaker_help', '--help')
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
4. Profit
|
148
|
+
|
149
|
+

|
150
|
+
|
151
|
+
|
152
|
+
## Contributing
|
153
|
+
|
154
|
+
1. Fork it
|
155
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
156
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
157
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
158
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler/gem_tasks'
|
5
|
+
require 'colored'
|
6
|
+
|
7
|
+
namespace :spec do
|
8
|
+
|
9
|
+
task :prepare do
|
10
|
+
verbose false
|
11
|
+
puts 'Prepare …'
|
12
|
+
sh 'mkdir -p tmp'
|
13
|
+
rm_rf 'tmp/*'
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Run the bacon integration spec'
|
17
|
+
task :bacon_integration => [:prepare] do
|
18
|
+
verbose false
|
19
|
+
sh 'rake spec:bacon_integration_runner > tmp/bacon_execution_output.txt' do; end
|
20
|
+
puts 'Run bacon spec …'
|
21
|
+
sh 'diff spec/bacon/execution_output.txt tmp/bacon_execution_output.txt' do |ok, res|
|
22
|
+
if ok
|
23
|
+
puts '✓ Spec for bacon passed.'.green
|
24
|
+
else
|
25
|
+
puts '✗ Spec for bacon failed.'.red
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Run the tasks for bacon integration spec verbose and without any outer expectations'
|
31
|
+
task :bacon_integration_runner do
|
32
|
+
sh [
|
33
|
+
'bundle exec bacon spec/bacon/spec_helper.rb',
|
34
|
+
'sed -e "s|$(echo $GEM_HOME)|\$GEM_HOME|g"',
|
35
|
+
'sed -e "s|$(dirname ~/.)|\$HOME|g"'
|
36
|
+
].join " | "
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Run all integration specs'
|
40
|
+
task :integration => [
|
41
|
+
'spec:bacon_integration'
|
42
|
+
]
|
43
|
+
|
44
|
+
desc 'Run all unit specs'
|
45
|
+
task :unit do
|
46
|
+
sh "bundle exec bacon #{specs('unit/**/*')}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def specs(dir)
|
50
|
+
FileList["spec/#{dir}_spec.rb"].shuffle.join(' ')
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'Run all specs'
|
54
|
+
task :all => [:unit, :integration]
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'Run all specs'
|
59
|
+
task :spec => 'spec:all'
|
60
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'CLIntegracon/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "clintegracon"
|
8
|
+
spec.version = CLIntegracon::VERSION
|
9
|
+
spec.authors = ["Marius Rackwitz"]
|
10
|
+
spec.email = ["git@mariusrackwitz.de"]
|
11
|
+
spec.homepage = "https://github.com/mrackwitz/CLIntegracon"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.summary = "Integration specs for your CLI"
|
15
|
+
spec.description = "CLIntegracon allows you to build Integration specs for your CLI," \
|
16
|
+
"independent if they are based on Ruby or another technology." \
|
17
|
+
"It is especially useful if your command modifies the file system." \
|
18
|
+
"It provides an integration for Bacon."
|
19
|
+
|
20
|
+
spec.files = `git ls-files`.split($/)
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake", '~> 10.1.0' # Ruby 1.8.7
|
27
|
+
spec.add_development_dependency "bacon"
|
28
|
+
spec.add_development_dependency "mocha", "~> 1.0.0" # Ruby 1.8.7
|
29
|
+
spec.add_development_dependency "mocha-on-bacon"
|
30
|
+
spec.add_development_dependency "claide" # Example CLI
|
31
|
+
|
32
|
+
spec.add_runtime_dependency 'colored', '~> 1.2'
|
33
|
+
spec.add_runtime_dependency 'diffy'
|
34
|
+
end
|
data/lib/CLIntegracon.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'colored'
|
2
|
+
|
3
|
+
# Layout structure
|
4
|
+
module CLIntegracon
|
5
|
+
module Adapter
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Define concrete adapter
|
10
|
+
module CLIntegracon::Adapter::Bacon
|
11
|
+
module Context
|
12
|
+
|
13
|
+
# Get or configure the current subject
|
14
|
+
#
|
15
|
+
# @note On first call this will create a new subject on base of the
|
16
|
+
# shared configuration and store it in the ivar `@subject`.
|
17
|
+
#
|
18
|
+
# @param [Block<(Subject) -> ()>]
|
19
|
+
# This block, if given, will be evaluated on the caller.
|
20
|
+
# It receives as first argument the subject itself.
|
21
|
+
#
|
22
|
+
# @return [Subject]
|
23
|
+
# the subject
|
24
|
+
#
|
25
|
+
def subject &block
|
26
|
+
@subject ||= CLIntegracon::shared_config.subject.dup
|
27
|
+
return @subject if block.nil?
|
28
|
+
instance_exec(@subject, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get or configure the current context
|
32
|
+
#
|
33
|
+
# @note On first call this will create a new context on base of the
|
34
|
+
# shared configuration and store it in the ivar `@context`.
|
35
|
+
#
|
36
|
+
# @param [Block<(FileTreeSpecContext) -> ()>]
|
37
|
+
# This block, if given, will be evaluated on the caller.
|
38
|
+
# It receives as first argument the context itself.
|
39
|
+
#
|
40
|
+
# @return [FileTreeSpecContext]
|
41
|
+
# the spec context, will be lazily created if not already present.
|
42
|
+
#
|
43
|
+
def context &block
|
44
|
+
@context ||= CLIntegracon.shared_config.context.dup
|
45
|
+
return @context if block.nil?
|
46
|
+
instance_exec(@context, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Works like `behaves_like`, but takes arguments for the shared example
|
50
|
+
#
|
51
|
+
# @param [String] name
|
52
|
+
# name of the shared context.
|
53
|
+
#
|
54
|
+
# @param [...] args
|
55
|
+
# params to pass to the shared context
|
56
|
+
#
|
57
|
+
def behaves_like_a(name, *args)
|
58
|
+
instance_exec(*args, &Bacon::Shared[name])
|
59
|
+
end
|
60
|
+
|
61
|
+
# Ad-hoc defines a set of shared expectations to be consumed directly by `behaves_like`.
|
62
|
+
# See the following example for usage:
|
63
|
+
#
|
64
|
+
# behaves_like cli_spec('my_spec_dir', 'install --verbose')
|
65
|
+
#
|
66
|
+
# @note This expects that a method `context` is defined, which is returning an
|
67
|
+
# instance of {FileTreeSpecContext}.
|
68
|
+
#
|
69
|
+
# @param [String] spec_dir
|
70
|
+
# the concrete directory of the spec, see {file_spec}.
|
71
|
+
#
|
72
|
+
# @param [String] args
|
73
|
+
# the additional arguments to pass on launch to {CLIntegracon::Subject}.
|
74
|
+
#
|
75
|
+
# @return [String]
|
76
|
+
# name of the set of shared expectations
|
77
|
+
#
|
78
|
+
def cli_spec(spec_dir, *args)
|
79
|
+
file_spec spec_dir do
|
80
|
+
output = subject.launch(*args)
|
81
|
+
status = $?
|
82
|
+
|
83
|
+
it "$ #{subject.name} #{args.join(' ')}" do
|
84
|
+
status.should.satisfy("Binary failed\n\n#{output}") do
|
85
|
+
status.success?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Ad-hoc defines a set of shared expectations to be consumed directly by `behaves_like`.
|
92
|
+
# See the following example for usage:
|
93
|
+
#
|
94
|
+
# behaves_like file_spec('my_spec_dir') do
|
95
|
+
# # do some changes to the current dir
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# @note This expects that a method `context` is defined, which is returning an
|
99
|
+
# instance of {FileTreeSpecContext}.
|
100
|
+
#
|
101
|
+
# @param [String] spec_dir
|
102
|
+
# the concrete directory of the spec to be passed to
|
103
|
+
# {FileTreeSpecContext.spec}
|
104
|
+
#
|
105
|
+
# @param [Block<() -> ()>] block
|
106
|
+
# the block which will be executed after the before state is laid out in the
|
107
|
+
# temporary directory, which normally will make modifications to file system,
|
108
|
+
# which will be compare to the state given in the after directory.
|
109
|
+
#
|
110
|
+
# @return [String]
|
111
|
+
# name of the set of shared expectations
|
112
|
+
#
|
113
|
+
def file_spec(spec_dir, &block)
|
114
|
+
raise ArgumentError.new("Spec directory is missing!") if spec_dir.nil?
|
115
|
+
|
116
|
+
shared_name = spec_dir
|
117
|
+
|
118
|
+
shared shared_name do
|
119
|
+
context.spec(spec_dir).run do |spec|
|
120
|
+
instance_eval &block
|
121
|
+
|
122
|
+
spec.compare do |diff|
|
123
|
+
it diff.relative_path.to_s do
|
124
|
+
diff.produced.should.satisfy(spec.formatter.describe_missing_file(diff.relative_path)) do
|
125
|
+
diff.produced.exist?
|
126
|
+
end
|
127
|
+
|
128
|
+
diff.produced.should.satisfy(spec.formatter.describe_file_diff(diff)) do
|
129
|
+
diff.is_equal?
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
spec.check_unexpected_files do |files|
|
135
|
+
it "should not produce unexpected files" do
|
136
|
+
files.should.satisfy(spec.formatter.describe_unexpected_files(files)) do
|
137
|
+
files.size == 0
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
shared_name
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
# Describe a command line interface
|
150
|
+
# This method basically behaves like {Bacon::Context.describe}, but it provides
|
151
|
+
# automatically the methods #subject, #context, #cli_spec and #file_spec.
|
152
|
+
#
|
153
|
+
# @param [String] subject_name
|
154
|
+
# the subject name will be used as first argument to initialize
|
155
|
+
# a new {CLIntegracon::Subject}, which will be accessible in the
|
156
|
+
# spec by #subject.
|
157
|
+
#
|
158
|
+
# @param [Hash<Symbol,String>] context_options
|
159
|
+
# the options to configure this spec context, could be one or more of:
|
160
|
+
# * :executable: the executable used to initialize {CLIntegracon::Subject}
|
161
|
+
# if not given, will fallback to param {subject_name}.
|
162
|
+
#
|
163
|
+
# @param [Block<() -> ()>] block
|
164
|
+
# the block to provide further sub-specs or requirements, as
|
165
|
+
# known from {Bacon::Context.describe}
|
166
|
+
#
|
167
|
+
def describe_cli(subject_name, context_options = {}, &block)
|
168
|
+
context = describe subject_name do
|
169
|
+
# Make Context methods available
|
170
|
+
# WORKAROUND: Bacon auto-inherits singleton methods to child contexts
|
171
|
+
# by using the runtime and won't include methods in modules included
|
172
|
+
# by the parent context. We have to ensure that the methods will be
|
173
|
+
# accessible by the child contexts by defining them as singleton methods.
|
174
|
+
extended = self.extend Context
|
175
|
+
Context.instance_methods.each do |method|
|
176
|
+
class << self; self end.instance_eval do
|
177
|
+
unbound_method = extended.method(method).unbind
|
178
|
+
|
179
|
+
send :define_method, method do |*args, &b|
|
180
|
+
unbound_method.bind(self).call(*args, &b)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
subject do |s|
|
186
|
+
s.name = subject_name
|
187
|
+
s.executable = context_options[:executable] || subject_name
|
188
|
+
end
|
189
|
+
|
190
|
+
instance_eval &block
|
191
|
+
end
|
192
|
+
|
193
|
+
Bacon::ErrorLog.gsub! %r{^.*lib/CLIntegracon/.*\n}, ''
|
194
|
+
|
195
|
+
context
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
# Make #describe_cli global available
|
201
|
+
extend CLIntegracon::Adapter::Bacon
|
202
|
+
|
203
|
+
# Patch Bacon::Context to support #describe_cli
|
204
|
+
module Bacon
|
205
|
+
class Context
|
206
|
+
include CLIntegracon::Adapter::Bacon
|
207
|
+
end
|
208
|
+
end
|