bumps 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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