crab 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.
- data/.gitignore +7 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Rakefile +9 -0
- data/bin/crab +5 -0
- data/crab.gemspec +33 -0
- data/features/find-text-in-stories.feature +31 -0
- data/features/list-from-rally.feature +27 -0
- data/features/login-and-out-of-rally.feature +14 -0
- data/features/project-selection.feature +27 -0
- data/features/pull-from-rally-into-cucumber.feature +28 -0
- data/features/show-from-rally.feature +25 -0
- data/features/steps/rally_steps.rb +94 -0
- data/features/subcommand-help.feature +84 -0
- data/features/support/aruba.rb +14 -0
- data/features/update-story-in-rally.feature +33 -0
- data/lib/crab.rb +19 -0
- data/lib/crab/cli.rb +112 -0
- data/lib/crab/cucumber_feature.rb +17 -0
- data/lib/crab/cucumber_scenario.rb +11 -0
- data/lib/crab/find.rb +30 -0
- data/lib/crab/list.rb +29 -0
- data/lib/crab/login.rb +25 -0
- data/lib/crab/project.rb +39 -0
- data/lib/crab/pull.rb +32 -0
- data/lib/crab/rally.rb +50 -0
- data/lib/crab/scenario.rb +23 -0
- data/lib/crab/show.rb +17 -0
- data/lib/crab/story.rb +63 -0
- data/lib/crab/update.rb +68 -0
- data/lib/crab/utilities.rb +18 -0
- data/lib/crab/version.rb +3 -0
- data/old/.gitignore +1 -0
- data/old/Gemfile +14 -0
- data/old/Rakefile +277 -0
- data/old/templates/feature-pt.mustache +5 -0
- data/old/templates/feature.mustache +4 -0
- metadata +230 -0
data/lib/crab/update.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Crab
|
2
|
+
class Update
|
3
|
+
def initialize(global_opts, cmd_opts, args)
|
4
|
+
@global_opts = global_opts
|
5
|
+
@cmd_opts = cmd_opts
|
6
|
+
@args = args
|
7
|
+
|
8
|
+
@rally = Rally.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
opts = validate_and_fix_up_arguments
|
13
|
+
|
14
|
+
@rally.connect
|
15
|
+
|
16
|
+
story = @rally.find_story_with_id @args.first
|
17
|
+
opts = validate_and_fix_up_arguments_with_rally(story, opts)
|
18
|
+
|
19
|
+
story.update opts
|
20
|
+
|
21
|
+
puts "#{story.formatted_id}: #{story.name} (#{story.state})"
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_and_fix_up_arguments
|
25
|
+
Trollop::die "No story given" if @args.empty?
|
26
|
+
Trollop::die "Nothing to update. Please provide some options" unless @cmd_opts.any? {|k, v| k.to_s =~ /_given$/ }
|
27
|
+
|
28
|
+
opts = {}
|
29
|
+
opts[:name] = @cmd_opts[:name] if @cmd_opts[:name_given]
|
30
|
+
opts[:schedule_state] = state_from(@cmd_opts[:state]) if @cmd_opts[:state_given]
|
31
|
+
|
32
|
+
if @cmd_opts[:estimate_given]
|
33
|
+
opts[:plan_estimate] = @cmd_opts[:estimate] # nobody is going to remember "Plan Estimate", really
|
34
|
+
end
|
35
|
+
|
36
|
+
opts[:blocked] = @cmd_opts[:blocked] if @cmd_opts[:blocked_given]
|
37
|
+
opts[:blocked] = !@cmd_opts[:unblocked] if @cmd_opts[:unblocked_given]
|
38
|
+
|
39
|
+
opts
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_and_fix_up_arguments_with_rally(story, opts)
|
43
|
+
if @cmd_opts[:iteration_given]
|
44
|
+
opts[:iteration] = @rally.find_iteration_by_name @cmd_opts[:iteration]
|
45
|
+
Trollop::die "Unknown iteration \"#{@cmd_opts[:iteration]}\"" if opts[:iteration].nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
if @cmd_opts[:release_given]
|
49
|
+
opts[:release] = @rally.find_release_by_name @cmd_opts[:release]
|
50
|
+
Trollop::die "Unknown release \"#{@cmd_opts[:release]}\"" if opts[:release].nil?
|
51
|
+
end
|
52
|
+
|
53
|
+
if @cmd_opts[:parent_given]
|
54
|
+
opts[:parent] = @rally.find_story_with_id(@cmd_opts[:parent]).rally_object
|
55
|
+
Trollop::die "Unknown story \"#{@cmd_opts[:parent]}\"" if opts[:parent].nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
opts[:name] = story.name if opts[:name].blank?
|
59
|
+
opts
|
60
|
+
end
|
61
|
+
|
62
|
+
def state_from(option)
|
63
|
+
fixed_option = option.gsub(/(^\w|[-_]\w)/) { $1.upcase.gsub(/_/, '-') }
|
64
|
+
Trollop::die :state, "has an invalid value" unless Crab::Story::VALID_STATES.include? fixed_option
|
65
|
+
fixed_option
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Crab
|
2
|
+
module Utilities
|
3
|
+
def credentials_file
|
4
|
+
File.expand_path("~/.rally_credentials")
|
5
|
+
end
|
6
|
+
|
7
|
+
def valid_credentials_file
|
8
|
+
Trollop::die "Please log in first" unless File.exists? credentials_file
|
9
|
+
credentials_file
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid_project_name(cmd_opts)
|
13
|
+
project_name = cmd_opts[:project_given] ? cmd_opts[:project] : Crab::Project.current_project_name
|
14
|
+
Trollop::die :project, "must be specified" if project_name.blank?
|
15
|
+
project_name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/crab/version.rb
ADDED
data/old/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
./crab/Gemfile.lock
|
data/old/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem 'rally_rest_api'
|
4
|
+
gem 'cucumber'
|
5
|
+
gem 'rake'
|
6
|
+
|
7
|
+
gem 'highline', :require => 'highline/import' # password prompts, etc
|
8
|
+
gem 'pry' # irb-like awesomeness for debugging
|
9
|
+
gem 'activesupport', :require => 'active_support/all' # nice additions to Ruby API
|
10
|
+
gem 'i18n'
|
11
|
+
|
12
|
+
gem 'mustache' # for file templates
|
13
|
+
gem 'sanitize' # clean up description HTML
|
14
|
+
|
data/old/Rakefile
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
Bundler.require :default
|
5
|
+
|
6
|
+
task :ensure_credentials_are_present do
|
7
|
+
ENV['RALLY_USERNAME'] ||= ask('Username: ')
|
8
|
+
ENV['RALLY_PASSWORD'] ||= ask('Password: ') {|q| q.echo = "*" }
|
9
|
+
end
|
10
|
+
|
11
|
+
task :rally => :ensure_credentials_are_present do
|
12
|
+
@rally = RallyRestAPI.new :username => ENV['RALLY_USERNAME'], :password => ENV['RALLY_PASSWORD']
|
13
|
+
puts "Logged in as #{@rally.user.login_name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
# a few things in this script break / do the wrong thing if you don't have
|
17
|
+
# the project set up or don't have access to it
|
18
|
+
task :project => :rally do
|
19
|
+
project = ENV['RALLY_PROJECT'] ||= ask('Project: ')
|
20
|
+
@project = @rally.find(:project) { equal :name, ENV['RALLY_PROJECT']}.first
|
21
|
+
puts "Using project \"#{ENV['RALLY_PROJECT']}\""
|
22
|
+
end
|
23
|
+
|
24
|
+
task :default => :rally do
|
25
|
+
binding.pry
|
26
|
+
end
|
27
|
+
|
28
|
+
class Feature < Mustache
|
29
|
+
|
30
|
+
self.template_path = File.join(File.dirname(__FILE__), 'templates')
|
31
|
+
|
32
|
+
# didn't like what's being done to support multiple languages
|
33
|
+
# this is going to break horribly when we try to add supoort for generating the
|
34
|
+
# scenarios from Rally and the whole roundtripping thing
|
35
|
+
# (as it'll be difficult to use partials, for example)
|
36
|
+
def initialize(rally_story, language=(ENV['CUCUMBER_LANG'] || ''))
|
37
|
+
@delegate = rally_story
|
38
|
+
self.class.template_file = File.join(self.class.template_path, "feature-#{language}.mustache") if language.present?
|
39
|
+
end
|
40
|
+
|
41
|
+
def name
|
42
|
+
@delegate.name
|
43
|
+
end
|
44
|
+
|
45
|
+
def formatted_id
|
46
|
+
@delegate.formatted_i_d
|
47
|
+
end
|
48
|
+
|
49
|
+
def state
|
50
|
+
(@delegate.schedule_state || "unknown").parameterize.underscore
|
51
|
+
end
|
52
|
+
|
53
|
+
def description
|
54
|
+
# this could use a lot of rethinking :(
|
55
|
+
# biggest problem is that Cucumber breaks if text in description looks like something
|
56
|
+
# it might know how to parse, but doesn't
|
57
|
+
# our feature descriptions are quite like that, so I was being ultra-conservative
|
58
|
+
sanitize(@delegate.description || '').gsub(/ +/, "\n").gsub(/\n\n/, "\n").gsub(/\n/, "\n ")
|
59
|
+
end
|
60
|
+
|
61
|
+
def file_name
|
62
|
+
"#{formatted_id}_#{name.parameterize.underscore}.feature"
|
63
|
+
end
|
64
|
+
|
65
|
+
def dir_name
|
66
|
+
state
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
task :generate_features => :project do
|
72
|
+
project = @project
|
73
|
+
@stories = @rally.find(:hierarchical_requirement, :fetch => true) { equal :project, project }
|
74
|
+
|
75
|
+
@stories.each do |story|
|
76
|
+
feature = Feature.new(story)
|
77
|
+
|
78
|
+
dir = File.join("features", feature.dir_name)
|
79
|
+
file = File.join(dir, feature.file_name)
|
80
|
+
|
81
|
+
FileUtils.mkdir_p dir
|
82
|
+
FileUtils.touch file
|
83
|
+
|
84
|
+
File.open(file, 'w') do |f|
|
85
|
+
f.write feature.render
|
86
|
+
end
|
87
|
+
putc '.'
|
88
|
+
end
|
89
|
+
puts
|
90
|
+
|
91
|
+
end
|
92
|
+
# this whole class should die and we should probably use
|
93
|
+
# a Cucumber formatter instead of relying directly on the Gherkin
|
94
|
+
# APIs. With the extra info that Cucumber provides, it should also
|
95
|
+
# be possible to generate Test Case Runs, which would be pretty sweet
|
96
|
+
# to track using charts in Rally -- eg "How often do I have to do a manual
|
97
|
+
# test run of the @manual stuff so it's all good?"
|
98
|
+
class FeatureProxy
|
99
|
+
def initialize
|
100
|
+
@scenarios = []
|
101
|
+
@steps = ActiveSupport::OrderedHash.new([])
|
102
|
+
end
|
103
|
+
|
104
|
+
attr_reader :scenarios, :steps
|
105
|
+
|
106
|
+
def name
|
107
|
+
@feature.name
|
108
|
+
end
|
109
|
+
|
110
|
+
def description
|
111
|
+
@feature.description
|
112
|
+
end
|
113
|
+
|
114
|
+
# the next two methods could be replaced with something like an extension to the cucumber syntax
|
115
|
+
# in which we annotate foreign IDs -- my proposal is something like what's in here:
|
116
|
+
#
|
117
|
+
# Feature: [XXXXX] Lorem Ipsum
|
118
|
+
#
|
119
|
+
# Simple regex, fairly hard to get wrong in almost any western keyboard, and should work for any bug/issue/card/story tracker I've seen
|
120
|
+
#
|
121
|
+
def rally_id
|
122
|
+
self.name.match(/^\[([^\]]+)\](.*)$/)
|
123
|
+
$1
|
124
|
+
end
|
125
|
+
|
126
|
+
def title_for_rally
|
127
|
+
self.name.match(/^\[([^\]]+)\](.*)$/)
|
128
|
+
$2.squish.titleize
|
129
|
+
end
|
130
|
+
|
131
|
+
# needed by Gherkin
|
132
|
+
def uri(uri)
|
133
|
+
end
|
134
|
+
|
135
|
+
def feature(feature)
|
136
|
+
@feature = feature
|
137
|
+
end
|
138
|
+
|
139
|
+
def get
|
140
|
+
@feature
|
141
|
+
end
|
142
|
+
|
143
|
+
def scenario(scenario)
|
144
|
+
@scenarios << scenario
|
145
|
+
end
|
146
|
+
|
147
|
+
def step(step)
|
148
|
+
@steps[@scenarios.last] += Array(step)
|
149
|
+
end
|
150
|
+
|
151
|
+
def eof
|
152
|
+
end
|
153
|
+
|
154
|
+
def has_story_id?
|
155
|
+
!!self.name.match(/^\[([^\]]+)\](.*)$/)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def parse(feature)
|
160
|
+
updater = FeatureProxy.new
|
161
|
+
parser = Gherkin::Parser::Parser.new(updater, false, "root", false)
|
162
|
+
parser.parse File.read(feature), feature, 0
|
163
|
+
updater
|
164
|
+
end
|
165
|
+
|
166
|
+
# took a while to figure out that we need to remove the CSS from inside embedded <style> tags!
|
167
|
+
# Rally uses some crazy rich text editor that I'd be soooooo happy to disable, somehow. Chrome Extension, perhaps?
|
168
|
+
def sanitize(source)
|
169
|
+
Sanitize.clean source, :remove_contents => %w{style}
|
170
|
+
end
|
171
|
+
|
172
|
+
task :update_features => :rally do
|
173
|
+
Dir['features/**/*.feature'].sort {|a,b| File.mtime(b) <=> File.mtime(a) }.each do |file|
|
174
|
+
feature = parse file
|
175
|
+
unless feature.has_story_id?
|
176
|
+
raise "Incompatible feature name: #{feature.name} in #{file} (needs to begin with a story number in square brackets)"
|
177
|
+
end
|
178
|
+
|
179
|
+
# TODO inline variables
|
180
|
+
feature_id = feature.rally_id
|
181
|
+
feature_name = feature.title_for_rally
|
182
|
+
|
183
|
+
story = @rally.find(:hierarchical_requirement) { equal :formatted_i_d, feature_id }.first
|
184
|
+
|
185
|
+
updates = {} # collect all updates first (room for double-checking and you only get to call Rally over the net once per item)
|
186
|
+
|
187
|
+
if story.name != feature_name
|
188
|
+
updates[:name] = feature_name
|
189
|
+
end
|
190
|
+
|
191
|
+
# matching the descriptions sucked -- Rally does some conversion to strip out HTML
|
192
|
+
rally_description = sanitize(story.description || '').gsub(/\s+/, ' ').strip
|
193
|
+
cuke_description = sanitize(feature.description || '').gsub(/\s+/, ' ').strip
|
194
|
+
|
195
|
+
if rally_description != cuke_description
|
196
|
+
formatted_description = (feature.description || '').strip.gsub(/\n/, '<br/>')
|
197
|
+
updates[:description] = formatted_description
|
198
|
+
end
|
199
|
+
#
|
200
|
+
|
201
|
+
if updates.empty?
|
202
|
+
puts "Nothing to do for #{feature_id} (story already up to date)"
|
203
|
+
else
|
204
|
+
story.update updates
|
205
|
+
puts "Updated #{feature_id}: #{story.name} (#{updates.keys.join(',')})"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
task :update_scenarios => :project do
|
211
|
+
Dir['features/**/*.feature'].sort {|a,b| File.mtime(b) <=> File.mtime(a) }.each do |file|
|
212
|
+
feature = parse file
|
213
|
+
feature.steps.each do |scenario, steps|
|
214
|
+
create_test_case @project, feature, scenario, steps
|
215
|
+
|
216
|
+
# TODO make the scenarios in the Cucumber file reflect what's in Rally as well
|
217
|
+
#
|
218
|
+
# goal is to have roundtrip editing, but if not whatever is in Cucumber is "the truth"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
TYPE_TAGS = %w{acceptance functional non-functional performance regression usability user_interface}
|
224
|
+
RISK_TAGS = %w{low_risk medium_risk high_risk}
|
225
|
+
PRIORITY_TAGS = %{useful important critical}
|
226
|
+
METHOD_TAGS = %{automated manual}
|
227
|
+
|
228
|
+
def create_test_case(project, feature, scenario, steps)
|
229
|
+
if !scenario.name.match /^\[([^\]]+)\](.*)$/
|
230
|
+
# uh oh, we have to create this scenario!
|
231
|
+
tags = scenario.tags.map {|t| t.name.gsub(/^@/, '') }
|
232
|
+
|
233
|
+
# could definitely use some cleaning up, but the defaults seem sensible so far
|
234
|
+
type_tag = (tags.find {|t| TYPE_TAGS.include? t } || 'acceptance').humanize
|
235
|
+
risk_tag = (tags.find {|t| RISK_TAGS.include? t } || 'medium_risk').gsub(/_risk/, '').humanize
|
236
|
+
priority_tag = (tags.find {|t| PRIORITY_TAGS.include? t } || 'important').humanize
|
237
|
+
method_tag = (tags.find {|t| METHOD_TAGS.include? t } || 'automated').humanize
|
238
|
+
|
239
|
+
story = @rally.find(:hierarchical_requirement) { equal :formatted_i_d, feature.rally_id }.first
|
240
|
+
|
241
|
+
options = {
|
242
|
+
:name => scenario.name,
|
243
|
+
:description => "Automatically updated by Cucumber, do not edit",
|
244
|
+
:type => type_tag,
|
245
|
+
:risk => risk_tag,
|
246
|
+
:priority => priority_tag,
|
247
|
+
:method => method_tag,
|
248
|
+
:pre_conditions => "N/A", # do scenarios have a header? BeforeScenario?
|
249
|
+
:post_conditions => "N/A", # ...and/or a footer? AfterScenario, perhaps?
|
250
|
+
:work_product => story,
|
251
|
+
:project => @project # linking testcase to story is not enough
|
252
|
+
}
|
253
|
+
|
254
|
+
test_case = @rally.create(:test_case, options) do |test_case|
|
255
|
+
steps.each_with_index do |step, i|
|
256
|
+
test_case_step = @rally.create(:test_case_step, :test_case => test_case, :index => i, :input => "#{step.keyword.strip} #{step.name.strip}")
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
puts "Created test case #{test_case.formatted_i_d} in story #{feature.rally_id}"
|
261
|
+
else
|
262
|
+
# TODO update scenario text in feature with ID from Rally
|
263
|
+
# then update other fields in Rally with our scenario fields / tags
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
desc "DANGER DANGER DANGER"
|
268
|
+
task :delete_all_test_cases => :project do
|
269
|
+
story_id = ENV['STORY'] ||= ask("Story #: ")
|
270
|
+
project = @project
|
271
|
+
story = @rally.find(:hierarchical_requirement) { equal :formatted_i_d, story_id }.first
|
272
|
+
test_cases = @rally.find(:test_case) { equal :work_product, story }
|
273
|
+
|
274
|
+
test_cases.each do |tc|
|
275
|
+
p tc.delete
|
276
|
+
end
|
277
|
+
end
|
metadata
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crab
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Carlos Villela
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-27 00:00:00 -03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: aruba
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: aruba
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: cucumber
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rally_rest_api
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: highline
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :runtime
|
90
|
+
version_requirements: *id005
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: activesupport
|
93
|
+
prerelease: false
|
94
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
type: :runtime
|
104
|
+
version_requirements: *id006
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: i18n
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
type: :runtime
|
118
|
+
version_requirements: *id007
|
119
|
+
- !ruby/object:Gem::Dependency
|
120
|
+
name: sanitize
|
121
|
+
prerelease: false
|
122
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
hash: 3
|
128
|
+
segments:
|
129
|
+
- 0
|
130
|
+
version: "0"
|
131
|
+
type: :runtime
|
132
|
+
version_requirements: *id008
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: trollop
|
135
|
+
prerelease: false
|
136
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
hash: 3
|
142
|
+
segments:
|
143
|
+
- 0
|
144
|
+
version: "0"
|
145
|
+
type: :runtime
|
146
|
+
version_requirements: *id009
|
147
|
+
description: CRaB is a bridge between Cucumber and Rally
|
148
|
+
email:
|
149
|
+
- cvillela@thoughtworks.com
|
150
|
+
executables:
|
151
|
+
- crab
|
152
|
+
extensions: []
|
153
|
+
|
154
|
+
extra_rdoc_files: []
|
155
|
+
|
156
|
+
files:
|
157
|
+
- .gitignore
|
158
|
+
- .rvmrc
|
159
|
+
- Gemfile
|
160
|
+
- Rakefile
|
161
|
+
- bin/crab
|
162
|
+
- crab.gemspec
|
163
|
+
- features/find-text-in-stories.feature
|
164
|
+
- features/list-from-rally.feature
|
165
|
+
- features/login-and-out-of-rally.feature
|
166
|
+
- features/project-selection.feature
|
167
|
+
- features/pull-from-rally-into-cucumber.feature
|
168
|
+
- features/show-from-rally.feature
|
169
|
+
- features/steps/rally_steps.rb
|
170
|
+
- features/subcommand-help.feature
|
171
|
+
- features/support/aruba.rb
|
172
|
+
- features/update-story-in-rally.feature
|
173
|
+
- lib/crab.rb
|
174
|
+
- lib/crab/cli.rb
|
175
|
+
- lib/crab/cucumber_feature.rb
|
176
|
+
- lib/crab/cucumber_scenario.rb
|
177
|
+
- lib/crab/find.rb
|
178
|
+
- lib/crab/list.rb
|
179
|
+
- lib/crab/login.rb
|
180
|
+
- lib/crab/project.rb
|
181
|
+
- lib/crab/pull.rb
|
182
|
+
- lib/crab/rally.rb
|
183
|
+
- lib/crab/scenario.rb
|
184
|
+
- lib/crab/show.rb
|
185
|
+
- lib/crab/story.rb
|
186
|
+
- lib/crab/update.rb
|
187
|
+
- lib/crab/utilities.rb
|
188
|
+
- lib/crab/version.rb
|
189
|
+
- old/.gitignore
|
190
|
+
- old/Gemfile
|
191
|
+
- old/Gemfile.lock
|
192
|
+
- old/Rakefile
|
193
|
+
- old/templates/feature-pt.mustache
|
194
|
+
- old/templates/feature.mustache
|
195
|
+
has_rdoc: true
|
196
|
+
homepage: ""
|
197
|
+
licenses: []
|
198
|
+
|
199
|
+
post_install_message:
|
200
|
+
rdoc_options: []
|
201
|
+
|
202
|
+
require_paths:
|
203
|
+
- lib
|
204
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
205
|
+
none: false
|
206
|
+
requirements:
|
207
|
+
- - ">="
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
hash: 3
|
210
|
+
segments:
|
211
|
+
- 0
|
212
|
+
version: "0"
|
213
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
214
|
+
none: false
|
215
|
+
requirements:
|
216
|
+
- - ">="
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
hash: 3
|
219
|
+
segments:
|
220
|
+
- 0
|
221
|
+
version: "0"
|
222
|
+
requirements: []
|
223
|
+
|
224
|
+
rubyforge_project: crab
|
225
|
+
rubygems_version: 1.6.2
|
226
|
+
signing_key:
|
227
|
+
specification_version: 3
|
228
|
+
summary: Cucumber-Rally Bridge
|
229
|
+
test_files: []
|
230
|
+
|