cog 0.2.0 → 0.2.1

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