cukehead 0.1.1

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