cog 0.2.0 → 0.2.1

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.
data/bin/cog CHANGED
@@ -73,7 +73,7 @@ command [:generator, :gen] do |c|
73
73
  c.command :run do |sub|
74
74
  sub.action do |gopt, opt, args|
75
75
  raise Cog::Errors::ActionRequiresProject.new('run generator') unless Cog::Config.instance.project?
76
- Cog::Project.gather_cog_directives
76
+ Cog::Embeds.gather_from_project
77
77
  args = Cog::Controllers::GeneratorController.list if args.empty?
78
78
  args.each do |name|
79
79
  Cog::Controllers::GeneratorController.run name
data/lib/cog.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  require 'cog/config'
2
2
  require 'cog/controllers'
3
+ require 'cog/embeds'
3
4
  require 'cog/errors'
4
5
  require 'cog/generator'
5
6
  require 'cog/helpers'
6
7
  require 'cog/languages'
7
- require 'cog/project'
8
8
  require 'cog/version'
9
9
 
10
10
  # +cog+ is a command line utility that makes it a bit easier to organize a project
@@ -10,10 +10,31 @@ module Cog
10
10
  # +Cogfile+ files are used to configure an instance of {Config}.
11
11
  #
12
12
  # @example
13
+ # # All paths are relative to the directory containing this file.
14
+ #
15
+ # # Define the directory in which to find project generators
13
16
  # project_generators_path 'cog/generators'
17
+ #
18
+ # # Define the directory in which to find custom project templates
14
19
  # project_templates_path 'cog/templates'
20
+ #
21
+ # # Define the directory to which project source code is generated
15
22
  # project_source_path 'src'
16
- # language 'c++'
23
+ #
24
+ # # Explicitly specify a mapping from file extensions to languages
25
+ # #
26
+ # # key => value pairs from this mapping will override the default
27
+ # # language map supplied by +cog+
28
+ # #
29
+ # # Type `cog language list` to see a list of supported languages
30
+ # # and the current file extension mappings
31
+ # language_extensions({
32
+ # # :h => 'c++',
33
+ # })
34
+ #
35
+ # # Define the target language which should be used when
36
+ # # creating generators, and no language is explicitly specified
37
+ # default_target_language 'c++'
17
38
  #
18
39
  class Cogfile
19
40
 
@@ -0,0 +1,89 @@
1
+ require 'cog/config'
2
+ require 'cog/embeds/context'
3
+ require 'cog/embeds/file_scanner'
4
+
5
+ module Cog
6
+
7
+ # Methods for querying and manipulating project files
8
+ module Embeds
9
+
10
+ # Search through all project files for cog embeds, and remember them so that generators can refer to them later
11
+ def gather_from_project
12
+ @embeds ||= {}
13
+ @embed_pattern ||= "cog\\s*:\\s*([-A-Za-z0-9_.]+)\\s*(?:\\(\\s*(.+?)\\s*\\))?(\\s*once\\s*)?(\\s*\\{)?"
14
+ exts = Config.instance.language_summary.collect(&:extensions).flatten
15
+ sources = Dir.glob "#{Config.instance.project_source_path}/**/*.{#{exts.join ','}}"
16
+ sources.each do |filename|
17
+ ext = File.extname(filename).slice(1..-1)
18
+ lang = Config.instance.language_for_extension ext
19
+ w = File.read filename
20
+ w.scan(lang.comment_pattern(@embed_pattern)) do |m|
21
+ key = m[0].split.first
22
+ @embeds[key] ||= {}
23
+ @embeds[key][filename] ||= 0
24
+ @embeds[key][filename] += 1
25
+ end
26
+ end
27
+ end
28
+
29
+ # @param key [String] embed key for which to find directive occurrences
30
+ # @yieldparam filename [String] name of the file in which the embed occurred
31
+ # @yieldparam index [Fixnum] occurrence index of the embed, 0 for the first occurrence, 1 for the second, and so on
32
+ def find(key)
33
+ x = @embeds[key]
34
+ unless x.nil?
35
+ x.keys.sort.each do |filename|
36
+ x[filename].times do |index|
37
+ yield filename, index
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ # @param key [String] unique identifier for the embed
44
+ # @param filename [String] file in which to look for the embed
45
+ # @param index [Fixnum] occurrence of the embed. 0 for first, 1 for second, ...
46
+ # @yieldparam context [Context] describes the context in which the directive occurred
47
+ # @yieldreturn [String] the value to substitute into the embed expansion
48
+ # @return [Boolean] whether or not the expansion was updated
49
+ def update(key, filename, index, &block)
50
+ c = Context.new key, filename
51
+ snip_pattern = c.language.comment_pattern("cog\\s*:\\s*(#{key})\\s*(?:\\(\\s*(.+?)\\s*\\))?(\\s*once\\s*)?(?:\\s*([{]))?")
52
+ end_pattern = c.language.comment_pattern("cog\\s*:\\s*[}]")
53
+ not_end_pattern = c.language.comment_pattern("cog\\s*:\\s*(?!\\s*[}]).*$")
54
+
55
+ s = FileScanner.new filename
56
+ updated = if match = s.read_until(snip_pattern, index)
57
+ if match.nil? # embed not found
58
+ false
59
+ else
60
+ c.lineno = s.marked_line_number
61
+ c.args = match[2].split if match[2]
62
+ c.once = !match[3].nil?
63
+ if match[4] == '{' # embed already expanded
64
+ unless s.capture_until end_pattern, :but_not => not_end_pattern
65
+ raise Errors::SnippetExpansionUnterminated.new "#{filename.relative_to_project_root}:#{s.marked_line_number}"
66
+ end
67
+ c.body = s.captured_text
68
+ value = block.call(c).rstrip
69
+ if c.once? || value != s.captured_text
70
+ s.replace_captured_text(value + "\n", :once => c.once?)
71
+ end
72
+ else # embed not yet expanded
73
+ value = block.call(c).rstrip
74
+ snip_line = c.language.comment "#{c.to_directive} {"
75
+ unless c.once?
76
+ value = [snip_line, value, c.language.comment("cog: }")].join("\n")
77
+ end
78
+ s.insert_at_mark(value + "\n")
79
+ end
80
+ end
81
+ end
82
+ s.close
83
+ updated
84
+ end
85
+
86
+ extend self # Singleton
87
+
88
+ end
89
+ end
@@ -0,0 +1,86 @@
1
+ require 'cog/config'
2
+
3
+ module Cog
4
+ module Embeds
5
+
6
+ # Describes the environment of an embed statement including the file in which it was found, the line number, the language, an more.
7
+ class Context
8
+
9
+ # @return [Fixnum] line number at which the directive was found
10
+ attr_accessor :lineno
11
+
12
+ # @return [String] the value for the given key
13
+ attr_reader :name
14
+
15
+ # @param path [String] path to the file in which the cog directive was found
16
+ def initialize(name, path)
17
+ @name = name
18
+ @options = {}
19
+ @path = path
20
+ end
21
+
22
+ # @return [Array<String>] arguments passed to the directive
23
+ def args
24
+ @args
25
+ end
26
+
27
+ # @return [String] for block directives, the body of text which occurs between the curly braces.
28
+ # @example
29
+ # // cog: snippet(my-snip) {
30
+ # body
31
+ # // cog: }
32
+ def body
33
+ @body
34
+ end
35
+
36
+ # @return [String] the filename extension
37
+ def extension
38
+ @ext ||= File.extname(filename).slice(1..-1)
39
+ end
40
+
41
+ # @return [String] basename of the file in which the cog directive was found
42
+ def filename
43
+ @filename ||= File.basename @path
44
+ end
45
+
46
+ # @return [Languages::Language] the language of the file
47
+ def language
48
+ @lang ||= Config.instance.language_for_extension extension
49
+ end
50
+
51
+ # @return [Boolean] was the once switch used?
52
+ def once?
53
+ @once
54
+ end
55
+
56
+ # @api developer
57
+ # @param value [String, nil] set the body to this value
58
+ def body=(value)
59
+ @body = value
60
+ end
61
+
62
+ # @api developer
63
+ # @param value [Boolean] was the once switch used?
64
+ def once=(value)
65
+ @once = !!value
66
+ end
67
+
68
+ # @api developer
69
+ # @param value [Array<String>] arguments used with the directive
70
+ def args=(value)
71
+ @args = value
72
+ end
73
+
74
+ # @api developer
75
+ # @return [String]
76
+ def to_directive
77
+ x = "cog: #{name}"
78
+ x += args.join ' ' if args
79
+ x += " once" if once?
80
+ x
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
@@ -1,77 +1,7 @@
1
- require 'cog/config'
2
-
3
1
  module Cog
4
-
5
- # Methods for querying and manipulating project files
6
- module Project
2
+ module Embeds
7
3
 
8
- # Search through all project files for cog directives, and remember them so that generators can refer to them later
9
- def gather_cog_directives
10
- @snippets ||= {}
11
- @snippet_pattern ||= "cog\\s*:\\s*snippet\\s*\\(\\s*(.*?)\\s*\\)(\\s*\\{)?"
12
- exts = Config.instance.language_summary.collect(&:extensions).flatten
13
- sources = Dir.glob "#{Config.instance.project_source_path}/**/*.{#{exts.join ','}}"
14
- sources.each do |filename|
15
- ext = File.extname(filename).slice(1..-1)
16
- lang = Config.instance.language_for_extension ext
17
- w = File.read filename
18
- w.scan(lang.comment_pattern(@snippet_pattern)) do |m|
19
- key = m[0]
20
- @snippets[key] ||= {}
21
- @snippets[key][filename] ||= 0
22
- @snippets[key][filename] += 1
23
- end
24
- end
25
- end
26
-
27
- # @param key [String] snippet key for which to find directive occurrences
28
- # @yieldparam filename [String] name of the file in which the snippet occurred
29
- # @yieldparam index [Fixnum] occurrence index of the snippet, 0 for the first occurrence, 1 for the second, and so on
30
- def snippet_directives(key)
31
- x = @snippets[key]
32
- unless x.nil?
33
- x.keys.sort.each do |filename|
34
- x[filename].times do |index|
35
- yield filename, index
36
- end
37
- end
38
- end
39
- end
40
-
41
- # @param key [String] unique identifier for the snippet
42
- # @param filename [String] file in which to look for the snippet
43
- # @param index [Fixnum] occurrence of the snippet. 0 for first, 1 for second, ...
44
- # @param value [String] expansion value to use as the update
45
- # @return [Boolean] whether or not the expansion was updated
46
- def update_snippet_expansion(key, filename, index, value)
47
- value = value.rstrip
48
- ext = File.extname(filename).slice(1..-1)
49
- lang = Config.instance.language_for_extension ext
50
-
51
- snip_pattern = lang.comment_pattern("cog\\s*:\\s*snippet\\s*\\(\\s*(#{key})\\s*\\)(?:\\s*([{]))?")
52
- end_pattern = lang.comment_pattern("cog\\s*:\\s*[}]")
53
- not_end_pattern = lang.comment_pattern("cog\\s*:\\s*(?!\\s*[}]).*$")
54
-
55
- s = FileScanner.new filename
56
- updated = if match = s.read_until(snip_pattern, index)
57
- if match.nil? # snippet not found
58
- false
59
- elsif match[2] == '{' # snippet already expanded
60
- unless s.capture_until end_pattern, :but_not => not_end_pattern
61
- raise Errors::SnippetExpansionUnterminated.new "#{filename.relative_to_project_root}:#{s.marked_line_number}"
62
- end
63
- s.replace_captured_text(value + "\n") if value != s.captured_text
64
- else # snippet not yet expanded
65
- snip_line = lang.comment "cog: snippet(#{match[1]}) {"
66
- value = [snip_line, value, lang.comment("cog: }") + "\n"]
67
- s.insert_at_mark value.join("\n")
68
- end
69
- end
70
- s.close
71
- updated
72
- end
73
-
74
- # Helper for scanning files for snippet expansions
4
+ # Helper for scanning files for embed expansions
75
5
  class FileScanner
76
6
 
77
7
  def initialize(filename)
@@ -92,7 +22,7 @@ module Cog
92
22
  # @return [nil]
93
23
  def mark!
94
24
  @mark = @f.pos
95
- @marked_line_number = @f.lineno
25
+ @marked_line_number = @f.lineno + 1
96
26
  end
97
27
 
98
28
  def unmark!
@@ -161,13 +91,15 @@ module Cog
161
91
  end
162
92
 
163
93
  # @param value [String] value to replace the captured_text with
94
+ # @param opt [Boolean] :once (false) if once, the cog delimiters will be removed
164
95
  # @return [Boolean] whether or not the operation succeeded
165
- def replace_captured_text(value)
96
+ def replace_captured_text(value, opt={})
166
97
  return false if @cap_begin_pos.nil? || @cap_end_pos.nil?
167
98
  @f.seek 0
168
- tmp.write @f.read(@cap_begin_pos)
99
+ tmp.write @f.read(opt[:once] ? @mark : @cap_begin_pos)
169
100
  tmp.write value
170
101
  @f.seek @cap_end_pos
102
+ @f.readline if opt[:once]
171
103
  tmp.write @f.read
172
104
  tmp.close
173
105
  close
@@ -201,7 +133,5 @@ module Cog
201
133
  end
202
134
  end
203
135
 
204
- extend self # Singleton
205
136
  end
206
-
207
137
  end
@@ -65,7 +65,7 @@ module Cog
65
65
  end
66
66
 
67
67
  define_error :SnippetExpansionUnterminated, 'location' do
68
- "a snippet expansion in the given file is missing the 'cog: }' terminator"
68
+ "a embed expansion in the given file is missing the 'cog: }' terminator"
69
69
  end
70
70
 
71
71
  end
@@ -72,14 +72,15 @@ module Cog
72
72
  nil
73
73
  end
74
74
 
75
- # Provide a value for the snippet with the given key
76
- # @param key [String] a unique identifier for the snippet
77
- # @yield The return value of the provided block will be used to expand the snippet
75
+ # Provide a value for the embed with the given key
76
+ # @param key [String] a unique identifier for the embed
77
+ # @yieldparam context [Embeds::Context] provides information about the environment in which the embed statement was found
78
+ # @yieldreturn The value which will be used to expand the embed (or replace the embedded content)
78
79
  # @return [nil]
79
- def snippet(key, &block)
80
- Project.snippet_directives(key) do |filename, index|
81
- if Project.update_snippet_expansion key, filename, index, block.call
82
- STDOUT.write "Updated #{filename.relative_to_project_root} - #{(index + 1).ordinalize} occurrence of snippet '#{key}'\n".color :white
80
+ def embed(key, &block)
81
+ Embeds.find(key) do |filename, index|
82
+ if Embeds.update key, filename, index, &block
83
+ STDOUT.write "Updated #{filename.relative_to_project_root} - #{(index + 1).ordinalize} occurrence of embed '#{key}'\n".color :white
83
84
  end
84
85
  end
85
86
  end
@@ -1,5 +1,5 @@
1
1
  module Cog
2
2
  unless const_defined? :VERSION
3
- VERSION = '0.2.0'
3
+ VERSION = '0.2.1'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cog
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 0
10
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kevin Tonon
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-12-09 00:00:00 Z
18
+ date: 2012-12-12 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: gli
@@ -146,6 +146,9 @@ files:
146
146
  - lib/cog/controllers/template_controller.rb
147
147
  - lib/cog/controllers/tool_controller.rb
148
148
  - lib/cog/controllers.rb
149
+ - lib/cog/embeds/context.rb
150
+ - lib/cog/embeds/file_scanner.rb
151
+ - lib/cog/embeds.rb
149
152
  - lib/cog/errors.rb
150
153
  - lib/cog/generator/file_methods.rb
151
154
  - lib/cog/generator/filters.rb
@@ -165,7 +168,6 @@ files:
165
168
  - lib/cog/languages/qt_project_language.rb
166
169
  - lib/cog/languages/ruby_language.rb
167
170
  - lib/cog/languages.rb
168
- - lib/cog/project.rb
169
171
  - lib/cog/spec_helpers/matchers/match_maker.rb
170
172
  - lib/cog/spec_helpers/matchers.rb
171
173
  - lib/cog/spec_helpers/runner.rb