bumps 0.0.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.
@@ -0,0 +1,5 @@
1
+ == 0.0.1 2009-07-09
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
5
+ * mmmm...has that new gem smell
@@ -0,0 +1,36 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ TODO
6
+ examples/feature_server
7
+ features/pull_remote_features.feature
8
+ features/push_feature_results.feature
9
+ features/steps/command_output.rb
10
+ features/steps/cucumber_run.rb
11
+ features/steps/env.rb
12
+ features/steps/feature_report.rb
13
+ features/steps/feature_server.rb
14
+ features/steps/helpers/scenario_process.rb
15
+ lib/bumps.rb
16
+ lib/bumps/configuration.rb
17
+ lib/bumps/feature.rb
18
+ lib/bumps/hook_tasks.rb
19
+ lib/bumps/pre_feature_load_hook.rb
20
+ lib/bumps/remote_feature.rb
21
+ lib/bumps/results_push_formatter.rb
22
+ script/console
23
+ script/destroy
24
+ script/generate
25
+ spec/bumps/configuration_spec.rb
26
+ spec/bumps/feature_spec.rb
27
+ spec/bumps/hook_tasks_spec.rb
28
+ spec/bumps/pre_feature_load_hook_spec.rb
29
+ spec/bumps/remote_feature_spec.rb
30
+ spec/bumps/results_push_formatter_spec.rb
31
+ spec/bumps_spec.rb
32
+ spec/spec.opts
33
+ spec/spec_helper.rb
34
+ tasks/rspec.rake
35
+ test_features/remote_content/destroy_dr_thaddeus_venture.feature
36
+ test_features/requires/env.rb
@@ -0,0 +1,59 @@
1
+ = Bumps
2
+
3
+ * http://github.com/brentsnook/bumps
4
+
5
+ Bumps shakes up {Cucumber}[http://cukes.info] by allowing you to pull feature content and push run results to and from a remote server. This means that your feature files no longer need to live with your steps and other code. This also means that you can publish the results of a Cucumber run to another system.
6
+
7
+ See the {wiki}[http://wiki.github.com/brentsnook/bumps] for more details.
8
+
9
+ == Installation
10
+
11
+ sudo gem install bumps
12
+
13
+ === Building the Gem yourself
14
+
15
+ Grab the code from github:
16
+
17
+ git clone git://github.com/brentsnook/bumps.git
18
+ cd bumps
19
+ rake install_gem
20
+
21
+ == How do I use it?
22
+
23
+ First, start up a server that meets the {push/pull contract}[http://wiki.github.com/brentsnook/bumps/push-pull-contract].
24
+
25
+ Secondly, bung this inside of your *env.rb* (or equivalent, just make sure it is under the feature directory):
26
+
27
+ require 'bumps'
28
+ Bumps.configure { use_server 'http://mycompliantserver.com' }
29
+
30
+ Lastly, just run Cukes as normal.
31
+
32
+ cucumber my_feature_directory
33
+
34
+ Bumps will pull feature files into the specified feature directory, run cukes and then push the results back to the server.
35
+
36
+ == License
37
+
38
+ (The MIT License)
39
+
40
+ Copyright (c) 2009 Brent Snook http://fuglylogic.com
41
+
42
+ Permission is hereby granted, free of charge, to any person obtaining
43
+ a copy of this software and associated documentation files (the
44
+ 'Software'), to deal in the Software without restriction, including
45
+ without limitation the rights to use, copy, modify, merge, publish,
46
+ distribute, sublicense, and/or sell copies of the Software, and to
47
+ permit persons to whom the Software is furnished to do so, subject to
48
+ the following conditions:
49
+
50
+ The above copyright notice and this permission notice shall be
51
+ included in all copies or substantial portions of the Software.
52
+
53
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
54
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
55
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
56
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
57
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
58
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
59
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/bumps'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('bumps', Bumps::VERSION) do |p|
7
+ p.developer 'Brent Snook', 'brent@fuglylogic.com'
8
+ p.summary = %q{Remote feature management for Cucumber.}
9
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
10
+ p.rubyforge_name = p.name
11
+ p.extra_deps = [
12
+ ['cucumber', ">= #{Bumps::LOWEST_SUPPORTED_CUCUMBER_VERSION}"],
13
+ ['nokogiri','>= 1.1.1'],
14
+ ]
15
+ p.extra_dev_deps = [
16
+ ['newgem', ">= #{::Newgem::VERSION}"],
17
+ ['rspec', '>= 1.2.7'],
18
+ ]
19
+
20
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
21
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
22
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
23
+ p.rsync_args = '-av --delete --ignore-errors'
24
+ end
25
+
26
+ require 'newgem/tasks' # load /tasks/*.rake
27
+ Dir['tasks/**/*.rake'].each { |t| load t }
28
+
29
+ task :default => [:spec, :features]
data/TODO ADDED
@@ -0,0 +1,20 @@
1
+ - tag release
2
+ - release gem on rubyforge
3
+
4
+ - register google group and update wiki doco
5
+
6
+
7
+ - add blog post on what it is, how to use it and why
8
+
9
+ - publish on twitter
10
+ - post to wave google group (links to blog)
11
+ - post to cucumber google group (links to blog)
12
+
13
+ --- LATER ---
14
+
15
+ - add validation for config and some nicer messages?
16
+ - get rid of treetop gibberish
17
+ - can it be more of a synch than a straight clobber??? for speed mainly
18
+ - extract process control logic out into a new gem
19
+ - support obtaining details for a single feature ???
20
+ - create lighthouse project
@@ -0,0 +1,61 @@
1
+ require 'webrick'
2
+ require 'stringio'
3
+
4
+ include WEBrick
5
+
6
+ class PullServlet < HTTPServlet::AbstractServlet
7
+
8
+ def do_GET(request, response)
9
+ response.body = feature_content
10
+ response['Content-Type'] = 'text/xml'
11
+ end
12
+
13
+ private
14
+
15
+ def feature_content
16
+ content = StringIO.new
17
+ content.puts '<?xml version="1.0"?>'
18
+ content.puts '<features>'
19
+
20
+ Dir.glob(features_pattern).each do |file|
21
+ content << feature_within(file) if File.file?(file)
22
+ end
23
+
24
+ content.puts '</features>'
25
+
26
+ content.string
27
+ end
28
+
29
+ def feature_within file
30
+ relative_path = file[(@options[0].length)..-1]
31
+ "<feature name=\"#{relative_path}\"> #{IO.read(file)}\n </feature>"
32
+ end
33
+
34
+ def features_pattern
35
+ File.join @options[0], '**', '*'
36
+ end
37
+
38
+ end
39
+
40
+ class PushServlet < HTTPServlet::AbstractServlet
41
+
42
+ def do_POST(request, response)
43
+ File.open(@options[0], 'w'){ |f| f.write(request.query['results']) }
44
+ response['Content-Type'] = 'text/html'
45
+ end
46
+
47
+ end
48
+
49
+ server = WEBrick::HTTPServer.new :Port => 1981
50
+
51
+ server.mount "/features/content", PullServlet, ARGV[0]
52
+ server.mount "/features/results", PushServlet, ARGV[1]
53
+
54
+ %w(INT TERM).each do |signal|
55
+ trap signal do
56
+ server.shutdown
57
+ exit!
58
+ end
59
+ end
60
+
61
+ server.start
@@ -0,0 +1,17 @@
1
+ Feature: Pull remote features
2
+
3
+ As a developer
4
+ I want to pull Cucumber features from a remote location
5
+ So that the features can be authored using a separate tool
6
+
7
+ Scenario: Remote features pulled successfully
8
+
9
+ Given that a feature server is running
10
+ When a cucumber run is performed
11
+ Then the feature report will contain all remote features
12
+
13
+ Scenario: Feature server not reachable
14
+
15
+ Given that a feature server is not running
16
+ When a cucumber run is performed
17
+ Then the command output should show that features could not be pulled
@@ -0,0 +1,11 @@
1
+ Feature: Serve feature results
2
+
3
+ As a remote feature authoring tool
4
+ I want to be informed of the results of a feature run
5
+ So that I can format my local feature data accordingly
6
+
7
+ Scenario: Push all feature results after a run
8
+
9
+ Given that a feature server is running
10
+ When a cucumber run is performed
11
+ Then the results of the feature run will be sent to the feature server
@@ -0,0 +1,3 @@
1
+ Then /^the command output should show that features could not be pulled$/ do
2
+ command_output.should match(/Could not pull features/)
3
+ end
@@ -0,0 +1,3 @@
1
+ Given /^a cucumber run is performed$/ do
2
+ ScenarioProcess.run_and_wait "cucumber -o #{feature_report_file} -r #{test_require_file} #{test_features_directory}", 'cucumber'
3
+ end
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + "/../../lib/bumps"
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
7
+
8
+ module CucumberWorld
9
+
10
+ def feature_report_file
11
+ File.expand_path File.join(root, 'tmp', 'cucumber.out')
12
+ end
13
+
14
+ def feature_report
15
+ IO.read feature_report_file
16
+ end
17
+
18
+ def command_output
19
+ IO.read command_output_file
20
+ end
21
+
22
+ def feature_server_script
23
+ File.expand_path File.join(root, 'examples', 'feature_server')
24
+ end
25
+
26
+ def push_request_file
27
+ File.expand_path File.join(root, 'tmp', 'results.xml')
28
+ end
29
+
30
+ def push_request
31
+ IO.read push_request_file
32
+ end
33
+
34
+ def test_require_file
35
+ File.expand_path File.join(root, 'test_features', 'requires', 'env.rb')
36
+ end
37
+
38
+ def test_features_directory
39
+ File.expand_path File.join(root, 'tmp')
40
+ end
41
+
42
+ def feature_server_command
43
+ "ruby #{feature_server_script} #{remote_features_directory} #{push_request_file}"
44
+ end
45
+
46
+ def each_feature
47
+ Dir.glob("#{remote_features_directory}/**/*") do |feature_file|
48
+ content = IO.read(feature_file).strip
49
+ yield content.first.strip
50
+ end
51
+ end
52
+
53
+ def clean
54
+ FileUtils.remove_entry_secure test_features_directory
55
+ FileUtils.mkdir test_features_directory
56
+ [
57
+ feature_report_file,
58
+ push_request_file
59
+ ].each do |file|
60
+ FileUtils.remove_entry_secure file if File.exist? file
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def root
67
+ File.expand_path File.join(File.dirname(__FILE__), '..', '..')
68
+ end
69
+
70
+ def command_output_file
71
+ File.expand_path File.join(root, 'tmp', 'cucumber.log')
72
+ end
73
+
74
+ def remote_features_directory
75
+ File.expand_path File.join(root, 'test_features', 'remote_content')
76
+ end
77
+
78
+ end
79
+
80
+ World CucumberWorld
81
+
82
+ Before do
83
+ clean
84
+ end
85
+
86
+ After do
87
+ ScenarioProcess.kill_all
88
+ end
@@ -0,0 +1,6 @@
1
+ Then /^the feature report will contain all remote features$/ do
2
+ each_feature do |feature|
3
+ feature_report.should match(/#{feature}/)
4
+ end
5
+ end
6
+
@@ -0,0 +1,14 @@
1
+ Given /^that a feature server is running$/ do
2
+ ScenarioProcess.run feature_server_command, 'feature_server'
3
+ end
4
+
5
+ Given /^that a feature server is not running$/ do
6
+ ScenarioProcess.kill feature_server_command
7
+ end
8
+
9
+ Then /^the results of the feature run will be sent to the feature server$/ do
10
+ each_feature do |feature|
11
+ push_request.should match(/#{feature}/)
12
+ end
13
+ end
14
+
@@ -0,0 +1,67 @@
1
+ class ScenarioProcess
2
+
3
+ @commands = []
4
+
5
+ def initialize command, name
6
+ @command = command
7
+ @name = name
8
+ end
9
+
10
+ def run
11
+ remove log
12
+ IO.popen(full_command).sync = true
13
+ wait_for log
14
+ end
15
+
16
+ def run_and_wait
17
+ remove log
18
+ `#{full_command}`
19
+ end
20
+
21
+ def self.run command, name
22
+ new(command,name).run
23
+ @commands << command
24
+ end
25
+
26
+ def self.run_and_wait command, name
27
+ new(command,name).run_and_wait
28
+ end
29
+
30
+ def self.kill_all
31
+ @commands.each { |command| kill command }
32
+ end
33
+
34
+ def self.kill command
35
+ # this method of killing stuff sucks, feels brittle
36
+
37
+ `ps`.each do |process|
38
+ # the space at the front of the pattern is essential...
39
+ # otherwise this will only work intermittently; if the pid you want to kill
40
+ # is shorter than others in the process list then the group will not capture it
41
+ matches = process.match /\s*(\d*).*#{Regexp.escape(command)}/
42
+ `kill #{matches[1]}` if matches
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def log
49
+ File.expand_path(File.dirname(__FILE__) + "/../../../tmp/#{@name}.log")
50
+ end
51
+
52
+ def remove file
53
+ FileUtils.rm file, :force => true
54
+ end
55
+
56
+ def full_command
57
+ "(#{@command}) > #{log} 2>&1"
58
+ end
59
+
60
+ def wait_for file
61
+ until File.exists? file
62
+ sleep 0.2
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,25 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'cucumber'
6
+
7
+ [
8
+ 'remote_feature',
9
+ 'feature',
10
+ 'configuration',
11
+ 'results_push_formatter',
12
+ 'hook_tasks',
13
+ 'pre_feature_load_hook'
14
+ ].each {|file| require "bumps/#{file}"}
15
+
16
+ module Bumps
17
+
18
+ VERSION = '0.0.1'
19
+ LOWEST_SUPPORTED_CUCUMBER_VERSION = '0.3.11'
20
+
21
+ def self.configure &block
22
+ Configuration.configure(&block)
23
+ PreFeatureLoadHook.register_on Cucumber::Cli::Main
24
+ end
25
+ end