cog 0.0.20 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/cog +54 -53
- data/lib/cog.rb +4 -3
- data/lib/cog/built_in_tools/basic.rb +10 -0
- data/lib/cog/built_in_tools/basic/cog_tool.rb +15 -0
- data/lib/cog/config.rb +111 -33
- data/lib/cog/config/tool.rb +99 -0
- data/lib/cog/controllers.rb +13 -0
- data/lib/cog/controllers/generator_controller.rb +45 -0
- data/lib/cog/controllers/template_controller.rb +31 -0
- data/lib/cog/controllers/tool_controller.rb +53 -0
- data/lib/cog/errors.rb +51 -9
- data/lib/cog/generator.rb +1 -74
- data/lib/cog/helpers.rb +1 -0
- data/lib/cog/helpers/cascading_template_set.rb +75 -0
- data/lib/cog/helpers/string.rb +9 -3
- data/lib/cog/version.rb +1 -1
- data/templates/basic/generator.rb.erb +12 -0
- data/templates/{cog/generator/basic-template.txt.erb.erb → basic/template.txt.erb.erb} +0 -0
- data/templates/cog/{tool → custom_tool}/Gemfile.erb +4 -0
- data/templates/cog/{tool → custom_tool}/LICENSE.erb +1 -1
- data/templates/cog/custom_tool/README.markdown.erb +18 -0
- data/templates/cog/custom_tool/Rakefile.erb +15 -0
- data/templates/cog/custom_tool/cog_tool.rb.erb +15 -0
- data/templates/cog/custom_tool/generator.rb.erb.erb +9 -0
- data/templates/cog/{tool → custom_tool}/template.txt.erb.erb +0 -0
- data/templates/cog/custom_tool/tool.gemspec.erb +18 -0
- data/templates/cog/{tool → custom_tool}/tool.rb.erb +5 -13
- data/templates/cog/{tool → custom_tool}/version.rb.erb +1 -1
- metadata +24 -17
- data/lib/cog/tool.rb +0 -81
- data/templates/cog/generator/basic.rb.erb +0 -12
- data/templates/cog/tool/API.rdoc.erb +0 -7
- data/templates/cog/tool/README.markdown.erb +0 -18
- data/templates/cog/tool/Rakefile.erb +0 -15
- data/templates/cog/tool/generator.rb.erb.erb +0 -6
- data/templates/cog/tool/tool.gemspec.erb +0 -18
data/bin/cog
CHANGED
@@ -11,58 +11,72 @@ include GLI::App
|
|
11
11
|
program_desc 'This is a utility to help you write code generators.'
|
12
12
|
|
13
13
|
# version Cog::VERSION
|
14
|
+
|
14
15
|
desc 'Output more detailed information when running a command'
|
15
|
-
switch :v
|
16
|
+
switch [:verbose, :v]
|
17
|
+
|
18
|
+
desc 'Set the Cogfile explicitly'
|
19
|
+
arg_name 'path'
|
20
|
+
flag :cogfile
|
21
|
+
|
22
|
+
desc 'Set the active tool'
|
23
|
+
arg_name 'name'
|
24
|
+
default_value 'basic'
|
25
|
+
flag :tool
|
16
26
|
|
17
27
|
desc 'Add cog to the project in the present working directory'
|
18
|
-
skips_pre
|
19
28
|
command :init do |c|
|
20
29
|
|
21
|
-
c.action do |
|
30
|
+
c.action do |gopt, opt, args|
|
22
31
|
Cog.initialize_project
|
23
32
|
end
|
24
33
|
end
|
25
34
|
|
26
|
-
desc '
|
35
|
+
desc 'Manage project generators'
|
27
36
|
command [:generator, :gen] do |c|
|
28
37
|
|
29
38
|
c.default_command :list
|
30
39
|
|
31
|
-
c.desc 'List generators
|
40
|
+
c.desc 'List project generators'
|
32
41
|
c.command :list do |sub|
|
33
|
-
sub.action do |
|
34
|
-
x = Cog::
|
42
|
+
sub.action do |gopt, opt, args|
|
43
|
+
x = Cog::Controllers::GeneratorController.list :verbose => gopt[:v]
|
35
44
|
puts x.join "\n" unless x.empty?
|
36
45
|
end
|
37
46
|
end
|
38
47
|
|
39
|
-
c.desc 'Create a new generator
|
40
|
-
c.arg_name '
|
48
|
+
c.desc 'Create a new project generator'
|
49
|
+
c.arg_name 'name'
|
41
50
|
c.command :new do |sub|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
sub.action do |gopt, opt, args|
|
52
|
+
args.each do |name|
|
53
|
+
Cog::Controllers::GeneratorController.create name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
c.desc 'Run project generators'
|
59
|
+
c.long_desc 'Omit generator name to run all of them'
|
60
|
+
c.arg_name 'name'
|
61
|
+
c.command :run do |sub|
|
62
|
+
sub.action do |gopt, opt, args|
|
63
|
+
args = Cog::Controllers::GeneratorController.list if args.empty?
|
49
64
|
args.each do |name|
|
50
|
-
Cog::
|
65
|
+
Cog::Controllers::GeneratorController.run name
|
51
66
|
end
|
52
67
|
end
|
53
68
|
end
|
54
69
|
end
|
55
70
|
|
56
71
|
desc 'List or create DSLs known as cog tools'
|
57
|
-
skips_pre
|
58
72
|
command :tool do |c|
|
59
73
|
|
60
74
|
c.default_command :list
|
61
75
|
|
62
76
|
c.desc 'List the available tools'
|
63
77
|
c.command :list do |sub|
|
64
|
-
sub.action do |
|
65
|
-
x = Cog::
|
78
|
+
sub.action do |gopt, opt, args|
|
79
|
+
x = Cog::Controllers::ToolController.list :verbose => gopt[:verbose]
|
66
80
|
puts x.join "\n" unless x.empty?
|
67
81
|
end
|
68
82
|
end
|
@@ -70,46 +84,38 @@ command :tool do |c|
|
|
70
84
|
c.desc 'Create a new tool'
|
71
85
|
c.arg_name 'tool_name'
|
72
86
|
c.command :new do |sub|
|
73
|
-
sub.action do |
|
87
|
+
sub.action do |gopt, opt, args|
|
74
88
|
args.each do |name|
|
75
|
-
Cog::
|
89
|
+
Cog::Controllers::ToolController.create name
|
76
90
|
end
|
77
91
|
end
|
78
92
|
end
|
79
93
|
end
|
80
94
|
|
81
|
-
desc '
|
82
|
-
|
83
|
-
command :run do |c|
|
95
|
+
desc 'Manage templates'
|
96
|
+
command :template do |c|
|
84
97
|
|
85
|
-
c.
|
86
|
-
|
87
|
-
|
88
|
-
|
98
|
+
c.default_command :list
|
99
|
+
|
100
|
+
c.desc 'List the available templates'
|
101
|
+
c.command :list do |sub|
|
102
|
+
sub.action do |gopt, opt, args|
|
103
|
+
x = Cog::Controllers::TemplateController.list :verbose => gopt[:verbose]
|
104
|
+
puts x.join "\n" unless x.empty?
|
89
105
|
end
|
90
106
|
end
|
91
107
|
end
|
92
108
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
c.action do
|
98
|
-
puts 'c++'
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
pre do |global_options, command, options, args|
|
103
|
-
# Pre logic here
|
104
|
-
# Return true to proceed; false to abort and not call the
|
105
|
-
# chosen command
|
106
|
-
# Use skips_pre before a command to skip this block
|
107
|
-
# on that command only
|
108
|
-
unless Cog::Config.instance.project?
|
109
|
-
STDERR.write "No Cogfile could be found\n".color(:red)
|
110
|
-
STDOUT.write "Run `cog init` to prepare a project for use with cog\n"
|
109
|
+
pre do |gopt, command, opt, args|
|
110
|
+
if gopt[:cogfile] && !File.exists?(gopt[:cogfile])
|
111
|
+
STDERR.write "No such Cogfile at #{gopt[:cogfile]}\n"
|
111
112
|
false
|
112
113
|
else
|
114
|
+
Cog::Config.instance = Cog::Config.new gopt[:cogfile] if gopt[:cogfile]
|
115
|
+
unless command.name == :init
|
116
|
+
Cog::Config.instance.register_tools
|
117
|
+
Cog::Config.instance.activate_tool gopt[:tool]
|
118
|
+
end
|
113
119
|
true
|
114
120
|
end
|
115
121
|
end
|
@@ -126,9 +132,4 @@ on_error do |exception|
|
|
126
132
|
true
|
127
133
|
end
|
128
134
|
|
129
|
-
|
130
|
-
exit run(ARGV)
|
131
|
-
rescue
|
132
|
-
# Don't show stack traces during normal use
|
133
|
-
raise unless ENV['COG_DEBUG'].nil? || /^(|0|false|no|off)$/i =~ ENV['COG_DEBUG']
|
134
|
-
end
|
135
|
+
exit run(ARGV)
|
data/lib/cog.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'cog/config'
|
2
|
+
require 'cog/controllers'
|
3
|
+
require 'cog/errors'
|
2
4
|
require 'cog/generator'
|
3
|
-
require 'cog/
|
5
|
+
require 'cog/helpers'
|
4
6
|
require 'cog/version'
|
5
|
-
require 'fileutils'
|
6
7
|
|
7
8
|
# +cog+ is a command line utility that makes it a bit easier to organize a project
|
8
9
|
# which uses code generation. These are the API docs, but you might want to read
|
@@ -21,5 +22,5 @@ module Cog
|
|
21
22
|
nil
|
22
23
|
end
|
23
24
|
end
|
24
|
-
|
25
|
+
|
25
26
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'cog'
|
2
|
+
|
3
|
+
# Register basic as a tool with cog
|
4
|
+
Cog::Config.instance.register_tool __FILE__, :built_in => true do |tool|
|
5
|
+
|
6
|
+
# Define how new baisc generators are created
|
7
|
+
#
|
8
|
+
# When the block is executed, +self+ will be an instance of Cog::Config::Tool::GeneratorStamper
|
9
|
+
tool.stamp_generator do
|
10
|
+
template_dest = File.join config.project_templates_path, "#{name}.txt.erb"
|
11
|
+
stamp 'basic/generator.rb', generator_dest, :absolute_destination => true
|
12
|
+
stamp 'basic/template.txt.erb', template_dest, :absolute_destination => true
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/lib/cog/config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'cog/config/cogfile'
|
2
|
-
require '
|
2
|
+
require 'cog/config/tool'
|
3
|
+
require 'cog/errors'
|
3
4
|
|
4
5
|
module Cog
|
5
6
|
|
@@ -27,28 +28,87 @@ module Cog
|
|
27
28
|
# @return [String] directory in which to find custom project templates
|
28
29
|
attr_reader :project_templates_path
|
29
30
|
|
30
|
-
# @return [
|
31
|
+
# @return [Tool] the active tool affects the creation of new generators
|
32
|
+
attr_reader :active_tool
|
33
|
+
|
34
|
+
# @return [Boolean] whether or not we operating in the context of a project
|
31
35
|
def project?
|
32
36
|
!@project_root.nil?
|
33
37
|
end
|
34
|
-
|
35
|
-
# @param value [String] a value which is set by the active tool
|
36
|
-
attr_writer :tool_templates_path
|
37
|
-
|
38
|
-
# @return [String] a value which is set by the active tool
|
39
|
-
attr_accessor :tool_generator_template
|
40
38
|
|
41
39
|
# A list of directories in which to find ERB template files
|
42
40
|
#
|
43
41
|
# Templates should be looked for in the following order as determined by the list returned from this method
|
44
42
|
#
|
45
|
-
# * {#project_templates_path}
|
46
|
-
# *
|
47
|
-
# *
|
43
|
+
# * {#project_templates_path} which is present if {#project?}
|
44
|
+
# * {Tool#templates_path} for each registered tool (in no particular order)
|
45
|
+
# * {Config.cog_templates_path} which is *always* present
|
48
46
|
#
|
49
47
|
# @return [Array<String>] a list of directories order with ascending priority
|
50
48
|
def template_paths
|
51
|
-
[@project_templates_path
|
49
|
+
paths = [@project_templates_path]
|
50
|
+
paths += tools.collect {|tool| tool.templates_path}
|
51
|
+
paths << Config.cog_templates_path
|
52
|
+
paths.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Array<Tool>] a sorted list of available tools
|
56
|
+
def tools
|
57
|
+
@tools.values.sort
|
58
|
+
end
|
59
|
+
|
60
|
+
# Register built-in and custom tools.
|
61
|
+
# @api developer
|
62
|
+
#
|
63
|
+
# Custom tools are specified in the +COG_TOOLS+ environment variable.
|
64
|
+
# @return [nil]
|
65
|
+
def register_tools
|
66
|
+
# Register built-in tools
|
67
|
+
[:basic].each do |built_in|
|
68
|
+
require File.join(Config.gem_dir, 'lib', 'cog', 'built_in_tools', built_in.to_s, 'cog_tool.rb')
|
69
|
+
end
|
70
|
+
# Register custom tools defined in COG_TOOLS
|
71
|
+
(ENV['COG_TOOLS'] || '').split(':').each do |path|
|
72
|
+
explicit = path.end_with? '.rb'
|
73
|
+
@next_tool_was_required_explicitly = explicit
|
74
|
+
path = "#{path}/cog_tool" unless explicit
|
75
|
+
begin
|
76
|
+
require path
|
77
|
+
rescue LoadError
|
78
|
+
raise Errors::CouldNotLoadTool.new path
|
79
|
+
end
|
80
|
+
end
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# Define a new +cog+ tool
|
85
|
+
# @api developer
|
86
|
+
# @param path [String] path to the <tt>cog_tool.rb</tt> file
|
87
|
+
# @option opt [Boolean] :built_in (false) if +true+, then treat this tool as a built-in tool
|
88
|
+
# @yield [Tool] define the tool
|
89
|
+
# @return [nil]
|
90
|
+
def register_tool(path, opt={}, &block)
|
91
|
+
tool = Tool.new path, :built_in => opt[:built_in], :explicit_require => @next_tool_was_required_explicitly
|
92
|
+
block.call tool
|
93
|
+
@tools[tool.name] = tool
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
97
|
+
# @api developer
|
98
|
+
# @return [Boolean] whether or not a tool is registered with the given name
|
99
|
+
def tool_registered?(name)
|
100
|
+
@tools.member? name
|
101
|
+
end
|
102
|
+
|
103
|
+
# Activate the registered tool with the given name
|
104
|
+
# @api developer
|
105
|
+
# @param name [String] name of the registered tool to activate
|
106
|
+
# @return [nil]
|
107
|
+
def activate_tool(name)
|
108
|
+
throw :ToolAlreadyActivated unless @active_tool.nil?
|
109
|
+
raise Errors::NoSuchTool.new(name) unless tool_registered?(name)
|
110
|
+
@tools[name].load
|
111
|
+
@active_tool = @tools[name]
|
52
112
|
end
|
53
113
|
|
54
114
|
# Location of the installed cog gem
|
@@ -64,20 +124,38 @@ module Cog
|
|
64
124
|
end
|
65
125
|
end
|
66
126
|
|
67
|
-
#
|
127
|
+
# Location of the built-in templates
|
128
|
+
def self.cog_templates_path
|
129
|
+
File.join Config.gem_dir, 'templates'
|
130
|
+
end
|
131
|
+
|
132
|
+
# @param cogfile_path [String] if provided the {Cogfile} at the given path
|
133
|
+
# is used to initialize this instance, and {#project?} will be +true+
|
134
|
+
def initialize(cogfile_path = nil)
|
135
|
+
@project_root = nil
|
136
|
+
@tools = {}
|
137
|
+
@language = 'c++'
|
138
|
+
if cogfile_path
|
139
|
+
@cogfile_path = File.expand_path cogfile_path
|
140
|
+
@project_root = File.dirname @cogfile_path
|
141
|
+
cogfile = Cogfile.new self
|
142
|
+
cogfile.interpret
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Find or create the singleton {Config} instance.
|
68
147
|
#
|
69
|
-
# Initialized using the Cogfile for the current project, if any can be
|
70
|
-
# found. If not,
|
71
|
-
#
|
148
|
+
# Initialized using the {Cogfile} for the current project, if any can be
|
149
|
+
# found. If not, {#project?} will be +false+ and all the <tt>project_...</tt>
|
150
|
+
# methods will return +nil+.
|
72
151
|
#
|
73
|
-
# The Cogfile will be looked for in the present working directory. If none
|
152
|
+
# The {Cogfile} will be looked for in the present working directory. If none
|
74
153
|
# is found there the parent directory will be checked, and then the
|
75
154
|
# grandparent, and so on.
|
76
155
|
#
|
77
|
-
# @return [Config] the singleton
|
156
|
+
# @return [Config] the singleton instance
|
78
157
|
def self.instance
|
79
158
|
return @instance if @instance
|
80
|
-
@instance = self.new
|
81
159
|
|
82
160
|
# Attempt to find a Cogfile
|
83
161
|
parts = Dir.pwd.split File::SEPARATOR
|
@@ -86,23 +164,23 @@ module Cog
|
|
86
164
|
i -= 1
|
87
165
|
end
|
88
166
|
path = File.join(parts.slice(0, i) + ['Cogfile']) if i >= 0
|
167
|
+
|
89
168
|
if path && File.exists?(path)
|
90
|
-
@instance.
|
91
|
-
|
92
|
-
|
93
|
-
cogfile = Cogfile.new self
|
94
|
-
cogfile.interpret
|
95
|
-
end
|
169
|
+
@instance = self.new path
|
170
|
+
else
|
171
|
+
@instance = self.new
|
96
172
|
end
|
97
|
-
|
98
|
-
@instance
|
99
173
|
end
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
174
|
+
|
175
|
+
# Explicitly set the singleton Config instance.
|
176
|
+
#
|
177
|
+
# The singleton must not already be set.
|
178
|
+
#
|
179
|
+
# @param config [Config] the instance to set as the singleton
|
180
|
+
# @return [nil]
|
181
|
+
def self.instance=(config)
|
182
|
+
throw :ConfigInstanceAlreadySet unless @instance.nil?
|
183
|
+
@instance = config
|
106
184
|
end
|
107
185
|
|
108
186
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'cog/errors'
|
2
|
+
require 'cog/generator'
|
3
|
+
|
4
|
+
module Cog
|
5
|
+
class Config
|
6
|
+
|
7
|
+
class Tool
|
8
|
+
|
9
|
+
# @return [String] name of the tool
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @return [String] explicit path to the tool
|
13
|
+
attr_reader :path
|
14
|
+
|
15
|
+
# @return [String] directory in which to find tool templates
|
16
|
+
attr_reader :templates_path
|
17
|
+
|
18
|
+
# @return [Boolean] the tool needs to be explicitly required
|
19
|
+
attr_reader :explicit_require
|
20
|
+
|
21
|
+
# @param path [String] path to the <tt>cog_tool.rb</tt> file
|
22
|
+
# @option opt [Boolean] :built_in (false) if +true+, {#templates_path} will be +nil+ for this tool, as the templates should be included with +cog+
|
23
|
+
# @option opt [Boolean] :explicit_require (false) the tool needs to be explicitly required
|
24
|
+
def initialize(path, opt={})
|
25
|
+
unless File.basename(path) == 'cog_tool.rb' && File.exists?(path)
|
26
|
+
raise Errors::InvalidToolConfiguration.new(path)
|
27
|
+
end
|
28
|
+
dir = File.dirname(path)
|
29
|
+
@name = File.basename dir
|
30
|
+
@path = File.expand_path File.join(dir, '..', "#{@name}.rb")
|
31
|
+
@explicit_require = opt[:explicit_require]
|
32
|
+
unless opt[:built_in]
|
33
|
+
@templates_path = File.expand_path File.join(dir, '..', '..', 'cog', 'templates')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Fully load the tool
|
38
|
+
def load
|
39
|
+
require @path
|
40
|
+
end
|
41
|
+
|
42
|
+
# Encapsulates the block which stamps the generator for this tool
|
43
|
+
class GeneratorStamper
|
44
|
+
|
45
|
+
include Generator
|
46
|
+
|
47
|
+
# @return [Config] singleton instance
|
48
|
+
attr_accessor :config
|
49
|
+
|
50
|
+
# @return [String] path to the generator file which should be created
|
51
|
+
attr_reader :generator_dest
|
52
|
+
|
53
|
+
# @return [String] +underscore_version+ of the generator name
|
54
|
+
attr_reader :name
|
55
|
+
|
56
|
+
# @return [Tool] the tool for which this stamps generators
|
57
|
+
attr_reader :tool
|
58
|
+
|
59
|
+
# @return [String] +CamelizedVersion+ of the generator name
|
60
|
+
def camelized
|
61
|
+
@name.camelize
|
62
|
+
end
|
63
|
+
|
64
|
+
# @api developer
|
65
|
+
# @param tool [Tool]
|
66
|
+
# @param block [Block]
|
67
|
+
def initialize(tool, block)
|
68
|
+
@tool = tool
|
69
|
+
@block = block
|
70
|
+
end
|
71
|
+
|
72
|
+
# Stamp the generator
|
73
|
+
# @api developer
|
74
|
+
def stamp_generator(name, generator_dest, config)
|
75
|
+
@name = name.to_s.underscore
|
76
|
+
@config = config
|
77
|
+
@generator_dest = generator_dest
|
78
|
+
instance_eval &@block
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @api developer
|
83
|
+
# @return [GeneratorStamper]
|
84
|
+
attr_reader :generator_stamper
|
85
|
+
|
86
|
+
# Define a block to call when stamping a generator for this tool
|
87
|
+
#
|
88
|
+
# @yield The block should do the work of stamping the generator file, and any custom templates. The block's +self+ object will be a {GeneratorStamper}
|
89
|
+
def stamp_generator(&block)
|
90
|
+
@generator_stamper = GeneratorStamper.new self, block
|
91
|
+
end
|
92
|
+
|
93
|
+
# Sort tools by name
|
94
|
+
def <=>(other)
|
95
|
+
@name <=> other.name
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|