cukehead 0.1.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Bill Melvin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,55 @@
1
+ == cukehead
2
+
3
+ === Introduction
4
+
5
+ Cukehead provides hours (well, maybe a couple minutes) of fun
6
+ exploring Cucumber feature files in the form of FreeMind mind
7
+ maps. If you want to create feature files based on a mind map
8
+ it can do that too but I'm not sure anyone will want to do
9
+ that.
10
+
11
+ Cukehead is the result of the author wanting to learn how Cucumber works
12
+ in preparation for a Rails project during the same period of time he was
13
+ exploring mind mapping software in a questionable attempt to become more
14
+ organized. Somehow the two came together as an exercise to learn how to
15
+ build a Ruby application.
16
+
17
+
18
+ === Usage
19
+
20
+ cukehead command [options]
21
+
22
+ command:
23
+ map
24
+ Read Cucumber feature files and create a FreeMind mind map file.
25
+
26
+ cuke
27
+ Read a FreeMind mind map file and create Cucumber feature files.
28
+
29
+ options:
30
+ -h or --help
31
+ Show the help text.
32
+
33
+ -o or --overwrite
34
+ Overwrite existing output file(s).
35
+
36
+ -m FILENAME or --mm-filename FILENAME
37
+ map: Name of output file (default is mm/cukehead-output.mm).
38
+
39
+ -f PATH or --features-path PATH
40
+ map: Directory containing feature files to read (default is directory
41
+ named 'features' in current directory).
42
+
43
+ cuke: Directory feature files will be written to.
44
+
45
+ -s FILENAME or --source-mm FILENAME
46
+ map: FreeMind mind map file to use as a template for creating
47
+ the output file. If the template contains a node with the text
48
+ 'Cucumber features:' then the feature nodes will be inserted there.
49
+
50
+ === Resources
51
+
52
+ Cucumber project: http://cukes.info/
53
+
54
+ FreeMind project: http://freemind.sourceforge.net/wiki/index.php/Main_Page
55
+
@@ -0,0 +1,101 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'spec/rake/spectask'
6
+ require 'rake/testtask'
7
+ require 'rake/rdoctask'
8
+ require 'cucumber/rake/task'
9
+
10
+ GEM = 'cukehead'
11
+
12
+ spec = Gem::Specification.new do |s|
13
+ s.name = "cukehead"
14
+ s.version = "0.1.1"
15
+ s.author = "Bill Melvin"
16
+ s.email = "bill@bogusoft.com"
17
+ s.homepage = "http://www.bogusoft.com/cukehead/"
18
+ s.description = s.summary = "A gem that creates a FreeMind mind map from Cucumber feature files and vice versa."
19
+
20
+ s.platform = Gem::Platform::RUBY
21
+ s.has_rdoc = true
22
+ #s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
23
+ s.extra_rdoc_files = ["README"]
24
+ #s.summary = SUMMARY
25
+
26
+ # Uncomment this to add a dependency
27
+ # s.add_dependency "foo"
28
+
29
+ s.require_path = 'lib'
30
+ #s.autorequire = GEM
31
+
32
+ #s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
33
+
34
+ s.executables = ["cukehead"]
35
+
36
+ files = []
37
+ File.open('Manifest.txt', 'r') {|f| f.each {|line| files << line.strip}}
38
+ s.files = files
39
+ end
40
+
41
+ #task :default => :spec
42
+
43
+ desc "Run specs"
44
+ Spec::Rake::SpecTask.new do |t|
45
+ t.spec_files = FileList['spec/**/*_spec.rb']
46
+ #t.spec_opts = %w(-fs --color)
47
+ t.spec_opts = %w(-fs --color --debug)
48
+ end
49
+
50
+
51
+ Rake::GemPackageTask.new(spec) do |pkg|
52
+ pkg.gem_spec = spec
53
+ end
54
+
55
+ #desc "install the gem locally"
56
+ #task :install => [:package] do
57
+ # sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
58
+ #end
59
+
60
+ desc "create a gemspec file"
61
+ task :make_spec do
62
+ File.open("#{GEM}.gemspec", "w") do |file|
63
+ file.puts spec.to_ruby
64
+ end
65
+ end
66
+
67
+
68
+ desc "run unit tests"
69
+ Rake::TestTask.new("unit_tests") {|t|
70
+ t.pattern = 'test/*_test.rb'
71
+ t.verbose = true
72
+ t.warning = true
73
+ }
74
+
75
+
76
+ desc "Run RDoc to generate documentation"
77
+ Rake::RDocTask.new do |rd|
78
+ rd.rdoc_dir = 'doc/rdocs'
79
+ rd.main = 'README'
80
+ rd.rdoc_files.include 'README', "lib/**/*\.rb"
81
+ rd.options << '--all'
82
+ rd.options << '--inline-source'
83
+ #rd.options << '--line-numbers'
84
+ end
85
+
86
+
87
+ desc "Cucumber features"
88
+ Cucumber::Rake::Task.new(:features) do |t|
89
+ t.cucumber_opts = "features --format pretty"
90
+ end
91
+
92
+
93
+ desc "Make feature files from CukeHead mind map"
94
+ task 'cukehead' do
95
+ sh "bin/cukehead cuke -m mm/CukeHead.mm -o"
96
+ end
97
+
98
+
99
+ task :cuke => [:cukehead, :features]
100
+
101
+ task :default => [:unit_tests, :spec, :cuke]
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
+ require 'cukehead'
5
+
6
+ app = Cukehead::App.new
7
+ app.run
@@ -0,0 +1 @@
1
+ require 'cukehead/app'
@@ -0,0 +1,241 @@
1
+ require 'getoptlong'
2
+ require 'fileutils'
3
+ require 'cukehead/feature_reader'
4
+ require 'cukehead/freemind_writer'
5
+ require 'cukehead/freemind_reader'
6
+ require 'cukehead/feature_writer'
7
+
8
+ module Cukehead
9
+
10
+ # The Cukehead::App class is responsible for responding to
11
+ # command line arguments and providing the main functionality
12
+ # of the cukehead application.
13
+ #
14
+ class App
15
+ attr_accessor :features_path
16
+ attr_accessor :mindmap_filename
17
+ attr_accessor :mindmap_template_filename
18
+ attr_accessor :do_overwrite
19
+ attr_reader :errors
20
+ attr_reader :feature_reader
21
+
22
+ def initialize
23
+ @command = ''
24
+ @features_path = File.join(Dir.getwd, 'features')
25
+ @mindmap_filename = File.join(Dir.getwd, 'mm', 'cukehead-output.mm')
26
+ @mindmap_template_filename = ''
27
+ @feature_reader = nil
28
+ @mindmap_reader = nil
29
+ @do_overwrite = false
30
+ @errors = []
31
+ end
32
+
33
+
34
+ # Main entry point for executing the cukehead application.
35
+ # Responsible for responding to the command line arguments.
36
+ #
37
+ def run
38
+ get_options
39
+ if @errors.empty?
40
+ if @command == 'map'
41
+ read_features
42
+ write_mindmap
43
+ show_errors
44
+ elsif @command == 'cuke'
45
+ read_mindmap
46
+ write_features
47
+ show_errors
48
+ else
49
+ show_help
50
+ end
51
+ else
52
+ show_errors
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def read_features
59
+ @feature_reader = FeatureReader.new get_source_xml
60
+ search_path = File.join @features_path, '*.feature'
61
+ puts "Reading #{search_path}"
62
+ Dir[search_path].sort.each {|filename|
63
+ File.open(filename, 'r') {|f|
64
+ text = f.readlines
65
+ @feature_reader.extract_features filename, text #unless text.nil?
66
+ }
67
+ }
68
+ end
69
+
70
+
71
+ def write_mindmap
72
+ if @feature_reader.nil?
73
+ @errors << "No features to write (perhaps read_features has not been called)"
74
+ return false
75
+ end
76
+ if @do_overwrite != true and File.exists? @mindmap_filename
77
+ @errors << "File already exists: #{@mindmap_filename}"
78
+ return false
79
+ end
80
+ dir = File.dirname(@mindmap_filename)
81
+ FileUtils.mkdir_p(dir) unless File.directory? dir
82
+ writer = FreemindWriter.new
83
+ puts "Writing " + @mindmap_filename
84
+ writer.write_mm @mindmap_filename, @feature_reader.freemind_xml
85
+ return true
86
+ end
87
+
88
+
89
+ def default_mm_search_path
90
+ File.join(Dir.getwd, 'mm', '*.mm')
91
+ end
92
+
93
+
94
+ def read_mindmap
95
+ puts "Reading #{@mindmap_filename}"
96
+ begin
97
+ @mindmap_reader = FreemindReader.new @mindmap_filename
98
+ rescue => ex
99
+ @errors << 'Error in read_mindmap: ' + ex.message
100
+ end
101
+ end
102
+
103
+
104
+ def write_features
105
+ if @mindmap_reader.nil?
106
+ @errors << "No mind map data."
107
+ return false
108
+ end
109
+ begin
110
+ writer = FeatureWriter.new
111
+ writer.output_path = @features_path
112
+ writer.overwrite = @do_overwrite
113
+ features = @mindmap_reader.get_features
114
+ if features.empty?
115
+ @errors << 'No Cucumber features found in the mind map file.'
116
+ @errors << 'Mind map may be missing a "Cucumber features:" node.'
117
+ else
118
+ features.each_key {|filename| puts "Writing #{File.join(@features_path, filename)}"}
119
+ writer.write_features features
120
+ @errors << writer.errors unless writer.errors.empty?
121
+ end
122
+ rescue => ex
123
+ @errors << 'Error in write_features: ' + ex.message
124
+ end
125
+ end
126
+
127
+
128
+ def get_source_xml
129
+ if @mindmap_template_filename.empty?
130
+ nil
131
+ else
132
+ text = nil
133
+ File.open(@mindmap_template_filename, 'r') {|f|
134
+ text = f.readlines
135
+ }
136
+ text.join
137
+ end
138
+ end
139
+
140
+
141
+ def default_mm_file
142
+ Dir[default_mm_search_path].first
143
+ end
144
+
145
+
146
+ def get_options
147
+ mm = ''
148
+ fp = ''
149
+ begin
150
+ opts = GetoptLong.new(
151
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
152
+ ['--overwrite', '-o', GetoptLong::NO_ARGUMENT],
153
+ ['--mm-filename', '-m', GetoptLong::REQUIRED_ARGUMENT],
154
+ ['--features-path', '-f', GetoptLong::REQUIRED_ARGUMENT],
155
+ ['--source-mm', '-s', GetoptLong::REQUIRED_ARGUMENT]
156
+ )
157
+ opts.each do |opt, arg|
158
+ case
159
+ when opt == '--help' then @command = 'help'
160
+ when opt == '--overwrite' then @do_overwrite = true
161
+ when opt == '--mm-filename' then mm = arg
162
+ when opt == '--features-path' then fp = arg
163
+ when opt == '--source-mm' then @mindmap_template_filename = arg
164
+ end
165
+ end
166
+ rescue => ex
167
+ @errors << ex.message
168
+ end
169
+
170
+ # If features path or mindmap file name was not specified
171
+ # in option arguments then infer them from any remaining
172
+ # command line arguments. Specific option takes precidence.
173
+ ARGV.each do |a|
174
+ if a == 'map'
175
+ @command = 'map' if @command == ''
176
+ elsif a == 'cuke'
177
+ @command = 'cuke' if @command == ''
178
+ elsif a.slice(-9, 9) == '/features'
179
+ fp = a if fp.empty?
180
+ elsif a.slice(-3, 3) == '.mm'
181
+ mm = a if mm.empty?
182
+ else
183
+ @errors << "Unknown command or option: #{a}"
184
+ end
185
+ end
186
+
187
+ @features_path = fp unless fp.empty?
188
+ @mindmap_filename = mm unless mm.empty?
189
+ end
190
+
191
+
192
+ def show_errors
193
+ unless @errors.empty?
194
+ puts "Errors:"
195
+ @errors.each {|err| puts err}
196
+ end
197
+ end
198
+
199
+
200
+ def show_help
201
+ puts <<xxx
202
+
203
+ Usage: cukehead command [options]
204
+
205
+ command:
206
+ map
207
+ Read Cucumber feature files and create a FreeMind mind map file.
208
+
209
+ cuke
210
+ Read a FreeMind mind map file and create Cucumber feature files.
211
+
212
+ options:
213
+ -h or --help
214
+ Show this help message.
215
+
216
+ -o or --overwrite
217
+ Overwrite existing output file(s).
218
+
219
+ -m FILENAME or --mm-filename FILENAME
220
+ map: Name of output file (default is mm/cukehead-output.mm).
221
+
222
+ cuke: Name of mind map file containing Cucumber features.
223
+
224
+ -f PATH or --features-path PATH
225
+ map: Directory containing feature files to read (default is directory
226
+ named 'features' in current directory).
227
+
228
+ cuke: Directory feature files will be written to.
229
+
230
+ -s FILENAME or --source-mm FILENAME
231
+ map: FreeMind mind map file to use as a template for creating
232
+ the output file. If the template contains a node with the text
233
+ 'Cucumber features:' then the feature nodes will be inserted there.
234
+
235
+ xxx
236
+ end
237
+
238
+
239
+ end
240
+
241
+ end
@@ -0,0 +1,73 @@
1
+ require 'cukehead/feature_part'
2
+
3
+ module Cukehead
4
+
5
+ # Base class for a container that holds a part of a Cucumber
6
+ # feature file that will be passed to a FreemindBuilder object
7
+ # once the part has been read.
8
+ #
9
+ class FeatureFileSection
10
+
11
+ # ===Parameters
12
+ # <tt>builder</tt> - Instance of FreemindBuilder that will receive this section of the file.
13
+ #
14
+ # <tt>title</tt> - String containing the title of the section.
15
+ #
16
+ def initialize(builder, title)
17
+ @builder = builder
18
+ @part = FeaturePart.new title
19
+ end
20
+
21
+
22
+ def add(line)
23
+ @part.add_line(line)
24
+ end
25
+
26
+
27
+ def set_tags(tags)
28
+ @part.tags = tags.clone
29
+ end
30
+
31
+
32
+ def finish
33
+ raise "Not implemented"
34
+ end
35
+
36
+ end
37
+
38
+
39
+ # Serves as a container for the feature description part of a Cucumber
40
+ # feature file while parsing the file.
41
+ #
42
+ class FeatureSection < FeatureFileSection
43
+
44
+ def initialize (builder, text, filename)
45
+ @feature_filename = filename
46
+ super builder, text
47
+ end
48
+
49
+ def finish
50
+ @builder.add_feature(@part, @feature_filename)
51
+ end
52
+
53
+ end
54
+
55
+
56
+ class BackgroundSection < FeatureFileSection
57
+
58
+ def finish
59
+ @builder.add_background(@part)
60
+ end
61
+
62
+ end
63
+
64
+
65
+ class ScenarioSection < FeatureFileSection
66
+
67
+ def finish
68
+ @builder.add_scenario(@part)
69
+ end
70
+
71
+ end
72
+
73
+ end