cog 0.2.1 → 0.2.2

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 (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