cog 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/bin/cog +6 -6
  2. data/lib/cog.rb +7 -11
  3. data/lib/cog/built_in_tools/basic.rb +1 -1
  4. data/lib/cog/built_in_tools/basic/cog_tool.rb +5 -12
  5. data/lib/cog/config.rb +47 -200
  6. data/lib/cog/config/cogfile.rb +5 -7
  7. data/lib/cog/config/lang_info.rb +1 -1
  8. data/lib/cog/config/language_methods.rb +71 -0
  9. data/lib/cog/config/project_methods.rb +35 -0
  10. data/lib/cog/config/tool_methods.rb +94 -0
  11. data/lib/cog/controllers/generator_controller.rb +7 -8
  12. data/lib/cog/controllers/template_controller.rb +18 -21
  13. data/lib/cog/controllers/tool_controller.rb +19 -22
  14. data/lib/cog/embed_context.rb +131 -0
  15. data/lib/cog/embeds.rb +84 -58
  16. data/lib/cog/embeds/file_scanner.rb +28 -5
  17. data/lib/cog/errors.rb +3 -0
  18. data/lib/cog/generator.rb +65 -31
  19. data/lib/cog/generator/file_methods.rb +1 -1
  20. data/lib/cog/generator/filters.rb +1 -1
  21. data/lib/cog/generator/language_methods.rb +3 -3
  22. data/lib/cog/helpers/string.rb +3 -3
  23. data/lib/cog/languages.rb +2 -0
  24. data/lib/cog/languages/c_language.rb +3 -27
  25. data/lib/cog/languages/c_plus_plus_language.rb +3 -8
  26. data/lib/cog/languages/c_sharp_language.rb +3 -16
  27. data/lib/cog/languages/java_language.rb +3 -16
  28. data/lib/cog/languages/java_script_language.rb +3 -16
  29. data/lib/cog/languages/language.rb +12 -12
  30. data/lib/cog/languages/mixins.rb +10 -0
  31. data/lib/cog/languages/mixins/c_style_comments.rb +23 -0
  32. data/lib/cog/languages/mixins/hash_comments.rb +19 -0
  33. data/lib/cog/languages/python_language.rb +2 -33
  34. data/lib/cog/languages/qt_project_language.rb +3 -34
  35. data/lib/cog/languages/ruby_language.rb +6 -31
  36. data/lib/cog/spec_helpers/matchers/match_maker.rb +10 -6
  37. data/lib/cog/tool.rb +61 -0
  38. data/lib/cog/tool/dsl.rb +26 -0
  39. data/lib/cog/version.rb +1 -1
  40. data/templates/basic/generator.rb.erb +1 -7
  41. data/templates/cog/custom_tool/cog_tool.rb.erb +10 -8
  42. data/templates/cog/custom_tool/generator.rb.erb.erb +3 -3
  43. data/templates/cog/custom_tool/tool.rb.erb +1 -1
  44. metadata +13 -16
  45. data/lib/cog/config/tool.rb +0 -99
  46. data/lib/cog/embeds/context.rb +0 -86
  47. data/templates/basic/template.c.erb.erb +0 -1
  48. data/templates/basic/template.cpp.erb.erb +0 -6
  49. data/templates/basic/template.cs.erb.erb +0 -6
  50. data/templates/basic/template.h.erb.erb +0 -1
  51. data/templates/basic/template.hpp.erb.erb +0 -6
  52. data/templates/basic/template.java.erb.erb +0 -1
  53. data/templates/basic/template.js.erb.erb +0 -1
  54. data/templates/basic/template.py.erb.erb +0 -1
  55. data/templates/basic/template.rb.erb.erb +0 -6
  56. data/templates/basic/template.txt.erb.erb +0 -1
@@ -0,0 +1,71 @@
1
+ require 'cog/config/lang_info'
2
+
3
+ module Cog
4
+ module Config
5
+
6
+ # {Config} methods related to languages
7
+ module LanguageMethods
8
+
9
+ # @return [Languages::Lanugage] target language which should be used when creating generators, and no language is explicitly specified
10
+ attr_accessor :target_language
11
+
12
+ # @return [Languages::Language] language which is active in the current context
13
+ def active_language
14
+ @active_languages.last
15
+ end
16
+
17
+ # Activate a given language within the scope of the provided block.
18
+ # Either provide <tt>:id</tt> or <tt>:ext</tt> but not both. If the extension does not match any of the supported languages, the {#active_language} will not change, but the block will still be called.
19
+ # @option opt [:String] :id (nil) the lanuage identifier. Type <tt>cog language list</tt> to see the possible values
20
+ # @option opt [:String] :ext (nil) a file extension which will map to a language identifier. Type <tt>cog language map</tt> to see mapped extensions
21
+ # @yield within this block the {#active_language} will be set to the desired value
22
+ # @return [Object] the value returned by the block
23
+ def activate_language(opt={}, &block)
24
+ throw :ActivateLanguageRequiresABlock if block.nil?
25
+ lang_id = if opt[:ext]
26
+ ext = opt[:ext].to_s
27
+ ext = ext.slice(1..-1) if ext.start_with?('.')
28
+ @language_extension_map[ext.downcase.to_sym] unless ext.empty?
29
+ else
30
+ opt[:id]
31
+ end
32
+ if lang_id
33
+ @active_languages << Languages.get_language(lang_id)
34
+ r = block.call
35
+ @active_languages.pop
36
+ r
37
+ else
38
+ block.call
39
+ end
40
+ end
41
+
42
+ # @return [Array<String>] list of file extensions for supported languages
43
+ def language_extensions
44
+ @language_extension_map.keys
45
+ end
46
+
47
+ # @param path [String] the file system extension, or full path to a file
48
+ # @return [Languages::Language, nil] the language for the given extension
49
+ def language_for(path)
50
+ ext = File.extname(path.to_s)
51
+ ext = path.to_s if ext.empty?
52
+ ext = ext.downcase
53
+ ext = ext.slice(1..-1) if ext.start_with? '.'
54
+ lang_id = @language_extension_map[ext.to_sym]
55
+ Languages.get_language lang_id unless lang_id.nil?
56
+ end
57
+
58
+ # @return [Array<LangInfo>] current configuration of supported languages
59
+ def language_summary
60
+ summary = {}
61
+ @language_extension_map.each_pair do |ext, lang_id|
62
+ lang_id = Languages::ALIAS[lang_id] || lang_id
63
+ summary[lang_id] ||= LangInfo.new(lang_id, Languages::REV_ALIAS[lang_id] || [])
64
+ summary[lang_id].extensions << ext
65
+ end
66
+ summary.values.sort
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,35 @@
1
+ module Cog
2
+ module Config
3
+
4
+ # {Config} methods related to projects
5
+ module ProjectMethods
6
+
7
+ # @return [String] path to the project's {Cogfile}
8
+ attr_reader :cogfile_path
9
+
10
+ # @return [String] directory in which to find project generators
11
+ attr_reader :project_generators_path
12
+
13
+ # @return [String] directory in which the project's {Cogfile} is found
14
+ attr_reader :project_root
15
+
16
+ # @return [String] directory to which project source code is generated
17
+ attr_reader :project_source_path
18
+
19
+ # @return [String] directory in which to find custom project templates
20
+ attr_reader :project_templates_path
21
+
22
+ # @return [Boolean] whether or not we operating in the context of a project
23
+ def project?
24
+ !@project_root.nil?
25
+ end
26
+
27
+ # @return [Array<String>] list of paths to files in the {#project_source_path} which are written in a supported language
28
+ def supported_project_files
29
+ exts = Cog.language_extensions.join ','
30
+ Dir.glob "#{Cog.project_source_path}/**/*.{#{exts}}"
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ module Cog
2
+ module Config
3
+
4
+ # {Config} methods related to tools
5
+ module ToolMethods
6
+
7
+ # @return [Tool] the active tool affects the creation of new generators
8
+ def active_tool
9
+ @active_tools.last
10
+ end
11
+
12
+ # @return [Array<Tool>] a sorted list of available tools
13
+ def tools
14
+ @tools.values.sort
15
+ end
16
+
17
+ # Register built-in and custom tools.
18
+ # @api developer
19
+ #
20
+ # Custom tools are specified in the +COG_TOOLS+ environment variable.
21
+ # @return [nil]
22
+ def register_tools
23
+ # Register built-in tools
24
+ [:basic].each do |built_in|
25
+ require File.join(Cog.gem_dir, 'lib', 'cog', 'built_in_tools', built_in.to_s, 'cog_tool.rb')
26
+ end
27
+ # Register custom tools defined in COG_TOOLS
28
+ (ENV['COG_TOOLS'] || '').split(':').each do |path|
29
+ explicit = path.end_with? '.rb'
30
+ @next_tool_was_required_explicitly = explicit
31
+ path = "#{path}/cog_tool" unless explicit
32
+ begin
33
+ require path
34
+ rescue LoadError
35
+ raise Errors::CouldNotLoadTool.new path
36
+ end
37
+ end
38
+ nil
39
+ end
40
+
41
+ # Define a new +cog+ tool
42
+ # @param path [String] path to the <tt>cog_tool.rb</tt> file. This method should be called from the <tt>cog_tool.rb</tt> file, in which case <tt>__FILE__</tt> should be used as the argument
43
+ # @option opt [Boolean] :built_in (false) if +true+, then treat this tool as a built-in tool. If you are defininig a custom tool, leave this value as +false+
44
+ # @yield [Tool::DSL] define the tool
45
+ # @return [nil]
46
+ # @example
47
+ # require 'cog'
48
+ # include Cog::Generator
49
+ #
50
+ # Cog.register_tool __FILE__ do |tool|
51
+ #
52
+ # # Define a method which creates the generator
53
+ # tool.stamp_generator do |name, dest|
54
+ #
55
+ # # Setup context for the template
56
+ # @name = name
57
+ #
58
+ # # Create the generator file
59
+ # stamp 'my_tool/generator.rb', dest, :absolute_destination => true
60
+ #
61
+ # end
62
+ # end
63
+ def register_tool(path, opt={}, &block)
64
+ tool = Tool.new path, :built_in => opt[:built_in], :explicit_require => @next_tool_was_required_explicitly
65
+ dsl = Tool::DSL.new tool
66
+ block.call dsl
67
+ @tools[tool.name] = tool
68
+ nil
69
+ end
70
+
71
+ # @api developer
72
+ # @return [Boolean] whether or not a tool is registered with the given name
73
+ def tool_registered?(name)
74
+ @tools.member? name
75
+ end
76
+
77
+ # Activate the registered tool with the given name within the scope of the provided block. If no block is provided, the tool will remain active indefinitely.
78
+ # @param name [String] name of the registered tool to activate
79
+ # @yield the tool will be active within this block
80
+ # @return [nil]
81
+ def activate_tool(name, &block)
82
+ name = name.to_s
83
+ raise Errors::NoSuchTool.new(name) unless tool_registered?(name)
84
+ @tools[name].load
85
+ @active_tools << @tools[name]
86
+ if block
87
+ block.call
88
+ @active_tools.pop
89
+ end
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -13,19 +13,18 @@ module Cog
13
13
  # @param name [String] the name to use for the new generator
14
14
  # @return [Boolean] was the generator successfully created?
15
15
  def self.create(name)
16
- raise Errors::ActionRequiresProject.new('create generator') unless Config.instance.project?
17
- generator_dest = File.join Config.instance.project_generators_path, "#{name}.rb"
16
+ raise Errors::ActionRequiresProject.new('create generator') unless Cog.project?
17
+ generator_dest = File.join Cog.project_generators_path, "#{name}.rb"
18
18
  raise Errors::DuplicateGenerator.new(generator_dest) if File.exists?(generator_dest)
19
- gs = Config.instance.active_tool.generator_stamper
20
- gs.stamp_generator name, generator_dest, Config.instance
19
+ Cog.active_tool.stamp_generator name, generator_dest
21
20
  end
22
21
 
23
22
  # List the available project generators
24
23
  # @option opt [Boolean] :verbose (false) list full paths to generator files
25
24
  # @return [Array<String>] a list of generators
26
25
  def self.list(opt={})
27
- raise Errors::ActionRequiresProject.new('list generators') unless Config.instance.project?
28
- x = Dir.glob(File.join Config.instance.project_generators_path, '*.rb')
26
+ raise Errors::ActionRequiresProject.new('list generators') unless Cog.project?
27
+ x = Dir.glob(File.join Cog.project_generators_path, '*.rb')
29
28
  opt[:verbose] ? x : (x.collect {|path| File.basename(path).slice(0..-4)})
30
29
  end
31
30
 
@@ -33,8 +32,8 @@ module Cog
33
32
  # @param name [String] name of the generator as returned by {GeneratorController.list}
34
33
  # @return [nil]
35
34
  def self.run(name)
36
- raise Errors::ActionRequiresProject.new('run generator') unless Config.instance.project?
37
- path = File.join Config.instance.project_generators_path, "#{name}.rb"
35
+ raise Errors::ActionRequiresProject.new('run generator') unless Cog.project?
36
+ path = File.join Cog.project_generators_path, "#{name}.rb"
38
37
  raise Errors::NoSuchGenerator.new(name) unless File.exists?(path)
39
38
  require path
40
39
  nil
@@ -15,13 +15,13 @@ module Cog
15
15
  # @return [Array<String>] a list of templates
16
16
  def self.list(opt={})
17
17
  cts = Helpers::CascadingTemplateSet.new
18
- cts.add_templates 'built-in', :built_in, Config.cog_templates_path, opt
19
- tool = Config.instance.active_tool
18
+ cts.add_templates 'built-in', :built_in, Cog.cog_templates_path, opt
19
+ tool = Cog.active_tool
20
20
  unless tool.templates_path.nil?
21
21
  cts.add_templates tool.name, :tool, tool.templates_path, opt
22
22
  end
23
- if Config.instance.project?
24
- cts.add_templates 'project', :project, Config.instance.project_templates_path, opt
23
+ if Cog.project?
24
+ cts.add_templates 'project', :project, Cog.project_templates_path, opt
25
25
  end
26
26
  cts.to_a
27
27
  end
@@ -31,25 +31,22 @@ module Cog
31
31
  # @option opt [Boolean] :force_override (false) if a built-in or tool template with the same name already exists, should a project override be created?
32
32
  # @return [nil]
33
33
  def self.create(name, opt={})
34
- raise Errors::ActionRequiresProject.new('create template') unless Config.instance.project?
34
+ raise Errors::ActionRequiresProject.new('create template') unless Cog.project?
35
35
  name = name.without_extension :erb
36
- dest = File.join Config.instance.project_templates_path, "#{name}.erb"
37
- Object.instance_eval do
38
- extend Generator
39
- begin
40
- original = get_template name, :as_path => true
41
- return if original == dest # Nothing to create
42
- if opt[:force_override]
43
- copy_file_if_missing original, dest
44
- else
45
- raise Errors::DuplicateTemplate.new original
46
- end
47
- rescue Errors::NoSuchTemplate
48
- # No original, ok to create an empty template
49
- touch_file dest
50
- end
51
- nil
36
+ dest = File.join Cog.project_templates_path, "#{name}.erb"
37
+
38
+ original = Generator.get_template name, :as_path => true
39
+ return if original == dest # Nothing to create
40
+ if opt[:force_override]
41
+ Generator.copy_file_if_missing original, dest
42
+ else
43
+ raise Errors::DuplicateTemplate.new original
52
44
  end
45
+ nil
46
+ rescue Errors::NoSuchTemplate
47
+ # No original, ok to create an empty template
48
+ touch_file dest
49
+ nil
53
50
  end
54
51
 
55
52
  end
@@ -16,38 +16,35 @@ module Cog
16
16
  # @return [nil]
17
17
  def self.create(name)
18
18
  raise Errors::DestinationAlreadyExists.new(name) if File.exists?(name)
19
- raise Errors::DuplicateTool.new(name) if Config.instance.tool_registered?(name)
20
- Object.new.instance_eval do
21
- extend Generator
22
- @tool_name = name.to_s.downcase
23
- @tool_module = name.to_s.capitalize
24
- @tool_author = '<Your name goes here>'
25
- @tool_email = 'youremail@...'
26
- @tool_description = 'A one-liner'
27
- @cog_version = Cog::VERSION
28
- stamp 'cog/custom_tool/tool.rb', "#{@tool_name}/lib/#{@tool_name}.rb", :absolute_destination => true
29
- stamp 'cog/custom_tool/cog_tool.rb', "#{@tool_name}/lib/#{@tool_name}/cog_tool.rb", :absolute_destination => true
30
- stamp 'cog/custom_tool/version.rb', "#{@tool_name}/lib/#{@tool_name}/version.rb", :absolute_destination => true
31
- stamp 'cog/custom_tool/generator.rb.erb', "#{@tool_name}/cog/templates/#{@tool_name}/generator.rb.erb", :absolute_destination => true
32
- stamp 'cog/custom_tool/template.txt.erb', "#{@tool_name}/cog/templates/#{@tool_name}/#{@tool_name}.txt.erb", :absolute_destination => true
33
- stamp 'cog/custom_tool/Gemfile', "#{@tool_name}/Gemfile", :absolute_destination => true
34
- stamp 'cog/custom_tool/Rakefile', "#{@tool_name}/Rakefile", :absolute_destination => true
35
- stamp 'cog/custom_tool/tool.gemspec', "#{@tool_name}/#{@tool_name}.gemspec", :absolute_destination => true
36
- stamp 'cog/custom_tool/LICENSE', "#{@tool_name}/LICENSE", :absolute_destination => true
37
- stamp 'cog/custom_tool/README.markdown', "#{@tool_name}/README.markdown", :absolute_destination => true
38
- end
19
+ raise Errors::DuplicateTool.new(name) if Cog.tool_registered?(name)
20
+ @tool_name = name.to_s.downcase
21
+ @tool_module = name.to_s.capitalize
22
+ @tool_author = '<Your name goes here>'
23
+ @tool_email = 'youremail@...'
24
+ @tool_description = 'A one-liner'
25
+ @cog_version = Cog::VERSION
26
+ opt = { :absolute_destination => true, :binding => binding }
27
+ Generator.stamp 'cog/custom_tool/tool.rb', "#{@tool_name}/lib/#{@tool_name}.rb", opt
28
+ Generator.stamp 'cog/custom_tool/cog_tool.rb', "#{@tool_name}/lib/#{@tool_name}/cog_tool.rb", opt
29
+ Generator.stamp 'cog/custom_tool/version.rb', "#{@tool_name}/lib/#{@tool_name}/version.rb", opt
30
+ Generator.stamp 'cog/custom_tool/generator.rb.erb', "#{@tool_name}/cog/templates/#{@tool_name}/generator.rb.erb", opt
31
+ Generator.stamp 'cog/custom_tool/template.txt.erb', "#{@tool_name}/cog/templates/#{@tool_name}/#{@tool_name}.txt.erb", opt
32
+ Generator.stamp 'cog/custom_tool/Gemfile', "#{@tool_name}/Gemfile", opt
33
+ Generator.stamp 'cog/custom_tool/Rakefile', "#{@tool_name}/Rakefile", opt
34
+ Generator.stamp 'cog/custom_tool/tool.gemspec', "#{@tool_name}/#{@tool_name}.gemspec", opt
35
+ Generator.stamp 'cog/custom_tool/LICENSE', "#{@tool_name}/LICENSE", opt
36
+ Generator.stamp 'cog/custom_tool/README.markdown', "#{@tool_name}/README.markdown", opt
39
37
  nil
40
38
  end
41
39
 
42
40
  # @param opt [Boolean] :verbose (false) list full paths to tools
43
41
  # @return [Array<String>] a list of available tools
44
42
  def self.list(opt={})
45
- Config.instance.tools.collect do |tool|
43
+ Cog.tools.collect do |tool|
46
44
  opt[:verbose] ? tool.path : tool.name
47
45
  end
48
46
  end
49
47
 
50
-
51
48
  end
52
49
  end
53
50
  end
@@ -0,0 +1,131 @@
1
+ require 'cog/config'
2
+
3
+ module Cog
4
+
5
+ # Describes the environment of an embed statement including the file in which it was found, the line number, the language, an more.
6
+ class EmbedContext
7
+
8
+ # @return [String] for expanded embed statements, the body of text which occurs between the curly braces
9
+ # @example
10
+ # // cog: my-hook {
11
+ # this is
12
+ # the
13
+ # body
14
+ # // cog: }
15
+ attr_reader :body
16
+
17
+ # @return [String] hook name used in the embed statement
18
+ # @example
19
+ # // cog: this-is-the-hook-name
20
+ attr_reader :hook
21
+
22
+ # @return [Fixnum] line number at which the embed statement was found, with 1 being the first line in the file
23
+ attr_reader :lineno
24
+
25
+ # @return [Fixnum] if multiple embeds with the same {#hook} occurred more than once in the same file, this value indicates to which occurrence this context belongs. 0 for the first, 1 for the second, and so on...
26
+ attr_reader :index
27
+
28
+ # @return [Fixnum] if multiple embeds with the same {#hook} occurred more than once in the same file, this value indicates the total number of occurrences
29
+ attr_reader :count
30
+
31
+ # @return [String] full file system path to the file
32
+ attr_reader :path
33
+
34
+ # @api developer
35
+ # @param hook [String] hook name used in the embed statement
36
+ # @param path [String] path to the file in which the cog directive was found
37
+ # @param count [Fixnum] total occurrences in file
38
+ def initialize(hook, path, count)
39
+ @hook = hook.to_s
40
+ @path = path.to_s
41
+ @count = count
42
+ @eaten = 0
43
+ end
44
+
45
+ # @return [Array<String>] arguments provided with the embed statement
46
+ # @example
47
+ # // cog: my-hook (these are the args)
48
+ def args
49
+ @args
50
+ end
51
+
52
+ # @return [String] the filename extension (without the period)
53
+ def extension
54
+ @ext ||= File.extname(filename).slice(1..-1)
55
+ end
56
+
57
+ # @return [String] basename of the file in which the embed statement was found
58
+ def filename
59
+ @filename ||= File.basename @path
60
+ end
61
+
62
+ # @return [Boolean] whether or not this was the first occurrence of the embed {#hook} in the file
63
+ def first?
64
+ @index == 0
65
+ end
66
+
67
+ # @return [Boolean] whether or not this was the last occurrence of the embed {#hook} in the file
68
+ def last?
69
+ @index == @count - 1
70
+ end
71
+
72
+ # @return [Boolean] was the 'once' switch used? Embed statements using this switch will be removed after the statement is expanded
73
+ # @example
74
+ # // cog: my-hook once
75
+ def once?
76
+ @once
77
+ end
78
+
79
+ # @api developer
80
+ # @param value [String, nil] set the body to this value
81
+ def body=(value)
82
+ @body = value
83
+ end
84
+
85
+ # @api developer
86
+ # @param value [Fixnum] set the line number
87
+ def lineno=(value)
88
+ @lineno = value
89
+ end
90
+
91
+ # @api developer
92
+ # @param value [Boolean] was the once switch used?
93
+ def once=(value)
94
+ @once = !!value
95
+ end
96
+
97
+ # @api developer
98
+ # @param value [Array<String>] arguments used with the directive
99
+ def args=(value)
100
+ @args = value
101
+ end
102
+
103
+ # @api developer
104
+ # @param index [Fixnum] occurrence in file, 0 for first
105
+ def index=(index)
106
+ @index = index
107
+ end
108
+
109
+ # @api developer
110
+ # @param value [Fixnum] the number of once statements before this one that were eaten. Used to adjust the actual_index so that the file_scanner looks for the right statement
111
+ def eaten=(value)
112
+ @eaten = value
113
+ end
114
+
115
+ # @api developer
116
+ # @return [Fixnum] takes into account the number of once statements that have been eaten
117
+ def actual_index
118
+ @index - @eaten
119
+ end
120
+
121
+ # @api developer
122
+ # @return [String]
123
+ def to_directive
124
+ x = "cog: #{hook}"
125
+ x += "(#{args.join ' '})" if args
126
+ x += " once" if once?
127
+ x
128
+ end
129
+
130
+ end
131
+ end