tagfu 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ Gemfile.lock
3
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # gem's dependencies in the tagfu gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 by Avinash Padmanabhan
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.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ Tagfu - Cucumber Tag Manipulation
2
+ =================================
3
+
4
+ Description
5
+ -----------
6
+
7
+ Tagfu is a command line utility for manipulating tags in Cucumber feature files
8
+
9
+ With Tagfu, you can easily add, update, delete specific tags or delete all tags from Cucumber feature files.
10
+ Tagfu can operate on a specific feature file or on all the feature files within a specified folder structure.
11
+
12
+ Features
13
+ --------
14
+
15
+ - Manipulate tags for a specific feature file or all the feature files within a specified folder
16
+ - Add one or multiple tags at the feature level
17
+ - Update existing tags
18
+ - Delete specific tags
19
+ - Delete all tags
20
+
21
+ Installation
22
+ ------------
23
+
24
+ `gem install tagfu`
25
+
26
+ Synopsis
27
+ --------
28
+
29
+ Access the command line help
30
+
31
+ `tagfu --help`
32
+
33
+ Add tags to the Cucumber feature file(s) in the current directory
34
+
35
+ `tagfu -p . -a wip,smoke`
36
+ `tagfu -p . -a @wip,@smoke`
37
+
38
+ Update tags in the specified Cucumber feature file
39
+
40
+ `tagfu -p story.feature -u wip,smoke`
41
+ `tagfu --path story.feature --update @wip,@smoke`
42
+
43
+ Delete tags from the Cucumber feature file(s) in the specified folder path
44
+
45
+ `tagfu --path /usr/home/jdoe/project/features --delete wip,hacky`
46
+
47
+ Delete all tags from Cucumber feature file(s) in the current directory
48
+
49
+ `tagfu --path . --delete-all`
50
+
51
+ Bugs
52
+ ----
53
+
54
+ Report bugs and requests at [the Github page](https://github.com/eveningsamurai/tagfu).
55
+
56
+
57
+ Copyright
58
+ ---------
59
+
60
+ Copyright 2012 by [Avinash Padmanabhan](http://eveningsamurai.wordpress.com) under the MIT license (see the LICENSE file).
data/bin/tagfu ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tagfu'
4
+
5
+ Tagfu.run(*ARGV)
@@ -0,0 +1,115 @@
1
+ module Tagfu
2
+
3
+ class Tagger
4
+ def initialize(options={})
5
+ @add_tags = options[:add_tags]
6
+ @update_tags = options[:update_tags]
7
+ @delete_tags = options[:delete_tags]
8
+ @delete_all = options[:delete_all]
9
+ @path = options[:path]
10
+ end
11
+
12
+ def sanitize_tags(tags)
13
+ tags.collect! {|tag| tag.match(/^@/) ? tag : "@#{tag}"}
14
+ end
15
+
16
+ def get_files
17
+ if File.directory?(@path)
18
+ current_working_dir = Dir.pwd
19
+ Dir.chdir(@path)
20
+ files = Dir.glob("**/*.feature")
21
+ raise ArgumentError, "No cucumber feature files in specified path" if files.empty?
22
+ files.each {|file| process_file(file)}
23
+ Dir.chdir(current_working_dir)
24
+ elsif File.file?(@path)
25
+ process_file(@path)
26
+ else
27
+ raise ArgumentError, "Path specified is either invalid or not a file/directory"
28
+ end
29
+ end
30
+
31
+ def process_file(file)
32
+ lines = File.readlines(file)
33
+ if @delete_all
34
+ delete_all(lines)
35
+ end
36
+ unless @delete_tags.nil?
37
+ sanitize_tags(@delete_tags)
38
+ delete_tags(lines)
39
+ end
40
+ unless @add_tags.nil?
41
+ sanitize_tags(@add_tags)
42
+ add_tags(lines)
43
+ end
44
+ unless @update_tags.nil?
45
+ sanitize_tags(@update_tags)
46
+ @update_tags = {:from_tag => @update_tags.first, :to_tag => @update_tags.last}
47
+ update_tags(lines)
48
+ end
49
+ File.open(file, 'w') do |fh|
50
+ lines.each do |line|
51
+ fh.write line
52
+ end
53
+ end
54
+ end
55
+
56
+ def delete_all(lines)
57
+ lines_to_delete = []
58
+ lines.each_with_index do |line, index|
59
+ if line.match(/@/)
60
+ lines_to_delete << index
61
+ end
62
+ end
63
+ running_index = 0
64
+ lines_to_delete.each do |line_index|
65
+ lines.delete_at(line_index - running_index)
66
+ running_index += 1
67
+ end
68
+ end
69
+
70
+ def delete_tags(lines)
71
+ @delete_tags.each do |tag|
72
+ lines.each_with_index do |line, index|
73
+ if only_tag?(line, tag)
74
+ lines.delete_at(index)
75
+ else
76
+ indent = lines[index][/^\s+/].to_s
77
+ line = line.strip
78
+ next if line.empty?
79
+ lines[index] = indent + line.chomp.split(' ').delete_if {|word| word.match(/#{tag}/)}.join(' ') + "\n" if line.match(/^@/)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def only_tag?(line, tag)
86
+ return line.strip == tag
87
+ end
88
+
89
+ def add_tags(lines)
90
+ feature_index = find_index_of_feature(lines)
91
+ indent = lines[feature_index][/^\s+/].to_s
92
+ lines.insert(feature_index, indent + @add_tags.join(' ') + "\n")
93
+ end
94
+
95
+ def find_index_of_feature(lines)
96
+ lines.each_with_index do |line, index|
97
+ return index if line.strip.match(/^Feature:/)
98
+ end
99
+ end
100
+
101
+ def update_tags(lines)
102
+ lines.each_with_index do |line, index|
103
+ indent = lines[index][/^\s+/].to_s
104
+ if only_tag?(line, @update_tags[:from_tag])
105
+ lines.delete_at(index)
106
+ lines.insert(index, indent + "#{@update_tags[:to_tag]}\n")
107
+ else
108
+ line = line.strip
109
+ next if line.empty?
110
+ lines[index] = indent + line.chomp.split(' ').map! {|word| word == "#{@update_tags[:from_tag]}" ? "#{@update_tags[:to_tag]}" : word}.join(' ') + "\n" if line.match(/^@/)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,3 @@
1
+ module Tagfu
2
+ VERSION = "1.0.0"
3
+ end
data/lib/tagfu.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'optparse'
2
+ require 'tagfu/tagger'
3
+
4
+ module Tagfu
5
+ def self.run(*args)
6
+ options = {}
7
+
8
+ option_parser = OptionParser.new do |opts|
9
+ opts.banner = 'Usage: tagfu [PATH] [OPTIONS]'
10
+
11
+ opts.on('-d', '--delete TAG1,TAG2', Array, 'Delete comma seperated list of tags from scenario(s), e.g. smoke, regression, wip') do |tags|
12
+ options[:delete_tags] = tags
13
+ end
14
+
15
+ opts.on('--delete-all', 'Delete all tags from specified feature files') do |t|
16
+ options[:delete_all] = true
17
+ end
18
+
19
+ opts.on('-u', '--update FROM_TAG,TO_TAG', Array, 'Update tag names in scenario(s) e.g. "wip" to "smoke"') do |tags|
20
+ options[:update_tags] = tags
21
+ end
22
+
23
+ opts.on('-a', '--add TAG1,TAG2', Array, 'Add comma seperated list of tags at the feature level, e.g. smoke, regression, wip') do |tags|
24
+ options[:add_tags] = tags
25
+ end
26
+
27
+ opts.on('-p PATH', '--path PATH', 'Path to the cucumber feature file(s)') do |path|
28
+ options[:path] = path
29
+ end
30
+ end
31
+
32
+ begin
33
+ option_parser.parse!
34
+ raise OptionParser::MissingArgument, "Atleast one of the options '--update', '--delete', '--add' or '--delete-all' should be specified" if op_tags_absent?(options)
35
+ raise OptionParser::MissingArgument, "Path '--path' should be specified" if path_absent?(options[:path])
36
+ rescue OptionParser::ParseError => e
37
+ puts e.message
38
+ puts
39
+ puts option_parser
40
+ exit -1
41
+ end
42
+
43
+ Tagger.new(options).get_files
44
+ end
45
+
46
+ def self.op_tags_absent?(options)
47
+ tags = [:update_tags, :delete_tags, :add_tags, :delete_all]
48
+ (tags & options.keys).empty?
49
+ end
50
+
51
+ def self.path_absent?(path)
52
+ path.nil?
53
+ end
54
+
55
+ end
56
+
@@ -0,0 +1,164 @@
1
+ require '../lib/tagfu/tagger.rb'
2
+ require 'rspec/expectations'
3
+ require 'tmpdir'
4
+
5
+ RSpec.configure do |config|
6
+ @dir = Dir.mktmpdir
7
+ config.before(:all) do
8
+ files = {
9
+ :no_tags =>
10
+ """
11
+ Feature: No Tags in feature file
12
+
13
+ Scenario: sample story
14
+ Given I have a feature file with no tags
15
+ When I run it through the tagger
16
+ Then I see nothing changed
17
+ """,
18
+ :add_tags =>
19
+ """
20
+ Feature: No Tags in feature file
21
+
22
+ Scenario: sample story
23
+ Given I have a feature file with no tags
24
+ When I run it through the tagger
25
+ Then I see nothing changed
26
+ """,
27
+ :one_tag_to_delete =>
28
+ """
29
+ @wip
30
+ Feature: One tag to delete in feature file
31
+
32
+ Scenario: sample story
33
+ Given I have a feature file with one tag
34
+ When I run it through the tagger
35
+ Then I see that the tag has been deleted
36
+ """,
37
+ :one_tag_add_update =>
38
+ """
39
+ Feature: No Tags in feature file
40
+
41
+ @pass
42
+ Scenario: sample story
43
+ Given I have a feature file with no tags
44
+ When I run it through the tagger
45
+ Then I see nothing changed
46
+ """,
47
+ :one_tag_to_update =>
48
+ """
49
+ @wip
50
+ Feature: One tag to update in feature file
51
+
52
+ Scenario: sample story
53
+ Given I have a feature file with one tag
54
+ When I run it through the tagger
55
+ Then I see that the tag has been updated
56
+ """,
57
+ :three_tags_delete_update =>
58
+ """
59
+ Feature: Three tags to update in feature file
60
+
61
+ @wip @story1 @story2
62
+ @story3
63
+ Scenario: sample story
64
+ Given I have a feature file with three tags
65
+ When I run it through the tagger
66
+ Then I see that the tags has been updated
67
+ """,
68
+ :four_tags_add_delete =>
69
+ """
70
+ @wip @story1
71
+ Feature: One tag to update in feature file
72
+
73
+ @wip
74
+ @story1
75
+ @story2
76
+ Scenario: sample story
77
+ Given I have a feature file with three tags
78
+ When I run it through the tagger
79
+ Then I see that the tags has been updated
80
+ """,
81
+ :all_tags_delete =>
82
+ """
83
+ @wip @smoke
84
+ @story1
85
+ Feature: One tag to delete in feature file
86
+
87
+ @story2
88
+ @story3 @story4
89
+ Scenario: sample story
90
+ Given I have a feature file with one tag
91
+ When I run it through the tagger
92
+ Then I see that the tag has been deleted
93
+ """
94
+ }
95
+ files.each_pair do |feature_name, feature|
96
+ File.open("#{feature_name.to_s}.feature", 'w') { |f| f.write(feature) }
97
+ end
98
+ end
99
+
100
+ config.after(:all) do
101
+ #FileUtils.remove_entry_secure @dir
102
+ files = Dir.glob("*.feature")
103
+ files.each {|file| File.unlink(file)}
104
+ end
105
+
106
+ describe Tagfu::Tagger do
107
+ it "should not change feature file when no tags present" do
108
+ file = "no_tags.feature"
109
+ file_before = File.readlines(file)
110
+ Tagfu::Tagger.new({:path => file, :delete_tags => ["smoke"]}).get_files
111
+ File.readlines(file).each_with_index do |line, index|
112
+ line.should == file_before[index]
113
+ end
114
+ end
115
+
116
+ it "should update tags when present in feature file" do
117
+ file = "one_tag_to_update.feature"
118
+ Tagfu::Tagger.new({:path => file, :update_tags => ["wip", "woot"]}).get_files
119
+ File.readlines(file).any? {|line| line =~ /@wip/}.should be_false
120
+ File.readlines(file).any? {|line| line =~ /@woot/}.should be_true
121
+ end
122
+
123
+ it "should delete tags when present in feature file" do
124
+ file = "one_tag_to_delete.feature"
125
+ Tagfu::Tagger.new({:path => file, :delete_tags => ["wip"]}).get_files
126
+ File.readlines(file).any? {|line| line =~ /@/}.should be_false
127
+ end
128
+
129
+ it "should add tags to feature file" do
130
+ file = "add_tags.feature"
131
+ Tagfu::Tagger.new({:path => file, :add_tags => ["wip"]}).get_files
132
+ File.readlines(file).any? {|line| line =~ /@wip/}.should be_true
133
+ end
134
+
135
+ it "should add/update tags in feature file" do
136
+ file = "one_tag_add_update.feature"
137
+ Tagfu::Tagger.new({:path => file, :add_tags => ["wip", "woot"], :update_tags => ["pass", "smoke"]}).get_files
138
+ File.readlines(file).any? {|line| line =~ /@smoke/}.should be_true
139
+ end
140
+
141
+ it "should delete/update tags in feature file" do
142
+ file = "three_tags_delete_update.feature"
143
+ Tagfu::Tagger.new({:path => file, :delete_tags => ["wip", "story3"], :update_tags => ["story1", "smoke"]}).get_files
144
+ File.readlines(file).any? {|line| line =~ /@story3/}.should be_false
145
+ File.readlines(file).any? {|line| line =~ /@wip/}.should be_false
146
+ File.readlines(file).any? {|line| line =~ /@smoke/}.should be_true
147
+ end
148
+
149
+ it "should add/delete tags in feature file" do
150
+ file = "four_tags_add_delete.feature"
151
+ Tagfu::Tagger.new({:path => file, :delete_tags => ["wip", "story1"], :update_tags => ["story2", "story3"]}).get_files
152
+ File.readlines(file).any? {|line| line =~ /@story1/}.should be_false
153
+ File.readlines(file).any? {|line| line =~ /@wip/}.should be_false
154
+ File.readlines(file).any? {|line| line =~ /@story2/}.should be_false
155
+ File.readlines(file).any? {|line| line =~ /@story3/}.should be_true
156
+ end
157
+
158
+ it "should delete all tags in feature file" do
159
+ file = "all_tags_delete.feature"
160
+ Tagfu::Tagger.new({:path => file, :delete_all => true}).get_files
161
+ File.readlines(file).any? {|line| line =~ /@/}.should be_false
162
+ end
163
+ end
164
+ end
data/tagfu.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ require './lib/tagfu/version.rb'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'tagfu'
5
+ s.version = Tagfu::VERSION
6
+ s.date = '2012-09-22'
7
+ s.summary = "tagfu"
8
+ s.description = "A gem to manipulate tags in cucumber feature files"
9
+ s.authors = ["Avinash Padmanabhan"]
10
+ s.email = ['avinashpadmanabhan@gmail.com']
11
+ s.files = `git ls-files`.split("\n")
12
+ s.homepage = "https://github.com/eveningsamurai/tagfu"
13
+
14
+ s.add_development_dependency "OptionParser"
15
+ s.add_development_dependency "rspec"
16
+ s.require_paths = ["lib"]
17
+
18
+ s.executables << 'tagfu'
19
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tagfu
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Avinash Padmanabhan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: OptionParser
16
+ requirement: !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: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: A gem to manipulate tags in cucumber feature files
47
+ email:
48
+ - avinashpadmanabhan@gmail.com
49
+ executables:
50
+ - tagfu
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - LICENSE
57
+ - README.md
58
+ - bin/tagfu
59
+ - lib/tagfu.rb
60
+ - lib/tagfu/tagger.rb
61
+ - lib/tagfu/version.rb
62
+ - spec/tagger_spec.rb
63
+ - tagfu.gemspec
64
+ homepage: https://github.com/eveningsamurai/tagfu
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.24
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: tagfu
88
+ test_files: []