tagfu 1.0.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 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: []