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.
- data/bin/cog +6 -6
- data/lib/cog.rb +7 -11
- data/lib/cog/built_in_tools/basic.rb +1 -1
- data/lib/cog/built_in_tools/basic/cog_tool.rb +5 -12
- data/lib/cog/config.rb +47 -200
- data/lib/cog/config/cogfile.rb +5 -7
- data/lib/cog/config/lang_info.rb +1 -1
- data/lib/cog/config/language_methods.rb +71 -0
- data/lib/cog/config/project_methods.rb +35 -0
- data/lib/cog/config/tool_methods.rb +94 -0
- data/lib/cog/controllers/generator_controller.rb +7 -8
- data/lib/cog/controllers/template_controller.rb +18 -21
- data/lib/cog/controllers/tool_controller.rb +19 -22
- data/lib/cog/embed_context.rb +131 -0
- data/lib/cog/embeds.rb +84 -58
- data/lib/cog/embeds/file_scanner.rb +28 -5
- data/lib/cog/errors.rb +3 -0
- data/lib/cog/generator.rb +65 -31
- data/lib/cog/generator/file_methods.rb +1 -1
- data/lib/cog/generator/filters.rb +1 -1
- data/lib/cog/generator/language_methods.rb +3 -3
- data/lib/cog/helpers/string.rb +3 -3
- data/lib/cog/languages.rb +2 -0
- data/lib/cog/languages/c_language.rb +3 -27
- data/lib/cog/languages/c_plus_plus_language.rb +3 -8
- data/lib/cog/languages/c_sharp_language.rb +3 -16
- data/lib/cog/languages/java_language.rb +3 -16
- data/lib/cog/languages/java_script_language.rb +3 -16
- data/lib/cog/languages/language.rb +12 -12
- data/lib/cog/languages/mixins.rb +10 -0
- data/lib/cog/languages/mixins/c_style_comments.rb +23 -0
- data/lib/cog/languages/mixins/hash_comments.rb +19 -0
- data/lib/cog/languages/python_language.rb +2 -33
- data/lib/cog/languages/qt_project_language.rb +3 -34
- data/lib/cog/languages/ruby_language.rb +6 -31
- data/lib/cog/spec_helpers/matchers/match_maker.rb +10 -6
- data/lib/cog/tool.rb +61 -0
- data/lib/cog/tool/dsl.rb +26 -0
- data/lib/cog/version.rb +1 -1
- data/templates/basic/generator.rb.erb +1 -7
- data/templates/cog/custom_tool/cog_tool.rb.erb +10 -8
- data/templates/cog/custom_tool/generator.rb.erb.erb +3 -3
- data/templates/cog/custom_tool/tool.rb.erb +1 -1
- metadata +13 -16
- data/lib/cog/config/tool.rb +0 -99
- data/lib/cog/embeds/context.rb +0 -86
- data/templates/basic/template.c.erb.erb +0 -1
- data/templates/basic/template.cpp.erb.erb +0 -6
- data/templates/basic/template.cs.erb.erb +0 -6
- data/templates/basic/template.h.erb.erb +0 -1
- data/templates/basic/template.hpp.erb.erb +0 -6
- data/templates/basic/template.java.erb.erb +0 -1
- data/templates/basic/template.js.erb.erb +0 -1
- data/templates/basic/template.py.erb.erb +0 -1
- data/templates/basic/template.rb.erb.erb +0 -6
- data/templates/basic/template.txt.erb.erb +0 -1
data/lib/cog/embeds.rb
CHANGED
@@ -1,89 +1,115 @@
|
|
1
1
|
require 'cog/config'
|
2
|
-
require 'cog/
|
2
|
+
require 'cog/embed_context'
|
3
3
|
require 'cog/embeds/file_scanner'
|
4
|
+
require 'cog/errors'
|
4
5
|
|
5
6
|
module Cog
|
6
7
|
|
8
|
+
# @api developer
|
7
9
|
# Methods for querying and manipulating project files
|
8
10
|
module Embeds
|
9
11
|
|
10
12
|
# Search through all project files for cog embeds, and remember them so that generators can refer to them later
|
11
13
|
def gather_from_project
|
12
14
|
@embeds ||= {}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
15
|
+
Cog.supported_project_files.each do |filename|
|
16
|
+
lang = Cog.language_for filename
|
17
|
+
File.read(filename).scan(statement '[-A-Za-z0-9_.]+', :lang => lang) do |m|
|
18
|
+
hook = m[0]
|
19
|
+
@embeds[hook] ||= {}
|
20
|
+
@embeds[hook][filename] ||= 0
|
21
|
+
@embeds[hook][filename] += 1
|
25
22
|
end
|
26
23
|
end
|
27
24
|
end
|
28
25
|
|
29
|
-
# @param
|
30
|
-
# @yieldparam
|
31
|
-
|
32
|
-
|
33
|
-
x = @embeds[key]
|
26
|
+
# @param hook [String] embed hook for which to find directive occurrences
|
27
|
+
# @yieldparam context [EmbedContext] describes the context in which the embed statement was found
|
28
|
+
def find(hook)
|
29
|
+
x = @embeds[hook]
|
34
30
|
unless x.nil?
|
35
31
|
x.keys.sort.each do |filename|
|
36
|
-
x[filename]
|
37
|
-
|
32
|
+
c = EmbedContext.new hook, filename, x[filename]
|
33
|
+
Cog.activate_language :ext => c.extension do
|
34
|
+
c.count.times do |index|
|
35
|
+
c.index = index
|
36
|
+
yield c
|
37
|
+
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
# @param
|
44
|
-
# @
|
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
|
43
|
+
# @param c [EmbedContext] describes the context in which the embed statement was found
|
44
|
+
# @yieldparam context [EmbedContext] describes the context in which the embed statement was found
|
47
45
|
# @yieldreturn [String] the value to substitute into the embed expansion
|
48
|
-
# @return [
|
49
|
-
def update(
|
50
|
-
c
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
updated = if match = s.read_until(snip_pattern, index)
|
57
|
-
if match.nil? # embed not found
|
58
|
-
false
|
46
|
+
# @return [Hash] whether or not the expansion was updated
|
47
|
+
def update(c, &block)
|
48
|
+
FileScanner.scan(c.path, statement(c.hook), :occurrence => c.actual_index) do |s|
|
49
|
+
c.lineno = s.marked_line_number
|
50
|
+
c.args = s.match[2].split if s.match[2]
|
51
|
+
c.once = !s.match[3].nil?
|
52
|
+
if s.match[4] == '{'
|
53
|
+
update_body c, s, &block
|
59
54
|
else
|
60
|
-
c
|
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
|
55
|
+
expand_body c, s, &block
|
80
56
|
end
|
81
57
|
end
|
82
|
-
s.close
|
83
|
-
updated
|
84
58
|
end
|
85
59
|
|
86
|
-
|
60
|
+
private
|
61
|
+
|
62
|
+
# Pattern groups are
|
63
|
+
# * 1 - hook
|
64
|
+
# * 2 - args
|
65
|
+
# * 3 - once
|
66
|
+
# * 4 - expansion begin (curly <tt>{</tt>)
|
67
|
+
# @return [Regexp] pattern to match the beginning of a cog embed statement
|
68
|
+
def statement(hook, opt={})
|
69
|
+
lang = opt[:lang] || Cog.active_language
|
70
|
+
lang.comment_pattern("cog\\s*:\\s*(#{hook})\\s*(?:\\(\\s*(.+?)\\s*\\))?(\\s*once\\s*)?(?:\\s*([{]))?")
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Regexp] pattern to match the end of a cog embed statement
|
74
|
+
def end_statement
|
75
|
+
Cog.active_language.comment_pattern("cog\\s*:\\s*[}]")
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Regexp] pattern to match an line that looks like a cog statement, but is not the end statement
|
79
|
+
def anything_but_end
|
80
|
+
Cog.active_language.comment_pattern("cog\\s*:\\s*(?!\\s*[}]).*$")
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param c [EmbedContext]
|
84
|
+
# @param s [FileScanner]
|
85
|
+
# @return [Boolean] whether or not the scanner updated its file
|
86
|
+
def update_body(c, s, &block)
|
87
|
+
unless s.capture_until end_statement, :but_not => anything_but_end
|
88
|
+
raise Errors::SnippetExpansionUnterminated.new "#{c.path.relative_to_project_root}:#{s.marked_line_number}"
|
89
|
+
end
|
90
|
+
c.body = s.captured_text
|
91
|
+
value = block.call(c).rstrip
|
92
|
+
if c.once? || value != s.captured_text
|
93
|
+
s.replace_captured_text(value + "\n", :once => c.once?)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param c [EmbedContext]
|
98
|
+
# @param s [FileScanner]
|
99
|
+
# @return [Boolean] whether or not the scanner updated its file
|
100
|
+
def expand_body(c, s, &block)
|
101
|
+
lang = Cog.active_language
|
102
|
+
value = block.call(c).rstrip
|
103
|
+
snip_line = lang.comment "#{c.to_directive} {"
|
104
|
+
unless c.once?
|
105
|
+
value = [snip_line, value, lang.comment("cog: }")].join("\n")
|
106
|
+
end
|
107
|
+
s.insert_at_mark(value + "\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
public
|
111
|
+
|
112
|
+
extend self
|
87
113
|
|
88
114
|
end
|
89
115
|
end
|
@@ -1,9 +1,14 @@
|
|
1
1
|
module Cog
|
2
2
|
module Embeds
|
3
3
|
|
4
|
+
# @api developer
|
4
5
|
# Helper for scanning files for embed expansions
|
5
6
|
class FileScanner
|
6
7
|
|
8
|
+
# @return [MatchData, nil] match data for the last matched pattern
|
9
|
+
attr_reader :match
|
10
|
+
|
11
|
+
# @api developer
|
7
12
|
def initialize(filename)
|
8
13
|
@filename = filename
|
9
14
|
@f = File.open filename, 'r'
|
@@ -11,13 +16,31 @@ module Cog
|
|
11
16
|
@mark = nil
|
12
17
|
@cap_begin_pos = nil
|
13
18
|
@cap_end_pos = nil
|
19
|
+
@match = nil # The last match object
|
14
20
|
end
|
15
|
-
|
21
|
+
|
22
|
+
# @api developer
|
16
23
|
# Closes the scanned file, if it is not already closed
|
17
24
|
def close
|
18
25
|
@f.close unless @f.closed?
|
19
26
|
end
|
20
27
|
|
28
|
+
# Opens the given file for scanning.
|
29
|
+
# @param filename [String] path to the file which will be scanned
|
30
|
+
# @param pattern [Regexp] a pattern to test for
|
31
|
+
# @option opt [Fixnum] :occurrence (0) 0 for the first, 1 for the second, and so on
|
32
|
+
# @yieldparam scanner [FileScanner] a file scanner
|
33
|
+
# @yieldreturn [Object]
|
34
|
+
# @return [Object] the return value of the block
|
35
|
+
def self.scan(filename, pattern, opt={}, &block)
|
36
|
+
s = new filename
|
37
|
+
if s.read_until pattern, opt[:occurrence] || 0
|
38
|
+
val = block.call s
|
39
|
+
end
|
40
|
+
s.close
|
41
|
+
val
|
42
|
+
end
|
43
|
+
|
21
44
|
# Remember this position. A later call to insert_at_mark will insert at this marked position
|
22
45
|
# @return [nil]
|
23
46
|
def mark!
|
@@ -36,20 +59,20 @@ module Cog
|
|
36
59
|
# Advances the file until the (n+1)th occurence of the given pattern is encountered, or the end of the file is reached
|
37
60
|
# @param pattern [Regexp] a pattern to test for
|
38
61
|
# @param n [Fixnum] 0 for the first, 1 for the second, and so on
|
39
|
-
# @return [
|
62
|
+
# @return [Boolean] whether or not a match was found. Use {#match} to retrieve the match data
|
40
63
|
def read_until(pattern, n=0)
|
41
64
|
i = 0
|
42
65
|
mark!
|
43
66
|
while (line = @f.readline) && i <= n
|
44
|
-
if
|
45
|
-
return
|
67
|
+
if @match = pattern.match(line)
|
68
|
+
return true if i == n
|
46
69
|
i += 1
|
47
70
|
end
|
48
71
|
mark!
|
49
72
|
end
|
50
73
|
rescue EOFError
|
51
74
|
unmark!
|
52
|
-
|
75
|
+
false
|
53
76
|
end
|
54
77
|
|
55
78
|
# Advances the file by one line
|
data/lib/cog/errors.rb
CHANGED
data/lib/cog/generator.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'cog/config'
|
2
|
+
require 'cog/embeds'
|
2
3
|
require 'cog/errors'
|
3
4
|
require 'cog/generator/file_methods'
|
4
5
|
require 'cog/generator/filters'
|
@@ -33,57 +34,90 @@ module Cog
|
|
33
34
|
|
34
35
|
# Stamp a template into a file or return it as a string
|
35
36
|
# @param template_path [String] path to template file relative one of the {Config#template_paths}
|
36
|
-
# @param destination [String] path to which the generated file should be written, relative to the {Config#project_source_path}
|
37
|
+
# @param destination [String] path to which the generated file should be written, relative to the {Config::ProjectMethods#project_source_path}
|
37
38
|
# @option opt [Boolean] :absolute_template_path (false) is the +template_path+ absolute?
|
38
39
|
# @option opt [Boolean] :absolute_destination (false) is the +destination+ absolute?
|
39
|
-
# @option opt [
|
40
|
+
# @option opt [Binding] :binding (nil) an optional binding to use while evaluating the template
|
41
|
+
# @option opt [String, Array<String>] :filter (nil) name(s) of {Filters}
|
40
42
|
# @option opt [Boolean] :quiet (false) suppress writing to STDOUT?
|
41
43
|
# @return [nil or String] if +destination+ is not provided, the stamped template is returned as a string
|
42
44
|
def stamp(template_path, destination=nil, opt={})
|
43
45
|
# Ignore destination if its a hash, its meant to be opt
|
44
46
|
opt, destination = destination, nil if destination.is_a? Hash
|
45
47
|
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
r = Config.instance.activate_language :ext => File.extname(template_path.to_s) do
|
50
|
-
t.result(b)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Run r through filters
|
54
|
-
f = opt[:filter]
|
55
|
-
f = [f].compact unless f.is_a?(Array)
|
56
|
-
f.each {|name| r = call_filter name, r }
|
57
|
-
|
48
|
+
# Render and filter
|
49
|
+
r = find_and_render template_path, opt
|
50
|
+
r = filter_through r, opt[:filter]
|
58
51
|
return r if destination.nil?
|
59
52
|
|
60
53
|
# Place it in a file
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
FileUtils.mv scratch, dest
|
70
|
-
STDOUT.write "#{updated ? :Updated : :Created} #{dest.relative_to_project_root}\n".color(updated ? :white : :green) unless opt[:quiet]
|
54
|
+
write_scratch_file(destination, r, opt[:absolute_destination]) do |path, scratch|
|
55
|
+
if files_are_same? path, scratch
|
56
|
+
FileUtils.rm scratch
|
57
|
+
else
|
58
|
+
updated = File.exists? path
|
59
|
+
FileUtils.mv scratch, path
|
60
|
+
STDOUT.write "#{updated ? :Updated : :Created} #{path.relative_to_project_root}\n".color(updated ? :white : :green) unless opt[:quiet]
|
61
|
+
end
|
71
62
|
end
|
72
63
|
nil
|
73
64
|
end
|
74
65
|
|
75
|
-
# Provide a value for
|
76
|
-
# @param
|
77
|
-
# @yieldparam context [Embeds::
|
66
|
+
# Provide a value for embeds with the given hook
|
67
|
+
# @param hook [String] hook name used in the embed statements
|
68
|
+
# @yieldparam context [Embeds::EmbedContext] provides information about the environment in which the embed statement was found
|
78
69
|
# @yieldreturn The value which will be used to expand the embed (or replace the embedded content)
|
79
70
|
# @return [nil]
|
80
|
-
def embed(
|
81
|
-
|
82
|
-
|
83
|
-
|
71
|
+
def embed(hook, &block)
|
72
|
+
eaten = 0 # keep track of eaten statements so that the index can be adjusted
|
73
|
+
Embeds.find(hook) do |c|
|
74
|
+
c.eaten = eaten
|
75
|
+
if Embeds.update c, &block
|
76
|
+
eaten += 1 if c.once?
|
77
|
+
STDOUT.write "Updated #{c.path.relative_to_project_root} - #{(c.index + 1).ordinalize} occurrence of embed '#{c.hook}'\n".color :white
|
84
78
|
end
|
85
79
|
end
|
86
80
|
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# @param template_path [String] path to template file relative one of the {Config#template_paths}
|
85
|
+
# @option opt [Boolean] :absolute_template_path (false) is the +template_path+ absolute?
|
86
|
+
# @option opt [Binding] :binding (nil) an optional binding to use while evaluating the template
|
87
|
+
# @return [String] result of rendering the template
|
88
|
+
def find_and_render(template_path, opt={})
|
89
|
+
t = get_template template_path, :absolute => opt[:absolute_template_path]
|
90
|
+
b = opt[:binding] || binding
|
91
|
+
Cog.activate_language :ext => File.extname(template_path.to_s) do
|
92
|
+
t.result(b)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param text [String] text to run through filters
|
97
|
+
# @param f [String, Array<String>] name(s) of {Filters}
|
98
|
+
def filter_through(text, f)
|
99
|
+
f = [f].compact unless f.is_a?(Array)
|
100
|
+
f.each {|name| text = call_filter name, text }
|
101
|
+
text
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param original [String] path to the original file
|
105
|
+
# @param text [String] text to write into the scratch file
|
106
|
+
# @param absolute [Boolean] is the path absolute or relative to the project root?
|
107
|
+
# @yieldparam original [String] absolute path to original file
|
108
|
+
# @yieldparam scratch [String] path to the scratch file
|
109
|
+
# @return [nil]
|
110
|
+
def write_scratch_file(original, text, absolute=false, &block)
|
111
|
+
path = absolute ? original : File.join(Cog.project_source_path, original)
|
112
|
+
FileUtils.mkpath File.dirname(path) unless File.exists? path
|
113
|
+
scratch = "#{path}.scratch"
|
114
|
+
File.open(scratch, 'w') {|file| file.write text}
|
115
|
+
block.call path, scratch
|
116
|
+
end
|
87
117
|
|
118
|
+
public
|
119
|
+
|
120
|
+
extend self
|
121
|
+
|
88
122
|
end
|
89
123
|
end
|
@@ -9,7 +9,7 @@ module Cog
|
|
9
9
|
# @param text [String] some text which should be rendered as a comment
|
10
10
|
# @return [String] a comment appropriate for the current language context
|
11
11
|
def comment(text)
|
12
|
-
|
12
|
+
Cog.active_language.comment text
|
13
13
|
end
|
14
14
|
|
15
15
|
# @api developer
|
@@ -17,7 +17,7 @@ module Cog
|
|
17
17
|
# @return [String] the scope begin statement
|
18
18
|
def scope_begin(scope)
|
19
19
|
gcontext[:scopes] << scope
|
20
|
-
|
20
|
+
Cog.active_language.method("#{scope.type}_begin").call(scope.name)
|
21
21
|
end
|
22
22
|
|
23
23
|
# End the scope, popping it off the scope stack
|
@@ -29,7 +29,7 @@ module Cog
|
|
29
29
|
return nil
|
30
30
|
end
|
31
31
|
scope = gcontext[:scopes].pop
|
32
|
-
|
32
|
+
Cog.active_language.method("#{scope.type}_end").call(scope.name)
|
33
33
|
end
|
34
34
|
|
35
35
|
# End all scope currently on the stack
|
@@ -45,7 +45,7 @@ module Cog
|
|
45
45
|
# @param name [String] name of the scope to use
|
46
46
|
# @return [String] a using statement for the named scope
|
47
47
|
def use_named_scope(name)
|
48
|
-
|
48
|
+
Cog.active_language.use_named_scope(name)
|
49
49
|
end
|
50
50
|
|
51
51
|
# @param name [String] name of the scope
|
data/lib/cog/helpers/string.rb
CHANGED
@@ -2,10 +2,10 @@ require 'cog/config'
|
|
2
2
|
|
3
3
|
class String
|
4
4
|
|
5
|
-
# @return [String] strips {Cog::Config#project_root} from the beginning of this string
|
5
|
+
# @return [String] strips {Cog::Config::ProjectMethods#project_root} from the beginning of this string
|
6
6
|
def relative_to_project_root
|
7
|
-
return dup unless Cog
|
8
|
-
relative_to Cog
|
7
|
+
return dup unless Cog.project?
|
8
|
+
relative_to Cog.project_root
|
9
9
|
end
|
10
10
|
|
11
11
|
# @param prefix [String] path prefix to strip from the beginning of this string
|
data/lib/cog/languages.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'cog/errors'
|
2
2
|
require 'cog/languages/language'
|
3
|
+
require 'cog/languages/mixins'
|
3
4
|
|
4
5
|
module Cog
|
5
6
|
|
@@ -15,6 +16,7 @@ module Cog
|
|
15
16
|
:hpp => 'c++',
|
16
17
|
:java => 'java',
|
17
18
|
:js => 'javascript',
|
19
|
+
:pri => 'qt',
|
18
20
|
:pro => 'qt',
|
19
21
|
:py => 'python',
|
20
22
|
:rb => 'ruby',
|