cog 0.0.20 → 0.1.0

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.
Files changed (36) hide show
  1. data/bin/cog +54 -53
  2. data/lib/cog.rb +4 -3
  3. data/lib/cog/built_in_tools/basic.rb +10 -0
  4. data/lib/cog/built_in_tools/basic/cog_tool.rb +15 -0
  5. data/lib/cog/config.rb +111 -33
  6. data/lib/cog/config/tool.rb +99 -0
  7. data/lib/cog/controllers.rb +13 -0
  8. data/lib/cog/controllers/generator_controller.rb +45 -0
  9. data/lib/cog/controllers/template_controller.rb +31 -0
  10. data/lib/cog/controllers/tool_controller.rb +53 -0
  11. data/lib/cog/errors.rb +51 -9
  12. data/lib/cog/generator.rb +1 -74
  13. data/lib/cog/helpers.rb +1 -0
  14. data/lib/cog/helpers/cascading_template_set.rb +75 -0
  15. data/lib/cog/helpers/string.rb +9 -3
  16. data/lib/cog/version.rb +1 -1
  17. data/templates/basic/generator.rb.erb +12 -0
  18. data/templates/{cog/generator/basic-template.txt.erb.erb → basic/template.txt.erb.erb} +0 -0
  19. data/templates/cog/{tool → custom_tool}/Gemfile.erb +4 -0
  20. data/templates/cog/{tool → custom_tool}/LICENSE.erb +1 -1
  21. data/templates/cog/custom_tool/README.markdown.erb +18 -0
  22. data/templates/cog/custom_tool/Rakefile.erb +15 -0
  23. data/templates/cog/custom_tool/cog_tool.rb.erb +15 -0
  24. data/templates/cog/custom_tool/generator.rb.erb.erb +9 -0
  25. data/templates/cog/{tool → custom_tool}/template.txt.erb.erb +0 -0
  26. data/templates/cog/custom_tool/tool.gemspec.erb +18 -0
  27. data/templates/cog/{tool → custom_tool}/tool.rb.erb +5 -13
  28. data/templates/cog/{tool → custom_tool}/version.rb.erb +1 -1
  29. metadata +24 -17
  30. data/lib/cog/tool.rb +0 -81
  31. data/templates/cog/generator/basic.rb.erb +0 -12
  32. data/templates/cog/tool/API.rdoc.erb +0 -7
  33. data/templates/cog/tool/README.markdown.erb +0 -18
  34. data/templates/cog/tool/Rakefile.erb +0 -15
  35. data/templates/cog/tool/generator.rb.erb.erb +0 -6
  36. 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 |global_options, options, args|
30
+ c.action do |gopt, opt, args|
22
31
  Cog.initialize_project
23
32
  end
24
33
  end
25
34
 
26
- desc 'List or create generators for the current project'
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 for the current project'
40
+ c.desc 'List project generators'
32
41
  c.command :list do |sub|
33
- sub.action do |global_options, option, args|
34
- x = Cog::Generator.list global_options[:v]
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 for the current project'
40
- c.arg_name 'generator_name'
48
+ c.desc 'Create a new project generator'
49
+ c.arg_name 'name'
41
50
  c.command :new do |sub|
42
-
43
- sub.desc 'which tool to use to create the generator'
44
- sub.arg_name 'tool_name'
45
- sub.default_value 'basic'
46
- sub.flag :tool
47
-
48
- sub.action do |global_options, options, args|
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::Generator.create name, :tool => options[:tool], :verbose => global_options[:v]
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 |global_options, options, args|
65
- x = Cog::Tool.list global_options[:v]
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 |global_options, options, args|
87
+ sub.action do |gopt, opt, args|
74
88
  args.each do |name|
75
- Cog::Tool.create name
89
+ Cog::Controllers::ToolController.create name
76
90
  end
77
91
  end
78
92
  end
79
93
  end
80
94
 
81
- desc 'Run generators in the current project'
82
- arg_name 'generator_name'
83
- command :run do |c|
95
+ desc 'Manage templates'
96
+ command :template do |c|
84
97
 
85
- c.action do |global_options, options, args|
86
- args = Cog::Generator.list if args.empty?
87
- args.each do |name|
88
- Cog::Generator.run name, :verbose => global_options[:v]
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
- desc 'List supported languages'
94
- skips_pre
95
- command :languages do |c|
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
- begin
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/tool'
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,10 @@
1
+ module Cog
2
+ module Tools
3
+
4
+ # The default +cog+ tool.
5
+ # Creates generator/template pairs.
6
+ module BasicTool
7
+ end
8
+
9
+ end
10
+ 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
@@ -1,5 +1,6 @@
1
1
  require 'cog/config/cogfile'
2
- require 'singleton'
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 [Boolean] are we operating in the context of a project?
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}, 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
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, @tool_templates_path, File.join(Config.gem_dir, 'templates')].compact
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
- # The singleton instance
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, then {#project?} will be +false+ and all the +project_...+
71
- # attributes will be +nil+.
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 Config instance
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.instance_eval do
91
- @cogfile_path = File.expand_path path
92
- @project_root = File.dirname @cogfile_path
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
- private
102
-
103
- def initialize
104
- @project_root = nil
105
- @language = 'c++'
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