cukedep 0.0.8 → 0.1.03
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.
- checksums.yaml +8 -8
- data/CHANGELOG.md +26 -0
- data/bin/cukedep +1 -1
- data/lib/cukedep/application.rb +7 -7
- data/lib/cukedep/config.rb +64 -7
- data/lib/cukedep/constants.rb +5 -1
- data/lib/cukedep/cuke-runner.rb +202 -0
- data/lib/cukedep/customization.rb +35 -0
- data/lib/cukedep/feature-rep.rb +2 -2
- data/lib/cukedep/file-action.rb +211 -0
- data/lib/cukedep/hook-dsl.rb +80 -0
- data/lib/cukedep/sandbox.rb +21 -0
- data/sample/cucumber.yml +4 -0
- data/sample/model/catalogue.yml +13 -0
- data/sample/model/members.yml +13 -0
- data/sample/model/model.rb +2 -2
- data/sample/model/rentals.yml +4 -0
- data/sample/model/users.yml +7 -0
- data/sample/result.html +472 -0
- data/spec/cukedep/application_spec.rb +8 -8
- data/spec/cukedep/cuke-runner_spec.rb +80 -0
- data/spec/cukedep/customization_spec.rb +36 -0
- data/spec/cukedep/file-action_spec.rb +374 -0
- data/spec/cukedep/gherkin-facade_spec.rb +54 -0
- data/spec/cukedep/hook-dsl_spec.rb +185 -0
- data/spec/cukedep/sample_features/cukedep.rake +204 -0
- data/spec/cukedep/sample_features/cukedep_hooks.rb +30 -0
- data/spec/cukedep/sample_features/dependencies.dot +38 -0
- data/spec/cukedep/sample_features/feature2id.csv +7 -0
- data/spec/cukedep/sample_features/files_to_copy/README.md +12 -0
- data/spec/cukedep/sample_features/files_to_copy/file1.txt +5 -0
- data/spec/cukedep/sample_features/files_to_copy/file2.txt +5 -0
- data/spec/cukedep/sample_features/files_to_copy/file3.txt +9 -0
- data/spec/spec_helper.rb +1 -0
- data/templates/rake.erb +184 -160
- metadata +26 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NTMzODc5ODIyYzYyNzk4YmIxMzA0ODU3MzNkZWUxYTRkYTcwNDgxZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTE0OTg5NTlhNTI1NGFiODJlYTI0NzgyN2U1N2IyZGUzZjJmZDRhYQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YWIwY2NlODYwMDE3NzRiNWRmZTE4NjUyZTc0NzkwYmY2MjdhMTU5ZWFiMmQx
|
10
|
+
ZDY5ZjkwMGRiNGFlMTk0MjNmY2IyN2YyMzBiY2UwZTFkNmQ2NmRjYTU0N2Ni
|
11
|
+
NGY1MjZmZjZiYjQzYzBjMWY3YjViMTZkYmViNTBhZWFlOGEwZTc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YmU1Zjg1MDA2OTdmNzg3ZDZiN2RmMWRiM2YwZTgxNTNmZGViNDJlMWY2MDZh
|
14
|
+
NjE1ZTNjZmJlNjBhOGJhYmMyNGEyODNiMzdmYTY2MTlmODhiZmU2ZGM1ZTlm
|
15
|
+
NWQwMmI4YzJiMjAwMjc1YzFiMjljMDRhZDA0YmM4MzIzNWQ4NWE=
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
### 0.1.03 / 2013-11-28
|
2
|
+
* [FIX] Failing execution of generated Rake file in Travis CI. Cause: Rake template always referenced class in gem while it should reference development code while self-testing.
|
3
|
+
* [FIX] Added three missing test data files in the committed version.
|
4
|
+
|
5
|
+
### 0.1.02 / 2013-11-27
|
6
|
+
* [FIX] Failing test again in `file-action_spec.rb` file. Cause: Commit to GitHub doesn't copy empty dir!. Added directory creation code.
|
7
|
+
|
8
|
+
### 0.1.01 / 2013-11-27
|
9
|
+
* [FIX] Failing test in `file-action_spec.rb` file. Cause: Commit to GitHub doesn't copy empty dir!. Added directory creation code.
|
10
|
+
|
11
|
+
### 0.1.00 / 2013-11-27
|
12
|
+
* [FEATURE] Customized file actions (save, delete, copy) associated with invocation event
|
13
|
+
* [FEATURE] Hooks for further invocation event customization.
|
14
|
+
* [NEW] `Cukedep::CukeRunner` class. Responsibilities: -Invokes Cucumber, handles the invocation events.
|
15
|
+
* [NEW] `Cukedep::Customization` class. Responsibilities: -Loads custom hook code blocks (in `cukedep_hooks.rb`)
|
16
|
+
* [NEW] `Cukedep::FileAction` class hierarchy. Responsibilities: specify the actions to perform before/after Cucumber invocation(s).
|
17
|
+
* [NEW] `Cukedep::HookDSL` module. Used to define a DSL (Domain Specific Language)
|
18
|
+
* [NEW] `Cukedep::Sandbox`. Responsibilities: Gives the context in which hook code block are executed.
|
19
|
+
* [NEW] `Cukedep::Config` class: new methods: `load_cfg`, `write`, `file_action_attrs`
|
20
|
+
* [CHANGE] Method `Application#run!`: Using new interface of `Cukedep::Config` class.
|
21
|
+
|
22
|
+
|
23
|
+
### 0.0.9 / 2013-11-04 [unreleased]
|
24
|
+
* [CHANGE] For uniformity reasons, method `Application#start!` renamed to `Application#run!`
|
25
|
+
* [FIX] Method `Application#run!`: incorrect string interpolation in error message.
|
26
|
+
|
1
27
|
### 0.0.8 / 2013-10-31
|
2
28
|
* [Fix] After adding a non-ASCII in a sample feature, Gherkin raised an encoding incompatibility error. Fixed
|
3
29
|
* [CHANGE] Added a new field 'feature_encoding' in Config object to store the encoding of feature files (default = 'UTF-8')
|
data/bin/cukedep
CHANGED
data/lib/cukedep/application.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# File: application.rb
|
2
2
|
|
3
|
-
require 'yaml'
|
4
3
|
|
5
4
|
require_relative 'cli/cmd-line'
|
6
5
|
require_relative 'config'
|
@@ -16,17 +15,17 @@ class Application
|
|
16
15
|
public
|
17
16
|
|
18
17
|
# Entry point for the application object.
|
19
|
-
def
|
18
|
+
def run!(theCmdLineArgs)
|
20
19
|
options = options_from(theCmdLineArgs)
|
21
20
|
create_default_cfg if options[:setup]
|
22
|
-
config = load_cfg
|
21
|
+
config = Config.load_cfg(Cukedep::YMLFilename)
|
23
22
|
|
24
23
|
# Complain if no project dir is specified
|
25
24
|
if config.proj_dir.nil? || config.proj_dir.empty?
|
26
25
|
if options[:project]
|
27
26
|
@proj_dir = options[:project]
|
28
27
|
else
|
29
|
-
msg_p1 = "No project dir specified
|
28
|
+
msg_p1 = "No project dir specified in '#{Cukedep::YMLFilename}'"
|
30
29
|
msg_p2 = ' nor via --project option.'
|
31
30
|
fail(StandardError, msg_p1 + msg_p2)
|
32
31
|
end
|
@@ -62,10 +61,11 @@ protected
|
|
62
61
|
answer = $stdin.gets
|
63
62
|
exit if answer =~ /^\s*[Nn]\s*$/
|
64
63
|
end
|
65
|
-
|
64
|
+
Config.default.write(Cukedep::YMLFilename)
|
65
|
+
|
66
66
|
exit
|
67
67
|
end
|
68
|
-
|
68
|
+
=begin
|
69
69
|
# Read the .cukedep.yml file in the current working directory
|
70
70
|
def load_cfg()
|
71
71
|
if File.exist?(Cukedep::YMLFilename)
|
@@ -74,7 +74,7 @@ protected
|
|
74
74
|
Config.default
|
75
75
|
end
|
76
76
|
end
|
77
|
-
|
77
|
+
=end
|
78
78
|
# Parse the feature files (with the specified external encoding)
|
79
79
|
def parse_features(external_encoding)
|
80
80
|
# Create a Gherkin listener
|
data/lib/cukedep/config.rb
CHANGED
@@ -1,34 +1,91 @@
|
|
1
1
|
# File: config.rb
|
2
2
|
|
3
|
+
require 'yaml'
|
4
|
+
require_relative 'file-action'
|
5
|
+
|
3
6
|
module Cukedep # Module used as a namespace
|
4
7
|
|
5
8
|
FileMetaData = Struct.new(:name)
|
6
9
|
|
7
10
|
Config = Struct.new(
|
8
|
-
:feature_encoding, # The encoding of feature files
|
11
|
+
:feature_encoding, # The character encoding of feature files
|
9
12
|
:proj_dir, # The directory of the cucumber project
|
10
13
|
:feature2id, # Meta-data about the feature => feature id report
|
11
14
|
:id2feature, # Meta-data about the feature id => feature report
|
12
15
|
:graph_file, # Meta-data about the dependency graph file
|
13
16
|
:rake_file, # Name of the output rake file
|
14
|
-
:cucumber_args # Command-line syntax to use for the cucumber application
|
17
|
+
:cucumber_args, # Command-line syntax to use for the cucumber application
|
18
|
+
# File actions triggered at Cucumber invocation events
|
19
|
+
:before_all_f_actions,
|
20
|
+
:before_each_f_actions,
|
21
|
+
:after_each_f_actions,
|
22
|
+
:after_all_f_actions
|
15
23
|
)
|
16
24
|
|
17
25
|
# Re-open the class for further customisation
|
18
|
-
|
26
|
+
|
19
27
|
# Configuration object for the Cukedep application.
|
20
28
|
class Config
|
21
29
|
# Factory method. Build a config object with default settings.
|
22
30
|
def self.default()
|
23
|
-
Config.new(
|
31
|
+
instance = Config.new(
|
24
32
|
'UTF-8',
|
25
|
-
nil,
|
26
|
-
FileMetaData.new('feature2id.csv'),
|
27
|
-
FileMetaData.new('feature2id.csv'),
|
33
|
+
nil,
|
34
|
+
FileMetaData.new('feature2id.csv'),
|
35
|
+
FileMetaData.new('feature2id.csv'),
|
28
36
|
FileMetaData.new('dependencies.dot'),
|
29
37
|
'cukedep.rake',
|
30
38
|
[]
|
31
39
|
)
|
40
|
+
|
41
|
+
file_action_attrs.each do |attr|
|
42
|
+
instance[attr] = empty_action_triplet
|
43
|
+
end
|
44
|
+
|
45
|
+
return instance
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Read the YAML file with specified name from the current working directory.
|
50
|
+
# If that file does not exist, then return an instance with default values.
|
51
|
+
def self.load_cfg(filename)
|
52
|
+
# TODO: validation
|
53
|
+
instance = File.exist?(filename) ? YAML.load_file(filename) : self.default
|
54
|
+
|
55
|
+
return instance
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# Save the Config object to a YAML file.
|
60
|
+
def write(filename)
|
61
|
+
File.open(filename, 'w') { |f| YAML.dump(self, f) }
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Purpose: get the list of attributes referencing
|
68
|
+
# a file action triplet.
|
69
|
+
def self.file_action_attrs()
|
70
|
+
return [
|
71
|
+
:before_all_f_actions,
|
72
|
+
:before_each_f_actions,
|
73
|
+
:after_each_f_actions,
|
74
|
+
:after_all_f_actions
|
75
|
+
]
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# Return Hash config for a no-op action triplet.
|
80
|
+
def self.empty_action_triplet()
|
81
|
+
{
|
82
|
+
save_patterns: [],
|
83
|
+
save_subdir: '',
|
84
|
+
delete_patterns: [],
|
85
|
+
delete_subdir: '',
|
86
|
+
copy_patterns: [],
|
87
|
+
copy_subdir: ''
|
88
|
+
}
|
32
89
|
end
|
33
90
|
|
34
91
|
end # class
|
data/lib/cukedep/constants.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
module Cukedep # Module used as a namespace
|
5
5
|
# The version number of the gem.
|
6
|
-
Version = '0.
|
6
|
+
Version = '0.1.03'
|
7
7
|
|
8
8
|
# Brief description of the gem.
|
9
9
|
Description = 'Manage dependencies between Cucumber feature files'
|
@@ -23,6 +23,10 @@ module Cukedep # Module used as a namespace
|
|
23
23
|
|
24
24
|
# The file name for the user's settings
|
25
25
|
YMLFilename = '.cukedep.yml'
|
26
|
+
|
27
|
+
# The file name for the custom block codes associated
|
28
|
+
# with before/after events.
|
29
|
+
HookFilename = 'cukedep_hooks.rb'
|
26
30
|
end
|
27
31
|
end # module
|
28
32
|
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# File: cuke-runner.rb
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
# Run Cucumber via specialized Rake task
|
7
|
+
require 'cucumber/rake/task'
|
8
|
+
|
9
|
+
require_relative 'file-action'
|
10
|
+
require_relative 'customization'
|
11
|
+
|
12
|
+
# UGLY workaround for bug in Cucumber's rake task
|
13
|
+
if Gem::VERSION[0].to_i >= 2 && Cucumber::VERSION <= '1.3.2'
|
14
|
+
# Monkey-patch a buggy method
|
15
|
+
class Cucumber::Rake::Task::ForkedCucumberRunner
|
16
|
+
def gem_available?(gemname)
|
17
|
+
if Gem::VERSION[0].to_i >= 2
|
18
|
+
gem_available_new_rubygems?(gemname)
|
19
|
+
else
|
20
|
+
gem_available_old_rubygems?(gemname)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end # class
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
module Cukedep # This module is used as a namespace
|
29
|
+
|
30
|
+
# Purpose: to launch Cucumber in the appropriate directory
|
31
|
+
# and pass it command-line arguments.
|
32
|
+
# Responsibilities:
|
33
|
+
# Know how to invoke Cucumber
|
34
|
+
# Know the base directory
|
35
|
+
# Know the project's root dir
|
36
|
+
class CukeRunner
|
37
|
+
# The current state of the runner.
|
38
|
+
attr_reader(:state)
|
39
|
+
|
40
|
+
# The absolute path of the root's project directory
|
41
|
+
attr_reader(:proj_dir)
|
42
|
+
attr_reader(:base_dir)
|
43
|
+
attr_reader(:config)
|
44
|
+
attr_reader(:handlers)
|
45
|
+
|
46
|
+
attr(:cucumber_opts, true)
|
47
|
+
|
48
|
+
# Constructor
|
49
|
+
def initialize(baseDir, projectDir, aConfig)
|
50
|
+
@base_dir = baseDir
|
51
|
+
@proj_dir = validated_proj_dir(projectDir)
|
52
|
+
@config = aConfig
|
53
|
+
@handlers = Customization.new.build_handlers(baseDir)
|
54
|
+
|
55
|
+
@state = :Initialized
|
56
|
+
end
|
57
|
+
|
58
|
+
# Launch Cucumber in the project directory.
|
59
|
+
def invoke()
|
60
|
+
options = [] # TODO: retrieve Cucumber options
|
61
|
+
orig_dir = Dir.getwd()
|
62
|
+
Dir.chdir(proj_dir)
|
63
|
+
|
64
|
+
begin
|
65
|
+
cuke_task = Cucumber::Rake::Task.new do |t|
|
66
|
+
t.cucumber_opts = options
|
67
|
+
end
|
68
|
+
|
69
|
+
cuke_task.runner.run()
|
70
|
+
rescue SystemExit => exc # Cucumber reports a failure.
|
71
|
+
raise StandardError, "Cucumber exited with status #{exc.status}"
|
72
|
+
ensure
|
73
|
+
Dir.chdir(orig_dir)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Event handler that is triggered
|
78
|
+
# before any other event.
|
79
|
+
# It executes the before all hook first.
|
80
|
+
# Then it executes in the following order:
|
81
|
+
# Built-in save action, Custom save action
|
82
|
+
# Built-in delete action, Custom delete action
|
83
|
+
# Built-in copy action, Custom copy action
|
84
|
+
def before_all()
|
85
|
+
expected_state(:Initialized)
|
86
|
+
|
87
|
+
# Execute before all hook code
|
88
|
+
run_code_block
|
89
|
+
|
90
|
+
# Execute file actions
|
91
|
+
builtin_actions = ActionTriplet.builtin(:before_all)
|
92
|
+
custom_actions = ActionTriplet.new(config.before_all_f_actions)
|
93
|
+
run_triplets([builtin_actions, custom_actions])
|
94
|
+
@state = :ReadyToRun
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Event handler that is triggered
|
99
|
+
# after any other event.
|
100
|
+
# It executes first actions in the following order:
|
101
|
+
# Built-in save action, Custom save action
|
102
|
+
# Built-in delete action, Custom delete action
|
103
|
+
# Built-in copy action, Custom copy action
|
104
|
+
# Then it executes the after all hook last.
|
105
|
+
def after_all()
|
106
|
+
expected_state(:ReadyToRun)
|
107
|
+
|
108
|
+
builtin_actions = ActionTriplet.builtin(:after_all)
|
109
|
+
custom_actions = ActionTriplet.new(config.after_all_f_actions)
|
110
|
+
run_triplets([builtin_actions, custom_actions])
|
111
|
+
|
112
|
+
# Execute before all hook code
|
113
|
+
run_code_block
|
114
|
+
@state = :Complete
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def run!(fileNames)
|
119
|
+
expected_state(:ReadyToRun)
|
120
|
+
before_each(fileNames)
|
121
|
+
invoke
|
122
|
+
after_each
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
private
|
127
|
+
def validated_proj_dir(projectDir)
|
128
|
+
path = Pathname.new(projectDir)
|
129
|
+
path = path.expand_path if path.relative?
|
130
|
+
unless path.exist?
|
131
|
+
raise StandardError, "No such project path: '#{path}'"
|
132
|
+
end
|
133
|
+
|
134
|
+
return path.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
def expected_state(aState)
|
138
|
+
unless state == aState
|
139
|
+
msg = "expected state was '#{aState}' instead of '#{state}'."
|
140
|
+
raise StandardError, msg
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def before_each(fileNames)
|
146
|
+
# Execute before each hook code
|
147
|
+
run_code_block(fileNames)
|
148
|
+
|
149
|
+
builtin_actions = ActionTriplet.builtin(:before_each).dup
|
150
|
+
unless builtin_actions.nil?
|
151
|
+
builtin_actions.copy_action.patterns = fileNames
|
152
|
+
end
|
153
|
+
|
154
|
+
custom_actions = ActionTriplet.new(config.before_each_f_actions)
|
155
|
+
run_triplets([builtin_actions, custom_actions])
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
def after_each()
|
160
|
+
builtin_actions = ActionTriplet.builtin(:after_each)
|
161
|
+
|
162
|
+
custom_actions = ActionTriplet.new(config.after_each_f_actions)
|
163
|
+
run_triplets([builtin_actions, custom_actions])
|
164
|
+
|
165
|
+
# Execute after each hook code
|
166
|
+
run_code_block
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
def run_triplets(theTriplets)
|
171
|
+
all_triplets = theTriplets.compact # Remove nil elements
|
172
|
+
|
173
|
+
# Do all save actions...
|
174
|
+
all_triplets.each { |t| t.save_action.run!(proj_dir, base_dir) }
|
175
|
+
|
176
|
+
# Do all delete actions...
|
177
|
+
all_triplets.each { |t| t.delete_action.run!(proj_dir) }
|
178
|
+
|
179
|
+
# Do all copy actions...
|
180
|
+
all_triplets.each { |t| t.copy_action.run!(base_dir, proj_dir) }
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
def run_code_block(*args)
|
185
|
+
# Retrieve the name of the parent method.
|
186
|
+
parent_mth = (caller[0].sub(/^(.+):in (.+)$/, "\\2"))[1..-2]
|
187
|
+
kind, scope = parent_mth.split('_')
|
188
|
+
hook_kind = (kind + '_hooks')
|
189
|
+
|
190
|
+
kode = handlers[hook_kind.to_sym][scope.to_sym]
|
191
|
+
unless kode.nil?
|
192
|
+
safe_args = args.map { |one_arg| one_arg.dup.freeze }
|
193
|
+
kode.call(*safe_args)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
end # class
|
199
|
+
|
200
|
+
end # module
|
201
|
+
|
202
|
+
# End of file
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# File: customization.rb
|
2
|
+
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'hook-dsl'
|
5
|
+
|
6
|
+
module Cukedep # This module is used as a namespace
|
7
|
+
|
8
|
+
|
9
|
+
class Customization
|
10
|
+
|
11
|
+
# Retrieve before/after handlers from file
|
12
|
+
# Handlers are put in a Hash with keys :before_hooks, :after_hooks.
|
13
|
+
def build_handlers(directory)
|
14
|
+
handlers = nil
|
15
|
+
|
16
|
+
filepath = directory + '/' + Cukedep::HookFilename
|
17
|
+
if File.exist? filepath
|
18
|
+
obj = Object.new
|
19
|
+
obj.extend(HookDSL)
|
20
|
+
hook_source = File.read(filepath)
|
21
|
+
obj.instance_eval(hook_source)
|
22
|
+
handlers = {
|
23
|
+
before_hooks: obj.before_hooks,
|
24
|
+
after_hooks: obj.after_hooks
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
return handlers
|
29
|
+
end
|
30
|
+
|
31
|
+
end # class
|
32
|
+
|
33
|
+
end # module
|
34
|
+
|
35
|
+
# End of file
|