cog 0.0.20 → 0.1.0

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