mandoline 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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mandoline.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2012 by Aanand Prasad
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
data/README.ronn ADDED
@@ -0,0 +1,39 @@
1
+ mandoline(1) - delete Cucumber features/scenarios by tag
2
+ ========================================================
3
+
4
+ Synopsis
5
+ --------
6
+
7
+ `mandoline` `-t`|`--tags` <tag>[,<tag>...] <path> [<path>...]
8
+
9
+ Installation
10
+ ------------
11
+
12
+ `gem install mandoline`
13
+
14
+ Description
15
+ -----------
16
+
17
+ Given a tag or comma-separated list of tags (e.g. `foo,bar`) and a list of Cucumber feature file paths, mandoline will:
18
+
19
+ - delete files with `@foo` or `@bar` at the top
20
+ - delete scenarios with `@foo` or `@bar` on the preceding line
21
+
22
+ Options
23
+ -------
24
+
25
+ - `-t <tags>`, `--tags <tags>`
26
+ Comma-separated list of tags to filter by.
27
+
28
+ - `<path>`
29
+ One or more feature file paths. If a single argument is given, and it is a directory, it is recursively scanned for `.feature` files.
30
+
31
+ Bugs
32
+ ----
33
+
34
+ Report bugs and find great salad recipes at [the Github page](https://github.com/aanand/mandoline).
35
+
36
+ Copyright
37
+ ---------
38
+
39
+ Copyright 2012 by [Aanand Prasad](http://aanandprasad.com) under the MIT license (see the LICENSE file).
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/mandoline ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mandoline'
4
+ Mandoline.run(*ARGV)
@@ -0,0 +1,27 @@
1
+ module Mandoline
2
+ class Logger
3
+ def initialize(io)
4
+ @io = io
5
+ end
6
+
7
+ def file_deleted(path)
8
+ @io.puts("Deleted #{red(path)}")
9
+ end
10
+
11
+ def scenarios_deleted(path, names)
12
+ @io.puts("Deleted scenarios from #{path}:")
13
+
14
+ names.each do |name|
15
+ @io.puts(" #{red(name)}")
16
+ end
17
+ end
18
+
19
+ def red(text)
20
+ color(text, 31)
21
+ end
22
+
23
+ def color(text, code)
24
+ "\033[1;#{code}m#{text}\033[0m"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,88 @@
1
+ module Mandoline
2
+ class Runner
3
+ def initialize(options)
4
+ @tags = options[:tags]
5
+ @logger = options[:logger]
6
+ end
7
+
8
+ def run(paths)
9
+ paths.each do |path|
10
+ process_file(path)
11
+ end
12
+ end
13
+
14
+ def process_file(path)
15
+ lines = File.readlines(path)
16
+
17
+ if entire_file_tagged?(lines)
18
+ delete_file(path)
19
+ else
20
+ delete_scenarios_from_file(path, lines)
21
+ end
22
+ end
23
+
24
+ def delete_file(path)
25
+ File.delete(path)
26
+ @logger.file_deleted(path) if @logger
27
+ end
28
+
29
+ def delete_scenarios_from_file(path, lines)
30
+ scenario_names = delete_tagged_scenarios(lines)
31
+
32
+ if scenario_names.any?
33
+ File.open(path, "w") do |f|
34
+ lines.each do |line|
35
+ f.write(line)
36
+ end
37
+ end
38
+
39
+ @logger.scenarios_deleted(path, scenario_names) if @logger
40
+ end
41
+ end
42
+
43
+ def delete_tagged_scenarios(lines)
44
+ index = 0
45
+ scenario_names = []
46
+
47
+ while index < lines.length
48
+ line = lines[index]
49
+
50
+ if line_tagged?(line)
51
+ scenario_names << delete_scenario(lines, index)
52
+ else
53
+ index += 1
54
+ end
55
+ end
56
+
57
+ scenario_names
58
+ end
59
+
60
+ def line_tagged?(line)
61
+ if tags_section = line[/^(\s+@\w+)+/]
62
+ found_tags = tags_section.scan(/@(\w+)/).flatten
63
+ (@tags & found_tags).any?
64
+ else
65
+ false
66
+ end
67
+ end
68
+
69
+ def entire_file_tagged?(lines)
70
+ first_non_empty_line = lines.find { |line| !line.strip.empty? }
71
+ @tags.any? { |tag| first_non_empty_line =~ /^@#{tag}/ }
72
+ end
73
+
74
+ def delete_scenario(lines, index)
75
+ lines.delete_at(index) # delete tag line
76
+
77
+ scenario_name = lines[index][/Scenario: (.*)/, 1]
78
+
79
+ indent = lines[index][/^\s+/]
80
+
81
+ lines.delete_at(index) # delete scenario line
82
+ lines.delete_at(index) while lines[index] =~ /^#{indent}\s+/ # delete nested lines
83
+ lines.delete_at(index) while lines[index] =~ /^\s+$/ # delete trailing blank lines
84
+
85
+ scenario_name
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,3 @@
1
+ module Mandoline
2
+ VERSION = "0.0.1"
3
+ end
data/lib/mandoline.rb ADDED
@@ -0,0 +1,39 @@
1
+ require "mandoline/version"
2
+ require "mandoline/logger"
3
+ require "mandoline/runner"
4
+ require "optparse"
5
+
6
+ module Mandoline
7
+ def self.run(*args)
8
+ options = {}
9
+
10
+ parser = OptionParser.new do |opts|
11
+ opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [PATHS]"
12
+
13
+ opts.on "-t TAGS", "--tags TAGS", "Comma-separated list of tags to delete, e.g. wip,pending,v1", Array do |v|
14
+ options[:tags] = v
15
+ end
16
+ end
17
+
18
+ begin
19
+ parser.parse!(args)
20
+ raise OptionParser::MissingArgument, "--tags" unless options[:tags]
21
+ raise OptionParser::MissingArgument, "At least one path must be specified" if args.empty?
22
+ rescue OptionParser::ParseError => e
23
+ puts e.message
24
+ puts
25
+ puts parser
26
+ exit -1
27
+ end
28
+
29
+ options[:logger] = Mandoline::Logger.new(STDERR)
30
+
31
+ paths = if args.length == 1 && File.directory?(args.first)
32
+ Dir.glob("#{args.first}/**/*.feature")
33
+ else
34
+ args
35
+ end
36
+
37
+ Runner.new(options).run(paths)
38
+ end
39
+ end
data/mandoline.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mandoline/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mandoline"
7
+ s.version = Mandoline::VERSION
8
+ s.authors = ["Aanand Prasad"]
9
+ s.email = ["aanand.prasad@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Delete Cucumber features by tag}
12
+
13
+ s.rubyforge_project = "mandoline"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_development_dependency "rspec"
21
+ end
@@ -0,0 +1,111 @@
1
+ require "mandoline/runner"
2
+ require "tmpdir"
3
+
4
+ describe Mandoline::Runner do
5
+ before do
6
+ @dir = Dir.mktmpdir
7
+
8
+ @files = {
9
+ :no_scenarios => %{
10
+ Feature: I have no scenarios.},
11
+ :entire_file_tagged => %{
12
+ @tag1
13
+ Feature: I am tagged.},
14
+ :scenarios_tagged => %{
15
+ Feature: I have one tagged scenario.
16
+ Scenario: This scenario should not be deleted.
17
+
18
+ @tag1
19
+ Scenario: This scenario should be deleted.
20
+
21
+ @tag2
22
+ Scenario: This scenario should not be deleted.
23
+
24
+ @tag3 @tag1 @tag2
25
+ Scenario: This scenario should be deleted.
26
+
27
+ @tag2 # @tag1
28
+ Scenario: This scenario should not be deleted.
29
+ },
30
+ :file_and_scenarios_tagged => %{
31
+ @tag1
32
+ Feature: I have one tagged scenario.
33
+ @tag1
34
+ Scenario: This scenario should be deleted.
35
+ },
36
+ :tag_at_file_end => %{
37
+ Feature: I have a tag at the end and should not be deleted.
38
+ Scenario: This scenario should not be deleted.
39
+ @tag1
40
+ }
41
+ }
42
+
43
+ @files.each_pair do |name, text|
44
+ File.open(feature_path(name), "w") do |f|
45
+ f.write(text)
46
+ end
47
+ end
48
+ end
49
+
50
+ after do
51
+ FileUtils.remove_entry_secure(@dir) if @dir
52
+ end
53
+
54
+ def feature_path(name)
55
+ "#{@dir}/#{name}.feature"
56
+ end
57
+
58
+ def feature_text(name)
59
+ @files[name]
60
+ end
61
+
62
+ let(:logger) { mock }
63
+
64
+ subject {
65
+ Mandoline::Runner.new(
66
+ :tags => ['tag1'],
67
+ :logger => logger
68
+ )
69
+ }
70
+
71
+ it "leaves empty files alone" do
72
+ expect { subject.run([feature_path(:no_scenarios)]) }.not_to change { File.read(feature_path(:no_scenarios)) }
73
+ end
74
+
75
+ it "deletes feature files which are tagged" do
76
+ features = [:entire_file_tagged, :file_and_scenarios_tagged]
77
+
78
+ features.each do |name|
79
+ logger.should_receive(:file_deleted).with(feature_path(name))
80
+ end
81
+
82
+ expect {
83
+ subject.run(features.map { |name| feature_path(name) })
84
+ }.to change {
85
+ features.map { |name| File.exist?(feature_path(name)) }
86
+ }.
87
+ from( features.map { |_| true } ).
88
+ to( features.map { |_| false })
89
+ end
90
+
91
+ it "leaves files alone which have a tag at the start of a line, but not at the top" do
92
+ expect { subject.run([feature_path(:tag_at_file_end)]) }.not_to change { File.read(feature_path(:tag_at_file_end)) }
93
+ end
94
+
95
+ it "deletes scenarios which are tagged" do
96
+ logger.should_receive(:scenarios_deleted).with(feature_path(:scenarios_tagged), ["This scenario should be deleted.", "This scenario should be deleted."])
97
+
98
+ expect { subject.run([feature_path(:scenarios_tagged)]) }.to change { File.read(feature_path(:scenarios_tagged)) }.
99
+ from(feature_text(:scenarios_tagged)).
100
+ to(%{
101
+ Feature: I have one tagged scenario.
102
+ Scenario: This scenario should not be deleted.
103
+
104
+ @tag2
105
+ Scenario: This scenario should not be deleted.
106
+
107
+ @tag2 # @tag1
108
+ Scenario: This scenario should not be deleted.
109
+ })
110
+ end
111
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mandoline
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Aanand Prasad
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70228780965340 !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: *70228780965340
25
+ description:
26
+ email:
27
+ - aanand.prasad@gmail.com
28
+ executables:
29
+ - mandoline
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - .rspec
35
+ - Gemfile
36
+ - LICENSE
37
+ - README.ronn
38
+ - Rakefile
39
+ - bin/mandoline
40
+ - lib/mandoline.rb
41
+ - lib/mandoline/logger.rb
42
+ - lib/mandoline/runner.rb
43
+ - lib/mandoline/version.rb
44
+ - mandoline.gemspec
45
+ - spec/runner_spec.rb
46
+ homepage: ''
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project: mandoline
66
+ rubygems_version: 1.8.15
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Delete Cucumber features by tag
70
+ test_files:
71
+ - spec/runner_spec.rb