cog 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cog/config/language_config.rb +5 -1
- data/lib/cog/embed_context.rb +7 -3
- data/lib/cog/embeds.rb +71 -23
- data/lib/cog/errors.rb +38 -27
- data/lib/cog/generator.rb +3 -2
- data/lib/cog/helpers/file_scanner.rb +5 -3
- data/lib/cog/plugin.rb +1 -1
- data/lib/cog/version.rb +1 -1
- metadata +4 -4
@@ -10,9 +10,10 @@ module Cog
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Activate a given language within the scope of the provided block.
|
13
|
-
# Either provide <tt>key</tt
|
13
|
+
# Either provide <tt>key</tt>, <tt>:ext</tt>, or <tt>:filename</tt> but not more than one. If the extension does not match any of the supported languages, the {#active_language} will not change, but the block will still be called.
|
14
14
|
# @param key [:String] the lanuage identifier. Type <tt>cog language list</tt> to see the possible values
|
15
15
|
# @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
|
16
|
+
# @option opt [:String] :filename (nil) a filename or path to file which will map to a language identifier
|
16
17
|
# @yield within this block the {#active_language} will be set to the desired value
|
17
18
|
# @return [Object] the value returned by the block
|
18
19
|
def activate_language(key, opt={}, &block)
|
@@ -22,6 +23,9 @@ module Cog
|
|
22
23
|
ext = opt[:ext].to_s.downcase
|
23
24
|
ext = ext.slice(1..-1) if ext.start_with?('.')
|
24
25
|
@language_extension_map[ext] unless ext.empty?
|
26
|
+
elsif opt[:filename]
|
27
|
+
ext = File.extname(opt[:filename]).slice(1..-1)
|
28
|
+
@language_extension_map[ext] unless ext.nil? || ext.empty?
|
25
29
|
else
|
26
30
|
key
|
27
31
|
end
|
data/lib/cog/embed_context.rb
CHANGED
@@ -38,6 +38,7 @@ module Cog
|
|
38
38
|
@path = path.to_s
|
39
39
|
@count = count
|
40
40
|
@eaten = 0
|
41
|
+
@index = 0
|
41
42
|
end
|
42
43
|
|
43
44
|
# @return [Array<String>] arguments provided with the embed statement
|
@@ -74,6 +75,9 @@ module Cog
|
|
74
75
|
@once
|
75
76
|
end
|
76
77
|
|
78
|
+
# @api developer
|
79
|
+
attr_accessor :keep_body
|
80
|
+
|
77
81
|
# @api developer
|
78
82
|
# @param value [String, nil] set the body to this value
|
79
83
|
def body=(value)
|
@@ -115,11 +119,11 @@ module Cog
|
|
115
119
|
def actual_index
|
116
120
|
@index - @eaten
|
117
121
|
end
|
118
|
-
|
122
|
+
|
119
123
|
# @api developer
|
120
124
|
# @return [String]
|
121
|
-
def
|
122
|
-
x = "
|
125
|
+
def to_statement(type='cog')
|
126
|
+
x = "#{type}: #{hook}"
|
123
127
|
x += "(#{args.join ' '})" if args
|
124
128
|
x += " once" if once?
|
125
129
|
x
|
data/lib/cog/embeds.rb
CHANGED
@@ -8,14 +8,54 @@ module Cog
|
|
8
8
|
def gather_from_project
|
9
9
|
@embeds ||= {}
|
10
10
|
Cog.supported_project_files.each do |filename|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
gather_from_file filename, :hash => @embeds, :type => 'cog'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Copy keep bodies from the original file to the scratch file
|
16
|
+
# @param original [String] file in which to search for keep statements. If the original does not exist, then scratch will serve as the original (we do this so that the keeps will get expanded in any case)
|
17
|
+
# @param scratch [String] file to which keep bodies will be copied
|
18
|
+
# @return [nil]
|
19
|
+
def copy_keeps(original, scratch)
|
20
|
+
keeps = {}
|
21
|
+
Cog.activate_language(:filename => original) do
|
22
|
+
original = scratch unless File.exists? original
|
23
|
+
gather_from_file(original, :type => 'keep').each_pair do |hook, count|
|
24
|
+
c = keeps[hook] = EmbedContext.new(hook, scratch, count[original])
|
25
|
+
raise Errors::DuplicateKeep.new :hook => hook if c.count > 1
|
26
|
+
Helpers::FileScanner.scan(original, statement('keep', hook)) do |s|
|
27
|
+
c.keep_body = if s.match[4] == '{'
|
28
|
+
s.capture_until end_statement('keep')
|
29
|
+
s.captured_text
|
30
|
+
else
|
31
|
+
''
|
32
|
+
end
|
33
|
+
end
|
17
34
|
end
|
35
|
+
keeps.each_pair do |hook, c|
|
36
|
+
result = update c, :type => 'keep' do |c|
|
37
|
+
c.keep_body
|
38
|
+
end
|
39
|
+
raise Errors::UnrecognizedKeepHook.new :hook => hook, :filename => original if result.nil?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param filename [String] file from which to gather statements
|
45
|
+
# @option opt [Hash] :hash ({}) object in which to gather the mapping
|
46
|
+
# @option opt [String] :type ('cog') one of <tt>'cog'</tt> or <tt>'keep'</tt>
|
47
|
+
# @return [Hash] mapping from hooks to <tt>{ 'filename' => count }</tt> hashes
|
48
|
+
def gather_from_file(filename, opt={})
|
49
|
+
bucket = opt[:hash] || {}
|
50
|
+
type = opt[:type] || 'cog'
|
51
|
+
lang = Cog.language_for filename
|
52
|
+
File.read(filename).scan(statement type, '[-A-Za-z0-9_.]+', :lang => lang) do |m|
|
53
|
+
hook = m[0]
|
54
|
+
bucket[hook] ||= {}
|
55
|
+
bucket[hook][filename] ||= 0
|
56
|
+
bucket[hook][filename] += 1
|
18
57
|
end
|
58
|
+
bucket
|
19
59
|
end
|
20
60
|
|
21
61
|
# @param hook [String] embed hook for which to find directive occurrences
|
@@ -36,18 +76,20 @@ module Cog
|
|
36
76
|
end
|
37
77
|
|
38
78
|
# @param c [EmbedContext] describes the context in which the embed statement was found
|
79
|
+
# @option opt [String] :type ('cog') one of <tt>'cog'</tt> or <tt>'keep'</tt>
|
39
80
|
# @yieldparam context [EmbedContext] describes the context in which the embed statement was found
|
40
81
|
# @yieldreturn [String] the value to substitute into the embed expansion
|
41
|
-
# @return [
|
42
|
-
def update(c, &block)
|
43
|
-
|
82
|
+
# @return [Boolean,nil] +true+ if the statement was expanded or updated, +false+ if the statement was found, but not changed, +nil+ if it could not be found.
|
83
|
+
def update(c, opt={}, &block)
|
84
|
+
type = opt[:type] || 'cog'
|
85
|
+
Helpers::FileScanner.scan(c.path, statement(type, c.hook), :occurrence => c.actual_index) do |s|
|
44
86
|
c.lineno = s.marked_line_number
|
45
87
|
c.args = s.match[2].split if s.match[2]
|
46
88
|
c.once = !s.match[3].nil?
|
47
89
|
if s.match[4] == '{'
|
48
|
-
update_body c, s, &block
|
90
|
+
update_body c, s, opt, &block
|
49
91
|
else
|
50
|
-
expand_body c, s, &block
|
92
|
+
expand_body c, s, opt, &block
|
51
93
|
end
|
52
94
|
end
|
53
95
|
end
|
@@ -60,44 +102,50 @@ module Cog
|
|
60
102
|
# * 3 - once
|
61
103
|
# * 4 - expansion begin (curly <tt>{</tt>)
|
62
104
|
# @return [Regexp] pattern to match the beginning of a cog embed statement
|
63
|
-
def statement(hook, opt={})
|
105
|
+
def statement(type, hook, opt={})
|
64
106
|
lang = opt[:lang] || Cog.active_language
|
65
|
-
lang.comment_pattern("
|
107
|
+
lang.comment_pattern("#{type}\\s*:\\s*(#{hook})\\s*(?:\\(\\s*(.+?)\\s*\\))?(\\s*once\\s*)?(?:\\s*([{]))?")
|
66
108
|
end
|
67
109
|
|
68
110
|
# @return [Regexp] pattern to match the end of a cog embed statement
|
69
|
-
def end_statement
|
70
|
-
Cog.active_language.comment_pattern("
|
111
|
+
def end_statement(type = 'cog')
|
112
|
+
Cog.active_language.comment_pattern("#{type}\\s*:\\s*[}]")
|
71
113
|
end
|
72
114
|
|
73
115
|
# @return [Regexp] pattern to match an line that looks like a cog statement, but is not the end statement
|
74
|
-
def anything_but_end
|
75
|
-
Cog.active_language.comment_pattern("
|
116
|
+
def anything_but_end(type = 'cog')
|
117
|
+
Cog.active_language.comment_pattern("#{type}\\s*:\\s*(?!\\s*[}]).*$")
|
76
118
|
end
|
77
119
|
|
78
120
|
# @param c [EmbedContext]
|
79
121
|
# @param s [FileScanner]
|
122
|
+
# @option opt [String] :type ('cog') one of <tt>'cog'</tt> or <tt>'keep'</tt>
|
80
123
|
# @return [Boolean] whether or not the scanner updated its file
|
81
|
-
def update_body(c, s, &block)
|
82
|
-
|
83
|
-
|
124
|
+
def update_body(c, s, opt={}, &block)
|
125
|
+
type = opt[:type] || 'cog'
|
126
|
+
unless s.capture_until end_statement(type), :but_not => anything_but_end(type)
|
127
|
+
raise Errors::SnippetExpansionUnterminated.new :filename => c.path.relative_to_project_root, :line => s.marked_line_number
|
84
128
|
end
|
85
129
|
c.body = s.captured_text
|
86
130
|
value = block.call(c).rstrip
|
87
131
|
if c.once? || value != s.captured_text
|
88
132
|
s.replace_captured_text(value + "\n", :once => c.once?)
|
133
|
+
else
|
134
|
+
false
|
89
135
|
end
|
90
136
|
end
|
91
137
|
|
92
138
|
# @param c [EmbedContext]
|
93
139
|
# @param s [FileScanner]
|
140
|
+
# @option opt [String] :type ('cog') one of <tt>'cog'</tt> or <tt>'keep'</tt>
|
94
141
|
# @return [Boolean] whether or not the scanner updated its file
|
95
|
-
def expand_body(c, s, &block)
|
142
|
+
def expand_body(c, s, opt={}, &block)
|
143
|
+
type = opt[:type] || 'cog'
|
96
144
|
lang = Cog.active_language
|
97
145
|
value = block.call(c).rstrip
|
98
|
-
snip_line = lang.comment "#{c.
|
146
|
+
snip_line = lang.comment "#{c.to_statement type} {"
|
99
147
|
unless c.once?
|
100
|
-
value = [snip_line, value, lang.comment("
|
148
|
+
value = [snip_line, value, lang.comment("#{type}: }")].join("\n")
|
101
149
|
end
|
102
150
|
s.insert_at_mark(value + "\n")
|
103
151
|
end
|
data/lib/cog/errors.rb
CHANGED
@@ -4,58 +4,69 @@ module Cog
|
|
4
4
|
|
5
5
|
# Root type for all cog errors
|
6
6
|
class CogError < Exception
|
7
|
+
def initialize(details={})
|
8
|
+
@details = if details.is_a? Hash
|
9
|
+
details.to_a.collect do |key, value|
|
10
|
+
"#{key} => #{value.inspect}"
|
11
|
+
end.sort
|
12
|
+
else
|
13
|
+
[details]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
w = custom_message || self.class.name.underscore.split('/').last.gsub('_', ' ')
|
19
|
+
w += " (#{@details.join ', '})" unless @details.empty?
|
20
|
+
w
|
21
|
+
end
|
7
22
|
end
|
8
23
|
|
9
24
|
# Define a +cog+ error class
|
10
25
|
# @api developer
|
11
|
-
#
|
12
26
|
# @param class_name [String] name of the error class
|
13
|
-
|
14
|
-
# @yield +self+ will be set to an instance of the error class and <tt>@msg</tt> will contain
|
15
|
-
def self.define_error(class_name, arg, &block)
|
27
|
+
def self.define_error(class_name, &block)
|
16
28
|
cls = Class.new CogError
|
17
29
|
Errors.instance_eval { const_set class_name, cls }
|
18
30
|
cls.instance_eval do
|
19
|
-
define_method
|
20
|
-
|
21
|
-
msg = if block.nil?
|
22
|
-
class_name.to_s.underscore.gsub '_', ' '
|
23
|
-
else
|
24
|
-
instance_eval &block
|
25
|
-
end
|
26
|
-
"#{msg} (#{arg} => #{@value})"
|
31
|
+
define_method :custom_message do
|
32
|
+
block.call if block
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
31
|
-
define_error :ActionRequiresProjectGeneratorPath
|
32
|
-
define_error :ActionRequiresProjectTemplatePath
|
33
|
-
define_error :ActionRequiresProjectPluginPath
|
37
|
+
define_error :ActionRequiresProjectGeneratorPath
|
38
|
+
define_error :ActionRequiresProjectTemplatePath
|
39
|
+
define_error :ActionRequiresProjectPluginPath
|
34
40
|
|
35
|
-
define_error :DuplicateGenerator
|
36
|
-
define_error :DuplicatePlugin
|
41
|
+
define_error :DuplicateGenerator
|
42
|
+
define_error :DuplicatePlugin
|
43
|
+
define_error :DuplicateKeep
|
37
44
|
|
38
|
-
define_error :InvalidPluginConfiguration
|
45
|
+
define_error :InvalidPluginConfiguration do
|
39
46
|
"invalid directory structure for a cog plugin"
|
40
47
|
end
|
41
48
|
|
42
|
-
define_error :
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
define_error :
|
49
|
+
define_error :UnrecognizedKeepHook do
|
50
|
+
"looks like that hook is longer being generated"
|
51
|
+
end
|
52
|
+
|
53
|
+
define_error :NoSuchFilter
|
54
|
+
define_error :NoSuchGenerator
|
55
|
+
define_error :NoSuchLanguage
|
56
|
+
define_error :NoSuchTemplate
|
57
|
+
define_error :NoSuchPlugin
|
47
58
|
|
48
|
-
define_error :PluginPathIsNotADirectory
|
59
|
+
define_error :PluginPathIsNotADirectory
|
49
60
|
|
50
|
-
define_error :ScopeStackUnderflow
|
61
|
+
define_error :ScopeStackUnderflow do
|
51
62
|
"scope stack underflow: this can happen if you have too many *_end calls in a template"
|
52
63
|
end
|
53
64
|
|
54
|
-
define_error :SnippetExpansionUnterminated
|
65
|
+
define_error :SnippetExpansionUnterminated do
|
55
66
|
"a embed expansion in the given file is missing the 'cog: }' terminator"
|
56
67
|
end
|
57
68
|
|
58
|
-
define_error :PluginMissingDefinition
|
69
|
+
define_error :PluginMissingDefinition do
|
59
70
|
"the plugin was not fully defined"
|
60
71
|
end
|
61
72
|
end
|
data/lib/cog/generator.rb
CHANGED
@@ -47,10 +47,11 @@ module Cog
|
|
47
47
|
|
48
48
|
# Place it in a file
|
49
49
|
write_scratch_file(destination, r, opt[:absolute_destination]) do |path, scratch|
|
50
|
-
|
50
|
+
updated = File.exists? path
|
51
|
+
Embeds.copy_keeps(path, scratch)
|
52
|
+
if files_are_same?(path, scratch) || (opt[:once] && updated)
|
51
53
|
FileUtils.rm scratch
|
52
54
|
else
|
53
|
-
updated = File.exists? path
|
54
55
|
FileUtils.mv scratch, path
|
55
56
|
STDOUT.write "#{updated ? :Updated : :Created} #{path.relative_to_project_root}\n".color(updated ? :white : :green) unless opt[:quiet]
|
56
57
|
end
|
@@ -31,11 +31,13 @@ module Cog
|
|
31
31
|
# @option opt [Fixnum] :occurrence (0) 0 for the first, 1 for the second, and so on
|
32
32
|
# @yieldparam scanner [FileScanner] a file scanner
|
33
33
|
# @yieldreturn [Object]
|
34
|
-
# @return [Object] the return value of the block
|
34
|
+
# @return [Object,nil] the return value of the block, or +nil+ if the pattern was not found
|
35
35
|
def self.scan(filename, pattern, opt={}, &block)
|
36
36
|
s = new filename
|
37
|
-
if s.read_until pattern, opt[:occurrence] || 0
|
38
|
-
|
37
|
+
val = if s.read_until pattern, opt[:occurrence] || 0
|
38
|
+
block.call s
|
39
|
+
else
|
40
|
+
nil
|
39
41
|
end
|
40
42
|
s.close
|
41
43
|
val
|
data/lib/cog/plugin.rb
CHANGED
@@ -18,7 +18,7 @@ module Cog
|
|
18
18
|
# @param cogfile_path [String] path to the plugin Cogfile
|
19
19
|
def initialize(cogfile_path)
|
20
20
|
unless File.exists?(cogfile_path)
|
21
|
-
raise Errors::InvalidPluginConfiguration.new
|
21
|
+
raise Errors::InvalidPluginConfiguration.new :cogfile => cogfile_path
|
22
22
|
end
|
23
23
|
@cogfile_path = File.expand_path cogfile_path
|
24
24
|
@path = File.dirname @cogfile_path
|
data/lib/cog/version.rb
CHANGED
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:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 1
|
10
|
+
version: 0.3.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:
|
18
|
+
date: 2013-01-05 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: gli
|