cog 0.0.19 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cog.rb +5 -2
- data/lib/cog/config.rb +26 -20
- data/lib/cog/config/cogfile.rb +21 -20
- data/lib/cog/generator.rb +51 -60
- data/lib/cog/helpers.rb +1 -0
- data/lib/cog/helpers/string.rb +11 -0
- data/lib/cog/spec_helpers.rb +41 -11
- data/lib/cog/spec_helpers/matchers.rb +14 -7
- data/lib/cog/spec_helpers/matchers/match_maker.rb +39 -17
- data/lib/cog/spec_helpers/runner.rb +18 -10
- data/lib/cog/tool.rb +7 -7
- data/lib/cog/version.rb +3 -1
- data/templates/{cog/snippets/c++/generated_warning.h.erb → warning.h.erb} +0 -0
- data/yard-templates/default/fulldoc/html/css/common.css +6 -0
- metadata +13 -16
- data/API.rdoc +0 -6
- data/templates/cog/snippets/generated_warning.txt +0 -7
data/lib/cog.rb
CHANGED
@@ -4,11 +4,13 @@ require 'cog/tool'
|
|
4
4
|
require 'cog/version'
|
5
5
|
require 'fileutils'
|
6
6
|
|
7
|
-
#
|
8
|
-
# the
|
7
|
+
# +cog+ is a command line utility that makes it a bit easier to organize a project
|
8
|
+
# which uses code generation. These are the API docs, but you might want to read
|
9
|
+
# the {https://github.com/ktonon/cog#readme general introduction} first.
|
9
10
|
module Cog
|
10
11
|
|
11
12
|
# Prepare the project in the present working directory for use with +cog+
|
13
|
+
# @return [nil]
|
12
14
|
def self.initialize_project
|
13
15
|
Object.new.instance_eval do
|
14
16
|
class << self ; include Generator ; end
|
@@ -16,6 +18,7 @@ module Cog
|
|
16
18
|
config = Config.instance
|
17
19
|
touch_path config.project_generators_path
|
18
20
|
touch_path config.project_templates_path
|
21
|
+
nil
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
data/lib/cog/config.rb
CHANGED
@@ -3,50 +3,57 @@ require 'singleton'
|
|
3
3
|
|
4
4
|
module Cog
|
5
5
|
|
6
|
-
# This is a low level interface. It is mainly used by the Generator methods
|
6
|
+
# This is a low level interface. It is mainly used by the {Generator} methods
|
7
7
|
# to determine where to find things, and where to put them. When +cog+ is used
|
8
|
-
# in a project the values of the singleton Config
|
9
|
-
# a Cogfile.
|
8
|
+
# in a project the values of the singleton {Config.instance} should be configured using
|
9
|
+
# a {Cogfile}.
|
10
10
|
class Config
|
11
11
|
|
12
|
-
#
|
12
|
+
# @return [String] path to the project's {Cogfile}
|
13
13
|
attr_reader :cogfile_path
|
14
14
|
|
15
|
-
#
|
15
|
+
# @return [String] default language in which to generated application source code
|
16
16
|
attr_reader :language
|
17
17
|
|
18
|
-
#
|
18
|
+
# @return [String] directory in which to find project generators
|
19
19
|
attr_reader :project_generators_path
|
20
20
|
|
21
|
-
#
|
21
|
+
# @return [String] directory in which the project's {Cogfile} is found
|
22
22
|
attr_reader :project_root
|
23
23
|
|
24
|
-
#
|
24
|
+
# @return [String] directory to which project source code is generated
|
25
25
|
attr_reader :project_source_path
|
26
26
|
|
27
|
-
#
|
27
|
+
# @return [String] directory in which to find custom project templates
|
28
28
|
attr_reader :project_templates_path
|
29
29
|
|
30
|
-
#
|
31
|
-
# That is, could a Cogfile be found?
|
30
|
+
# @return [Boolean] are we operating in the context of a project?
|
32
31
|
def project?
|
33
32
|
!@project_root.nil?
|
34
33
|
end
|
35
34
|
|
36
|
-
#
|
35
|
+
# @param value [String] a value which is set by the active tool
|
37
36
|
attr_writer :tool_templates_path
|
38
37
|
|
39
|
-
#
|
38
|
+
# @return [String] a value which is set by the active tool
|
40
39
|
attr_accessor :tool_generator_template
|
41
40
|
|
42
|
-
# A list of directories in which to find ERB template files
|
43
|
-
#
|
41
|
+
# A list of directories in which to find ERB template files
|
42
|
+
#
|
43
|
+
# Templates should be looked for in the following order as determined by the list returned from this method
|
44
|
+
#
|
45
|
+
# * {#project_templates_path}, present if we are in the context of a {#project?}
|
46
|
+
# * tool templates, present if we are in the context of a tool (i.e. a tool has set {#tool_templates_path=})
|
47
|
+
# * +cog+ built-in templates, always present
|
48
|
+
#
|
49
|
+
# @return [Array<String>] a list of directories order with ascending priority
|
44
50
|
def template_paths
|
45
51
|
[@project_templates_path, @tool_templates_path, File.join(Config.gem_dir, 'templates')].compact
|
46
52
|
end
|
47
53
|
|
48
54
|
# Location of the installed cog gem
|
49
|
-
|
55
|
+
# @return [String] path to the cog gem
|
56
|
+
def self.gem_dir
|
50
57
|
spec = Gem.loaded_specs['cog']
|
51
58
|
if spec.nil?
|
52
59
|
# The current __FILE__ is:
|
@@ -57,18 +64,17 @@ module Cog
|
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
|
-
# The singleton instance
|
67
|
+
# The singleton instance
|
61
68
|
#
|
62
69
|
# Initialized using the Cogfile for the current project, if any can be
|
63
|
-
# found. If not, then #project? will be +false+ and all the +project_...+
|
70
|
+
# found. If not, then {#project?} will be +false+ and all the +project_...+
|
64
71
|
# attributes will be +nil+.
|
65
72
|
#
|
66
73
|
# The Cogfile will be looked for in the present working directory. If none
|
67
74
|
# is found there the parent directory will be checked, and then the
|
68
75
|
# grandparent, and so on.
|
69
76
|
#
|
70
|
-
#
|
71
|
-
# An instance of Config.
|
77
|
+
# @return [Config] the singleton Config instance
|
72
78
|
def self.instance
|
73
79
|
return @instance if @instance
|
74
80
|
@instance = self.new
|
data/lib/cog/config/cogfile.rb
CHANGED
@@ -3,32 +3,35 @@ module Cog
|
|
3
3
|
|
4
4
|
# In your project's +Cogfile+, +self+ has been set to an instance of this class.
|
5
5
|
#
|
6
|
-
#
|
6
|
+
# Typing <tt>cog init</tt> will create a +Cogfile+ in the present working directory.
|
7
|
+
# +Cogfile+ files are used to configure an instance of {Config}.
|
8
|
+
#
|
9
|
+
# @example
|
7
10
|
# project_generators_path 'cog/generators'
|
8
11
|
# project_templates_path 'cog/templates'
|
9
12
|
# project_source_path 'src'
|
10
13
|
# language 'c++'
|
11
14
|
#
|
12
|
-
# Typing `cog init` will create a +Cogfile+ in the present working directory.
|
13
|
-
#
|
14
|
-
# +Cogfile+ files are used to configure an instance of Config.
|
15
15
|
class Cogfile
|
16
|
-
|
17
|
-
|
16
|
+
|
17
|
+
# Initialize with an instance of {Config}
|
18
|
+
# @api developer
|
19
|
+
# @param config [Config] the object which will be configured by this Cogfile
|
20
|
+
def initialize(config)
|
18
21
|
@config = config
|
19
22
|
end
|
20
23
|
|
21
|
-
# Interpret the Cogfile
|
22
|
-
|
24
|
+
# Interpret the +Cogfile+ at {Config#cogfile_path}
|
25
|
+
# @api developer
|
26
|
+
def interpret
|
23
27
|
eval File.read(@config.cogfile_path), binding
|
24
28
|
rescue Exception => e
|
25
29
|
raise CogfileError.new(e.to_s)
|
26
30
|
end
|
27
31
|
|
28
32
|
# Define the directory in which to find project generators
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# * +absolute+ - If false, the path is relative to the directory containing the +Cogfile+
|
33
|
+
# @param path [String] a file system path
|
34
|
+
# @param absolute [Boolean] if +false+, the path is relative to {Config#project_root}
|
32
35
|
def project_generators_path(path, absolute=false)
|
33
36
|
@config.instance_eval do
|
34
37
|
@project_generators_path = absolute ? path : File.join(project_root, path)
|
@@ -36,9 +39,8 @@ module Cog
|
|
36
39
|
end
|
37
40
|
|
38
41
|
# Define the directory in which to find custom project templates
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# * +absolute+ - If false, the path is relative to the directory containing the +Cogfile+
|
42
|
+
# @param path [String] a file system path
|
43
|
+
# @param absolute [Boolean] if +false+, the path is relative to {Config#project_root}
|
42
44
|
def project_templates_path(path, absolute=false)
|
43
45
|
@config.instance_eval do
|
44
46
|
@project_templates_path = absolute ? path : File.join(project_root, path)
|
@@ -46,9 +48,8 @@ module Cog
|
|
46
48
|
end
|
47
49
|
|
48
50
|
# Define the directory to which project source code is generated
|
49
|
-
#
|
50
|
-
#
|
51
|
-
# * +absolute+ - If false, the path is relative to the directory containing the +Cogfile+
|
51
|
+
# @param path [String] a file system path
|
52
|
+
# @param absolute [Boolean] if +false+, the path is relative to {Config#project_root}
|
52
53
|
def project_source_path(path, absolute=false)
|
53
54
|
@config.instance_eval do
|
54
55
|
@project_source_path = absolute ? path : File.join(project_root, path)
|
@@ -56,8 +57,7 @@ module Cog
|
|
56
57
|
end
|
57
58
|
|
58
59
|
# Define the default language in which to generated application source code
|
59
|
-
#
|
60
|
-
# * +lang+ - A code for the language. Acceptable values are <tt>c++</tt>.
|
60
|
+
# @param lang [String] a code for the language. Acceptable values are <tt>c++</tt>
|
61
61
|
def language(lang)
|
62
62
|
@config.instance_eval do
|
63
63
|
@language = lang
|
@@ -65,7 +65,8 @@ module Cog
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
# For wrapping errors which occur during the processing of a
|
68
|
+
# For wrapping errors which occur during the processing of a {Cogfile}
|
69
|
+
# @api client
|
69
70
|
class CogfileError < StandardError
|
70
71
|
def message
|
71
72
|
"in Cogfile, " + super
|
data/lib/cog/generator.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'cog/config'
|
2
2
|
require 'cog/errors'
|
3
|
+
require 'cog/helpers'
|
3
4
|
require 'erb'
|
4
5
|
require 'rainbow'
|
5
6
|
|
@@ -7,12 +8,14 @@ module Cog
|
|
7
8
|
|
8
9
|
# This module defines an interface which can be used by generator objects.
|
9
10
|
# Specifically, it makes it easy to find ERB templates and render them into
|
10
|
-
# generated source code files, using the #stamp method.
|
11
|
+
# generated source code files, using the {#stamp} method.
|
11
12
|
#
|
12
|
-
#
|
13
|
+
# @see https://github.com/ktonon/cog#generators Introduction to Generators
|
13
14
|
module Generator
|
14
15
|
|
15
|
-
#
|
16
|
+
# List the available project generators
|
17
|
+
# @param verbose [Boolean] should full generator paths be listed?
|
18
|
+
# @return [Array<String>] a list of generator names
|
16
19
|
def self.list(verbose=false)
|
17
20
|
if Config.instance.project?
|
18
21
|
x = Dir.glob(File.join Config.instance.project_generators_path, '*.rb')
|
@@ -23,15 +26,9 @@ module Cog
|
|
23
26
|
end
|
24
27
|
|
25
28
|
# Create a new generator
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# ==== Options
|
31
|
-
# * +tool+ - the name of the tool to use (default: basic)
|
32
|
-
#
|
33
|
-
# ==== Returns
|
34
|
-
# Whether or not the generator was created successfully
|
29
|
+
# @param name [String] the name to use for the new generator
|
30
|
+
# @option opt [String] :tool ('basic') the name of the tool to use
|
31
|
+
# @return [Boolean] was the generator successfully created?
|
35
32
|
def self.create(name, opt={})
|
36
33
|
return false unless Config.instance.project?
|
37
34
|
|
@@ -72,12 +69,9 @@ module Cog
|
|
72
69
|
end
|
73
70
|
|
74
71
|
# Run the generator with the given name
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
# ==== Returns
|
80
|
-
# Whether or not the generator could be found
|
72
|
+
# @param name [String] name of the generator as returned by {Generator.list}
|
73
|
+
# @option opt [Boolean] :verbose (false) in the case of an exception, should a full stack trace be written?
|
74
|
+
# @return [Boolean] was the generator run successfully?
|
81
75
|
def self.run(name, opt={})
|
82
76
|
filename = File.join Cog::Config.instance.project_generators_path, "#{name}.rb"
|
83
77
|
if File.exists? filename
|
@@ -92,17 +86,10 @@ module Cog
|
|
92
86
|
false
|
93
87
|
end
|
94
88
|
|
95
|
-
# Get the template with the given name
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
# one of the template directories
|
100
|
-
#
|
101
|
-
# ==== Options
|
102
|
-
# * <tt>:absolute</tt> - is the +path+ argument absolute? (default: +false+)
|
103
|
-
#
|
104
|
-
# ==== Returns
|
105
|
-
# An instance of ERB.
|
89
|
+
# Get the template with the given name
|
90
|
+
# @param path [String] path to template file relative one of the {Config#template_paths}
|
91
|
+
# @option opt [Boolean] :absolute (false) is the +path+ argument absolute?
|
92
|
+
# @return [ERB] an instance of {http://ruby-doc.org/stdlib-1.9.3/libdoc/erb/rdoc/ERB.html ERB}
|
106
93
|
def get_template(path, opt={})
|
107
94
|
path += '.erb'
|
108
95
|
fullpath = if opt[:absolute]
|
@@ -117,19 +104,18 @@ module Cog
|
|
117
104
|
ERB.new File.read(fullpath), 0, '>'
|
118
105
|
end
|
119
106
|
|
120
|
-
# Stamp a template
|
121
|
-
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
|
128
|
-
# * <tt>:absolute_template_path</tt> - is the +template_path+ argument absolute? (default: +false+)
|
129
|
-
# * <tt>:absolute_destination</tt> - is the +destination+ argument absolute? (default: +false+)
|
130
|
-
def stamp(template_path, destination, opt={})
|
107
|
+
# Stamp a template into a file or return it as a string
|
108
|
+
# @param template_path [String] path to template file relative one of the {Config#template_paths}
|
109
|
+
# @param destination [String] path to which the generated file should be written, relative to the {Config#project_source_path}
|
110
|
+
# @option opt [Boolean] :absolute_template_path (false) is the +template_path+ absolute?
|
111
|
+
# @option opt [Boolean] :absolute_destination (false) is the +destination+ absolute?
|
112
|
+
# @option opt [Boolean] :quiet (false) suppress writing to STDOUT?
|
113
|
+
# @return [nil or String] if +destination+ is not provided, the stamped template is returned as a string
|
114
|
+
def stamp(template_path, destination=nil, opt={})
|
131
115
|
t = get_template template_path, :absolute => opt[:absolute_template_path]
|
132
116
|
b = opt[:binding] || binding
|
117
|
+
return t.result(b) if destination.nil?
|
118
|
+
|
133
119
|
dest = opt[:absolute_destination] ? destination : File.join(Config.instance.project_source_path, destination)
|
134
120
|
FileUtils.mkpath File.dirname(dest) unless File.exists? dest
|
135
121
|
scratch = "#{dest}.scratch"
|
@@ -139,30 +125,38 @@ module Cog
|
|
139
125
|
else
|
140
126
|
updated = File.exists? dest
|
141
127
|
FileUtils.mv scratch, dest
|
142
|
-
STDOUT.write "#{updated ? :Updated : :Created} #{dest}\n".color(updated ? :white : :green)
|
128
|
+
STDOUT.write "#{updated ? :Updated : :Created} #{dest.relative_to_project_root}\n".color(updated ? :white : :green) unless opt[:quiet]
|
143
129
|
end
|
144
130
|
nil
|
145
131
|
end
|
146
132
|
|
147
133
|
# Copy a file from +src+ to +dest+, but only if +dest+ does not already exist.
|
148
|
-
|
134
|
+
# @param src [String] where to copy from
|
135
|
+
# @param dest [String] where to copy to
|
136
|
+
# @option opt [Boolean] :quiet (false) suppress writing to STDOUT?
|
137
|
+
# @return [nil]
|
138
|
+
def copy_if_missing(src, dest, opt={})
|
149
139
|
unless File.exists? dest
|
150
140
|
FileUtils.cp src, dest
|
151
|
-
STDOUT.write "Created #{dest}\n".color(:green)
|
141
|
+
STDOUT.write "Created #{dest.relative_to_project_root}\n".color(:green) unless opt[:quiet]
|
152
142
|
end
|
153
143
|
end
|
154
144
|
|
155
145
|
# Recursively create directories in the given path if they are missing.
|
156
|
-
|
157
|
-
|
146
|
+
# @param path [String] a file system path representing a directory
|
147
|
+
# @option opt [Boolean] :quiet (false) suppress writing to STDOUT?
|
148
|
+
# @return [nil]
|
149
|
+
def touch_path(path, opt={})
|
158
150
|
unless File.exists? path
|
159
151
|
FileUtils.mkdir_p path
|
160
|
-
STDOUT.write "Created #{path}\n".color(:green)
|
152
|
+
STDOUT.write "Created #{path.relative_to_project_root}\n".color(:green) unless opt[:quiet]
|
161
153
|
end
|
154
|
+
nil
|
162
155
|
end
|
163
156
|
|
164
157
|
# File extension for a snippet of the given source code language.
|
165
|
-
#
|
158
|
+
# @api unstable
|
159
|
+
# @example
|
166
160
|
# snippet_extension 'c++' # => 'h'
|
167
161
|
def snippet_extension(lang = 'text') # :nodoc:
|
168
162
|
case lang
|
@@ -172,24 +166,20 @@ module Cog
|
|
172
166
|
'txt'
|
173
167
|
end
|
174
168
|
end
|
175
|
-
|
176
|
-
#
|
177
|
-
def
|
178
|
-
lang = Config.instance.language
|
179
|
-
t = get_template "snippets/#{lang}/generated_warning.#{snippet_extension lang}", :cog_template => true
|
180
|
-
t.result(binding)
|
181
|
-
end
|
182
|
-
|
183
|
-
def include_guard_begin(name) # :nodoc:
|
169
|
+
|
170
|
+
# @api unstable
|
171
|
+
def include_guard_begin(name)
|
184
172
|
full = "COG_INCLUDE_GUARD_#{name.upcase}"
|
185
173
|
"#ifndef #{full}\n#define #{full}"
|
186
174
|
end
|
187
175
|
|
188
|
-
|
176
|
+
# @api unstable
|
177
|
+
def include_guard_end
|
189
178
|
"#endif // COG_INCLUDE_GUARD_[...]"
|
190
179
|
end
|
191
180
|
|
192
|
-
|
181
|
+
# @api unstable
|
182
|
+
def namespace_begin(name)
|
193
183
|
return if name.nil?
|
194
184
|
case Config.instance.language
|
195
185
|
when /c\+\+/
|
@@ -197,7 +187,8 @@ module Cog
|
|
197
187
|
end
|
198
188
|
end
|
199
189
|
|
200
|
-
|
190
|
+
# @api unstable
|
191
|
+
def namespace_end(name)
|
201
192
|
return if name.nil?
|
202
193
|
case Config.instance.language
|
203
194
|
when /c\+\+/
|
@@ -206,7 +197,7 @@ module Cog
|
|
206
197
|
end
|
207
198
|
|
208
199
|
private
|
209
|
-
def same?(original, scratch)
|
200
|
+
def same?(original, scratch)
|
210
201
|
if File.exists? original
|
211
202
|
File.read(original) == File.read(scratch)
|
212
203
|
else
|
data/lib/cog/helpers.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'cog/helpers/string'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'cog/config'
|
2
|
+
|
3
|
+
class String
|
4
|
+
|
5
|
+
# @return [String] strips {Cog::Config#project_root} from the beginning of this string
|
6
|
+
def relative_to_project_root
|
7
|
+
return unless Cog::Config.instance.project?
|
8
|
+
root = Cog::Config.instance.project_root
|
9
|
+
start_with?(root) ? slice(root.length+1..-1) : dup
|
10
|
+
end
|
11
|
+
end
|
data/lib/cog/spec_helpers.rb
CHANGED
@@ -6,15 +6,17 @@ module Cog
|
|
6
6
|
|
7
7
|
# Modules and classes to help write specs for testing +cog+
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# which is returned from a call to SpecHelpers::Runner#run
|
9
|
+
# Requiring the helpers will make extra {SpecHelpers::Matchers} available to
|
10
|
+
# your RSpec tests. These are useful for testing a {SpecHelpers::Invocation},
|
11
|
+
# which is returned from a call to {SpecHelpers::Runner#run}
|
13
12
|
#
|
13
|
+
# @example
|
14
14
|
# require 'cog/spec_helpers'
|
15
15
|
#
|
16
16
|
# describe 'The command line interface' do
|
17
17
|
#
|
18
|
+
# include Cog::SpecHelpers
|
19
|
+
#
|
18
20
|
# before :all do
|
19
21
|
# @cog = Cog::SpecHelpers::Runner.new 'bin/cog'
|
20
22
|
# end
|
@@ -22,42 +24,69 @@ module Cog
|
|
22
24
|
# it 'should print help when no args are passed' do
|
23
25
|
# @cog.run.should show_help
|
24
26
|
# end
|
27
|
+
#
|
28
|
+
# context 'in an uninitialized project' do
|
29
|
+
#
|
30
|
+
# before :each do
|
31
|
+
# use_fixture :uninitialized
|
32
|
+
# end
|
25
33
|
#
|
34
|
+
# it 'running `cog init` should make a Cogfile' do
|
35
|
+
# @cog.run(:init).should make(cogfile_path)
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# end
|
26
39
|
# end
|
27
40
|
module SpecHelpers
|
28
41
|
|
29
|
-
#
|
42
|
+
# @return [String] absolute path to the root spec directory
|
30
43
|
def spec_root
|
31
44
|
File.expand_path File.join(File.dirname(__FILE__), '..', '..', 'spec')
|
32
45
|
end
|
33
46
|
|
34
|
-
#
|
47
|
+
# @return [String] directory of an active spec fixture.
|
35
48
|
def active_fixture_dir
|
36
49
|
File.join spec_root, 'active_fixture'
|
37
50
|
end
|
38
51
|
|
39
|
-
#
|
52
|
+
# @return [String] path to the Cogfile in the active spec fixture
|
40
53
|
def cogfile_path
|
41
54
|
File.join active_fixture_dir, 'Cogfile'
|
42
55
|
end
|
43
56
|
|
44
|
-
#
|
57
|
+
# @return [String] path to the cog directory in the active spec fixture
|
45
58
|
def cog_directory
|
46
59
|
File.join active_fixture_dir, 'cog'
|
47
60
|
end
|
48
61
|
|
49
|
-
#
|
62
|
+
# @param name [String] active fixture generator identifier
|
63
|
+
# @return [String] path to the generator with the given name
|
50
64
|
def generator(name)
|
51
65
|
File.expand_path File.join(active_fixture_dir, 'cog', 'generators', "#{name}.rb")
|
52
66
|
end
|
67
|
+
|
68
|
+
# @param name [String] template identifier (without the .erb extension)
|
69
|
+
# @return [String] absolute file system path to the template
|
70
|
+
def template(name)
|
71
|
+
File.expand_path File.join(active_fixture_dir, 'cog', 'templates', "#{name}")
|
72
|
+
end
|
53
73
|
|
54
|
-
#
|
74
|
+
# @param name [String] tool fixture identifier
|
75
|
+
# @return [String] path to the test tool with the given name
|
55
76
|
def tool(name)
|
56
77
|
File.expand_path File.join(spec_root, 'tools', name.to_s, 'lib', "#{name}.rb")
|
57
78
|
end
|
58
79
|
|
59
|
-
#
|
80
|
+
# @param filename [String] name of a generated source file
|
81
|
+
# @return [String] absolute path to the generated file
|
82
|
+
def generated_file(filename)
|
83
|
+
File.expand_path File.join(active_fixture_dir, 'src', filename)
|
84
|
+
end
|
85
|
+
|
86
|
+
# The next cog spec will execute in a fresh copy of the given fixture
|
60
87
|
# Fixture directories are stored in <tt>spec/fixtures</tt>.
|
88
|
+
# @param name [String] name of the fixture
|
89
|
+
# @return [nil]
|
61
90
|
def use_fixture(name)
|
62
91
|
path = File.join spec_root, 'fixtures', name.to_s
|
63
92
|
if File.exists?(path) && File.directory?(path)
|
@@ -67,6 +96,7 @@ module Cog
|
|
67
96
|
else
|
68
97
|
throw :invalid_fixture_name
|
69
98
|
end
|
99
|
+
nil
|
70
100
|
end
|
71
101
|
|
72
102
|
end
|
@@ -4,11 +4,12 @@ require 'rspec'
|
|
4
4
|
module Cog
|
5
5
|
module SpecHelpers
|
6
6
|
|
7
|
-
# Extra should or should_not matchers for RSpec.
|
8
|
-
# Check out #match_maker for help writing new matchers.
|
7
|
+
# Extra +should+ or +should_not+ matchers for RSpec.
|
8
|
+
# Check out {#match_maker} for help writing new matchers.
|
9
9
|
module Matchers
|
10
10
|
|
11
|
-
# The target Invocation should write something to STDERR, indicating an error
|
11
|
+
# The target {Invocation} should write something to STDERR, indicating an error
|
12
|
+
# @return [nil]
|
12
13
|
def complain
|
13
14
|
match_maker do
|
14
15
|
message { "to [write something|not write anything] to STDERR" }
|
@@ -16,7 +17,9 @@ module Cog
|
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
19
|
-
# The target Invocation should create a
|
20
|
+
# The target {Invocation} should create a file at the given +path+
|
21
|
+
# @param path [String] path to check for a file after the invocation
|
22
|
+
# @return [nil]
|
20
23
|
def make(path)
|
21
24
|
match_maker do
|
22
25
|
message { "to [create|not create] #{path}" }
|
@@ -29,7 +32,8 @@ module Cog
|
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
32
|
-
# The target Invocation should do something, as determined by standard output
|
35
|
+
# The target {Invocation} should do something, as determined by standard output
|
36
|
+
# @return [nil]
|
33
37
|
def do_something
|
34
38
|
match_maker do
|
35
39
|
message { "to [write something|not write anything] to STDOUT" }
|
@@ -37,7 +41,9 @@ module Cog
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
# The target Invocation should write the given list of lines to standard output
|
44
|
+
# The target {Invocation} should write the given list of lines to standard output
|
45
|
+
# @param x [Array<String>] a list of lines to match against standard output
|
46
|
+
# @return [nil]
|
41
47
|
def output(x)
|
42
48
|
match_maker do
|
43
49
|
message { "to [write|not write] #{x.join "\n"} to STDOUT"}
|
@@ -47,7 +53,8 @@ module Cog
|
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
50
|
-
# The target Invocation should output the default help text
|
56
|
+
# The target {Invocation} should output the default help text
|
57
|
+
# @return [nil]
|
51
58
|
def show_help
|
52
59
|
match_maker do
|
53
60
|
message { "to [show|not show] the default help text, got #{lines.first.inspect}" }
|
@@ -2,8 +2,8 @@ module Cog
|
|
2
2
|
module SpecHelpers
|
3
3
|
module Matchers
|
4
4
|
|
5
|
-
# Within Matchers#match_maker blocks, +self+ is set to an instance of this
|
6
|
-
# class
|
5
|
+
# Within {Matchers#match_maker} blocks, +self+ is set to an instance of this
|
6
|
+
# class
|
7
7
|
class MatchMaker
|
8
8
|
|
9
9
|
# A list of lines read from STDOUT after executing the Invocation
|
@@ -17,69 +17,91 @@ module Cog
|
|
17
17
|
#
|
18
18
|
# The template is used for both positive and negative failures.
|
19
19
|
# Substrings which look like this <tt>"[positive|negative]</tt>" will
|
20
|
-
# be replaced with the appropriate section.
|
20
|
+
# be replaced with the appropriate section.
|
21
|
+
#
|
22
|
+
# @example
|
21
23
|
# message { "to [show|not show] the default help text" }
|
22
24
|
#
|
23
25
|
# would read "expected cog to show the default help text"
|
24
26
|
# for a positive failure and "expected cog to not show the
|
25
27
|
# default help text" for a negative failure. The "expected cog"
|
26
28
|
# part is inserted automatically.
|
29
|
+
#
|
30
|
+
# @yield called after the invocation. {#lines} and {#error} can be used
|
31
|
+
# @return [nil]
|
27
32
|
def message(&block)
|
28
33
|
@msg_block = block
|
34
|
+
nil
|
29
35
|
end
|
30
36
|
|
31
37
|
# Define a block which runs before the Invocation.
|
32
38
|
#
|
33
39
|
# This is not required, but can be used to save context that is used
|
34
40
|
# the in post invocation #test.
|
41
|
+
#
|
42
|
+
# @yield called before the invocation. Save context as instance variables
|
43
|
+
# @return [nil]
|
35
44
|
def before(&block)
|
36
45
|
@before_block = block
|
46
|
+
nil
|
37
47
|
end
|
38
48
|
|
39
49
|
# Define the test which runs after the Invocation
|
40
50
|
#
|
41
51
|
# This can make use of instance variables set during #before.
|
52
|
+
#
|
53
|
+
# @yield called after the invocation
|
54
|
+
# @return [nil]
|
42
55
|
def test(&block)
|
43
56
|
@test_block = block
|
57
|
+
nil
|
44
58
|
end
|
45
59
|
|
46
|
-
|
47
|
-
|
60
|
+
# @api developer
|
61
|
+
# @param invocation [Invocation] +cog+ executable and arguments bundled up
|
62
|
+
# @return [Boolean] result of the {#test} block
|
63
|
+
def matches?(invocation)
|
64
|
+
@invocation = invocation
|
48
65
|
instance_eval &@before_block unless @before_block.nil?
|
49
|
-
@
|
66
|
+
@invocation.exec do |input, output, error|
|
50
67
|
@lines = output.readlines
|
51
68
|
@error = error.readlines
|
52
69
|
end
|
53
70
|
instance_eval &@test_block
|
54
71
|
end
|
55
|
-
|
72
|
+
|
73
|
+
# @api developer
|
74
|
+
# @return [String] positive interpretation of the {#message} block result
|
75
|
+
def failure_message
|
56
76
|
msg = instance_eval &@msg_block
|
57
77
|
msg = msg.gsub /\[([^\|\]]*)(?:\|([^\]]*)\])?/, '\1'
|
58
|
-
"expected #{@
|
78
|
+
"expected #{@invocation} #{msg}\n#{trace}"
|
59
79
|
end
|
60
|
-
|
80
|
+
|
81
|
+
# @api developer
|
82
|
+
# @return [String] negative interpretation of the {#message} block result
|
83
|
+
def negative_failure_message
|
61
84
|
msg = instance_eval &@msg_block
|
62
85
|
msg = msg.gsub /\[([^\|\]]*)(?:\|([^\]]*)\])?/, '\2'
|
63
|
-
"expected #{@
|
86
|
+
"expected #{@invocation} #{msg}\n#{trace}"
|
64
87
|
end
|
65
88
|
|
66
|
-
|
89
|
+
# @api developer
|
90
|
+
# @return [String] STDOUT and STDERR
|
91
|
+
def trace
|
67
92
|
"STDOUT:\n#{@lines.join "\n"}\nSTDERR:\n#{@error.join "\n"}"
|
68
93
|
end
|
69
94
|
end
|
70
95
|
|
71
|
-
# Makes it
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# #match_maker method.
|
96
|
+
# Makes it easier to write RSpec matchers for testing +cog+ command invocations
|
97
|
+
# @yield +self+ is set to an instance of {MatchMaker}
|
98
|
+
# @example
|
75
99
|
# def show_help
|
76
100
|
# match_maker do
|
77
101
|
# message { "to [show|not show] the default help text, got #{lines.first.inspect}" }
|
78
102
|
# test { (/help.*code gen/ =~ lines[1]) }
|
79
103
|
# end
|
80
104
|
# end
|
81
|
-
#
|
82
|
-
# Within the +match_maker+ block, +self+ is set to an instance of MatchMaker.
|
83
105
|
def match_maker(&block)
|
84
106
|
m = MatchMaker.new
|
85
107
|
m.instance_eval &block
|
@@ -9,16 +9,15 @@ module Cog
|
|
9
9
|
# Value of the COG_TOOLS environment variable for invocations returned from #run
|
10
10
|
attr_accessor :tools
|
11
11
|
|
12
|
+
# @param path_to_cl_app [String] path
|
12
13
|
def initialize(path_to_cl_app)
|
13
14
|
@cog = File.expand_path path_to_cl_app
|
14
15
|
@tools = []
|
15
16
|
end
|
16
17
|
|
17
18
|
# Run cog with the given arguments
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# An instance of Invocation configured with the arguments. Use should and
|
21
|
-
# should_not with the custom Matchers
|
19
|
+
# @param args [Array<String>] arguments to pass to +cog+
|
20
|
+
# @return [Invocation] an object which can be used with custom {Matchers}
|
22
21
|
def run(*args)
|
23
22
|
args = [@cog] + args
|
24
23
|
Invocation.new(args.collect {|x| x.to_s}, :tools => @tools)
|
@@ -26,16 +25,23 @@ module Cog
|
|
26
25
|
end
|
27
26
|
|
28
27
|
# Represents a +cog+ command line invocation, which can be tested with
|
29
|
-
# +RSpec+ +should+ and +should_not+ custom Matchers. This is the kind of
|
30
|
-
# object returned by Runner#run.
|
28
|
+
# +RSpec+ +should+ and +should_not+ custom {Matchers}. This is the kind of
|
29
|
+
# object returned by {Runner#run}.
|
31
30
|
class Invocation
|
32
31
|
|
33
|
-
|
32
|
+
# @api developer
|
33
|
+
# @param cmd [Array<String>] path to +cog+ executable and arguments
|
34
|
+
# @option opt [Array<String>] :tools ([]) a list of tools to add to the +COG_TOOLS+ environment variable just before running the command with {#exec}
|
35
|
+
def initialize(cmd, opt={})
|
34
36
|
@cmd = cmd
|
35
|
-
@tools = opt[:tools].join ':'
|
37
|
+
@tools = (opt[:tools] || []).join ':'
|
36
38
|
end
|
37
39
|
|
38
|
-
|
40
|
+
# Execute the command
|
41
|
+
# @api developer
|
42
|
+
# @yield [stdin, stdout, stderr] standard pipes for the invocation
|
43
|
+
# @return [nil]
|
44
|
+
def exec(&block)
|
39
45
|
@cmd = ['bundle', 'exec'] + @cmd
|
40
46
|
ENV['COG_TOOLS'] = @tools
|
41
47
|
Open3.popen3 *@cmd do |i,o,e,t|
|
@@ -43,7 +49,9 @@ module Cog
|
|
43
49
|
end
|
44
50
|
end
|
45
51
|
|
46
|
-
|
52
|
+
# @api developer
|
53
|
+
# @return [String] loggable representation
|
54
|
+
def to_s
|
47
55
|
"`COG_TOOLS=#{@tools} #{@cmd.compact.join ' '}`"
|
48
56
|
end
|
49
57
|
end
|
data/lib/cog/tool.rb
CHANGED
@@ -4,10 +4,12 @@ require 'rainbow'
|
|
4
4
|
|
5
5
|
module Cog
|
6
6
|
|
7
|
-
#
|
7
|
+
# @see https://github.com/ktonon/cog#tools Introduction to Tools
|
8
8
|
class Tool
|
9
9
|
|
10
10
|
# A list of available tools
|
11
|
+
# @param verbose [Boolean] should full paths be listed for tools which are explicitly referenced?
|
12
|
+
# @return [Array<String>] a list of strings which can be passed to +require+
|
11
13
|
def self.list(verbose=false)
|
12
14
|
x = (ENV['COG_TOOLS'] || '').split ':'
|
13
15
|
if x.all? {|path| path.slice(-3..-1) != '.rb' || File.exists?(path)}
|
@@ -29,9 +31,8 @@ module Cog
|
|
29
31
|
end
|
30
32
|
|
31
33
|
# Generate a new tool with the given name
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# Whether or not the generator was created successfully
|
34
|
+
# @param name [String] name of the tool to create. Should not conflict with other tool names
|
35
|
+
# @return [Boolean] was the tool created successfully?
|
35
36
|
def self.create(name)
|
36
37
|
if File.exists? name
|
37
38
|
STDERR.write "A #{File.directory?(name) ? :directory : :file} named '#{name}' already exists\n".color(:red)
|
@@ -62,9 +63,8 @@ module Cog
|
|
62
63
|
end
|
63
64
|
|
64
65
|
# Find an available tool with the given name
|
65
|
-
#
|
66
|
-
#
|
67
|
-
# A fully qualified tool path, which can be required
|
66
|
+
# @param name [String] name of the tool to search for
|
67
|
+
# @return [String] a fully qualified tool path, which can be passed to +require+
|
68
68
|
def self.find(name)
|
69
69
|
x = (ENV['COG_TOOLS'] || '').split ':'
|
70
70
|
x.each do |path|
|
data/lib/cog/version.rb
CHANGED
File without changes
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 55
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 20
|
10
|
+
version: 0.0.20
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kevin Tonon
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-11-
|
18
|
+
date: 2012-11-07 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: gli
|
@@ -60,7 +60,7 @@ dependencies:
|
|
60
60
|
type: :development
|
61
61
|
version_requirements: *id003
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: yard
|
64
64
|
prerelease: false
|
65
65
|
requirement: &id004 !ruby/object:Gem::Requirement
|
66
66
|
none: false
|
@@ -79,16 +79,14 @@ executables:
|
|
79
79
|
- cog
|
80
80
|
extensions: []
|
81
81
|
|
82
|
-
extra_rdoc_files:
|
83
|
-
|
82
|
+
extra_rdoc_files: []
|
83
|
+
|
84
84
|
files:
|
85
85
|
- bin/cog
|
86
86
|
- Default.cogfile
|
87
87
|
- LICENSE
|
88
88
|
- templates/cog/generator/basic-template.txt.erb.erb
|
89
89
|
- templates/cog/generator/basic.rb.erb
|
90
|
-
- templates/cog/snippets/c++/generated_warning.h.erb
|
91
|
-
- templates/cog/snippets/generated_warning.txt
|
92
90
|
- templates/cog/tool/API.rdoc.erb
|
93
91
|
- templates/cog/tool/Gemfile.erb
|
94
92
|
- templates/cog/tool/generator.rb.erb.erb
|
@@ -99,10 +97,13 @@ files:
|
|
99
97
|
- templates/cog/tool/tool.gemspec.erb
|
100
98
|
- templates/cog/tool/tool.rb.erb
|
101
99
|
- templates/cog/tool/version.rb.erb
|
100
|
+
- templates/warning.h.erb
|
102
101
|
- lib/cog/config/cogfile.rb
|
103
102
|
- lib/cog/config.rb
|
104
103
|
- lib/cog/errors.rb
|
105
104
|
- lib/cog/generator.rb
|
105
|
+
- lib/cog/helpers/string.rb
|
106
|
+
- lib/cog/helpers.rb
|
106
107
|
- lib/cog/spec_helpers/matchers/match_maker.rb
|
107
108
|
- lib/cog/spec_helpers/matchers.rb
|
108
109
|
- lib/cog/spec_helpers/runner.rb
|
@@ -110,17 +111,13 @@ files:
|
|
110
111
|
- lib/cog/tool.rb
|
111
112
|
- lib/cog/version.rb
|
112
113
|
- lib/cog.rb
|
113
|
-
-
|
114
|
+
- yard-templates/default/fulldoc/html/css/common.css
|
114
115
|
homepage: https://github.com/ktonon/cog
|
115
116
|
licenses: []
|
116
117
|
|
117
118
|
post_install_message:
|
118
|
-
rdoc_options:
|
119
|
-
|
120
|
-
- cog
|
121
|
-
- --main
|
122
|
-
- cog.rdoc
|
123
|
-
- -ri
|
119
|
+
rdoc_options: []
|
120
|
+
|
124
121
|
require_paths:
|
125
122
|
- lib
|
126
123
|
- lib
|
data/API.rdoc
DELETED