cog 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/BuiltIn.cogfile +96 -0
- data/bin/cog +30 -39
- data/built_in/generators/sort.rb +3 -0
- data/built_in/plugins/basic/Cogfile +6 -0
- data/built_in/plugins/basic/templates/basic/generator.rb.erb +1 -0
- data/built_in/templates/cog/Cogfile.erb +40 -0
- data/built_in/templates/cog/plugin/generator.rb.erb.erb +3 -0
- data/{templates/cog/custom_tool/tool.rb.erb → built_in/templates/cog/plugin/plugin.rb.erb} +4 -11
- data/{templates → built_in/templates}/warning.erb +0 -0
- data/lib/cog.rb +25 -11
- data/lib/cog/config.rb +112 -49
- data/lib/cog/config/{language_methods.rb → language_config.rb} +23 -27
- data/lib/cog/config/plugin_config.rb +31 -0
- data/lib/cog/config/project_config.rb +48 -0
- data/lib/cog/controllers.rb +6 -8
- data/lib/cog/controllers/generator_controller.rb +30 -21
- data/lib/cog/controllers/plugin_controller.rb +43 -0
- data/lib/cog/controllers/template_controller.rb +12 -33
- data/lib/cog/dsl.rb +9 -0
- data/lib/cog/dsl/cogfile.rb +145 -0
- data/lib/cog/dsl/language_dsl.rb +142 -0
- data/lib/cog/embed_context.rb +0 -2
- data/lib/cog/embeds.rb +2 -7
- data/lib/cog/errors.rb +11 -24
- data/lib/cog/generator.rb +10 -15
- data/lib/cog/generator/file_methods.rb +2 -2
- data/lib/cog/generator/filters.rb +10 -14
- data/lib/cog/generator/language_methods.rb +2 -3
- data/lib/cog/generator_sandbox.rb +32 -0
- data/lib/cog/helpers.rb +10 -2
- data/lib/cog/helpers/cascading_set.rb +104 -0
- data/lib/cog/{embeds → helpers}/file_scanner.rb +1 -1
- data/lib/cog/language.rb +140 -0
- data/lib/cog/native_extensions.rb +2 -0
- data/lib/cog/native_extensions/array.rb +19 -0
- data/lib/cog/native_extensions/string.rb +54 -0
- data/lib/cog/plugin.rb +35 -0
- data/lib/cog/spec_helpers.rb +36 -14
- data/lib/cog/spec_helpers/matchers.rb +14 -4
- data/lib/cog/spec_helpers/matchers/match_maker.rb +1 -1
- data/lib/cog/spec_helpers/runner.rb +3 -9
- data/lib/cog/version.rb +1 -1
- metadata +27 -44
- data/Default.cogfile +0 -25
- data/lib/cog/built_in_tools.rb +0 -9
- data/lib/cog/built_in_tools/basic.rb +0 -10
- data/lib/cog/built_in_tools/basic/cog_tool.rb +0 -11
- data/lib/cog/config/cogfile.rb +0 -117
- data/lib/cog/config/lang_info.rb +0 -40
- data/lib/cog/config/project_methods.rb +0 -35
- data/lib/cog/config/tool_methods.rb +0 -94
- data/lib/cog/controllers/tool_controller.rb +0 -50
- data/lib/cog/helpers/cascading_template_set.rb +0 -75
- data/lib/cog/helpers/string.rb +0 -26
- data/lib/cog/languages.rb +0 -52
- data/lib/cog/languages/c_language.rb +0 -29
- data/lib/cog/languages/c_plus_plus_language.rb +0 -28
- data/lib/cog/languages/c_sharp_language.rb +0 -24
- data/lib/cog/languages/java_language.rb +0 -24
- data/lib/cog/languages/java_script_language.rb +0 -24
- data/lib/cog/languages/language.rb +0 -73
- data/lib/cog/languages/mixins.rb +0 -10
- data/lib/cog/languages/mixins/c_style_comments.rb +0 -23
- data/lib/cog/languages/mixins/hash_comments.rb +0 -19
- data/lib/cog/languages/python_language.rb +0 -20
- data/lib/cog/languages/qt_project_language.rb +0 -16
- data/lib/cog/languages/ruby_language.rb +0 -28
- data/lib/cog/tool.rb +0 -61
- data/lib/cog/tool/dsl.rb +0 -26
- data/templates/basic/generator.rb.erb +0 -4
- data/templates/cog/custom_tool/Gemfile.erb +0 -8
- data/templates/cog/custom_tool/LICENSE.erb +0 -18
- data/templates/cog/custom_tool/README.markdown.erb +0 -18
- data/templates/cog/custom_tool/Rakefile.erb +0 -15
- data/templates/cog/custom_tool/cog_tool.rb.erb +0 -17
- data/templates/cog/custom_tool/generator.rb.erb.erb +0 -9
- data/templates/cog/custom_tool/template.txt.erb.erb +0 -1
- data/templates/cog/custom_tool/tool.gemspec.erb +0 -17
- data/templates/cog/custom_tool/version.rb.erb +0 -5
data/lib/cog/language.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
module Cog
|
2
|
+
|
3
|
+
# Describes a language support by Cog
|
4
|
+
class Language
|
5
|
+
|
6
|
+
# @return [Array<String>] list of file extensions
|
7
|
+
attr_reader :extensions
|
8
|
+
|
9
|
+
# @return [String] unique lower case identifier
|
10
|
+
attr_reader :key
|
11
|
+
|
12
|
+
# @return [String] readable name for the language
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
# @return [String] the style of comments used by this language
|
16
|
+
attr_reader :comment_style
|
17
|
+
|
18
|
+
# @return [String] the style of include guards used by this language
|
19
|
+
attr_reader :include_guard_style
|
20
|
+
|
21
|
+
# @api developer
|
22
|
+
# Initialize with default values
|
23
|
+
# @param key [String] unique case-insensitive identifier
|
24
|
+
def initialize(key = :text)
|
25
|
+
@key = key.to_s.downcase
|
26
|
+
@name = key.to_s
|
27
|
+
@comment_pattern = '^\s*(%s)\s*$'
|
28
|
+
@comment_prefix = nil
|
29
|
+
@multiline_comment_prefix = nil
|
30
|
+
@multiline_comment_postfix = nil
|
31
|
+
@extensions = []
|
32
|
+
identibitch = lambda {|name| ''}
|
33
|
+
@use_named_scope_block = identibitch
|
34
|
+
@named_scope_begin_block = identibitch
|
35
|
+
@named_scope_end_block = identibitch
|
36
|
+
@include_guard_begin_block = identibitch
|
37
|
+
@include_guard_end_block = identibitch
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param nested_pattern [String] regular expression pattern (as a string) to embed in the regular expression which matches one line comments in this language
|
41
|
+
# @return [Regexp] pattern for matching one line comments in this language
|
42
|
+
def comment_pattern(nested_pattern)
|
43
|
+
Regexp.new(@comment_pattern % nested_pattern)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param text [String] some text which should be rendered as a comment
|
47
|
+
# @return [String] a comment appropriate for this language
|
48
|
+
def comment(text)
|
49
|
+
if text =~ /\n/
|
50
|
+
multi_line_comment text
|
51
|
+
else
|
52
|
+
one_line_comment text
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @api developer
|
57
|
+
def one_line_comment(text)
|
58
|
+
if @comment_prefix
|
59
|
+
"#{@comment_prefix} #{text}"
|
60
|
+
elsif @multiline_comment_prefix
|
61
|
+
"#{@multiline_comment_prefix} #{text} #{@multiline_comment_postfix}"
|
62
|
+
else
|
63
|
+
text
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @api developer
|
68
|
+
def multi_line_comment(text)
|
69
|
+
if @multiline_comment_prefix
|
70
|
+
"#{@multiline_comment_prefix}\n#{text}\n#{@multiline_comment_postfix}"
|
71
|
+
elsif @comment_prefix
|
72
|
+
text.split("\n").collect do |line|
|
73
|
+
"#{@comment_prefix} #{line}"
|
74
|
+
end.join("\n")
|
75
|
+
else
|
76
|
+
text
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# @api developer
|
81
|
+
# Called after all Cogfiles have been processed
|
82
|
+
# @param other [Language] language to borrow notation from
|
83
|
+
def apply_comment_style(other)
|
84
|
+
@comment_prefix = other.instance_eval {@comment_prefix}
|
85
|
+
@multiline_comment_prefix = other.instance_eval {@multiline_comment_prefix}
|
86
|
+
@multiline_comment_postfix = other.instance_eval {@multiline_comment_postfix}
|
87
|
+
@comment_pattern = other.instance_eval {@comment_pattern}
|
88
|
+
end
|
89
|
+
|
90
|
+
# @api developer
|
91
|
+
# Called after all Cogfiles have been processed
|
92
|
+
# @param other [Language] language to borrow notation from
|
93
|
+
def apply_include_guard_style(other)
|
94
|
+
@include_guard_begin_block = other.instance_eval {@include_guard_begin_block}
|
95
|
+
@include_guard_end_block = other.instance_eval {@include_guard_end_block}
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param w [FixNum] width of the first column
|
99
|
+
# @return [String] one line summary in two columns
|
100
|
+
def to_s(w=nil)
|
101
|
+
w ||= @name.length
|
102
|
+
"#{@name.ljust w} -> #{@extensions.collect {|x| x.to_s}.sort.join ', '}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Sort by name
|
106
|
+
def <=>(other)
|
107
|
+
@name <=> other.name
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param name [String] name of the scope to use
|
111
|
+
# @return [String] a using statement for the named scope
|
112
|
+
def use_named_scope(name)
|
113
|
+
@use_named_scope_block.call name
|
114
|
+
end
|
115
|
+
|
116
|
+
# @param name [String] name of the scope
|
117
|
+
# @return [String] begin a named scope
|
118
|
+
def named_scope_begin(name)
|
119
|
+
@named_scope_begin_block.call name
|
120
|
+
end
|
121
|
+
|
122
|
+
# @param name [String] name of the scope
|
123
|
+
# @return [String] end the given named scope
|
124
|
+
def named_scope_end(name)
|
125
|
+
@named_scope_end_block.call name
|
126
|
+
end
|
127
|
+
|
128
|
+
# @param name [String] name of the module to protect
|
129
|
+
# @return [String] an include guard statement
|
130
|
+
def include_guard_begin(name)
|
131
|
+
@include_guard_begin_block.call name
|
132
|
+
end
|
133
|
+
|
134
|
+
# @param name [String] name of the module to protect
|
135
|
+
# @return [String] an include guard end statement
|
136
|
+
def include_guard_end(name)
|
137
|
+
@include_guard_end_block.call name
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# @api developer
|
2
|
+
class Array
|
3
|
+
|
4
|
+
# Iterate through each path in this array
|
5
|
+
# @yieldparam source [String] readable label for the source. In the case of plugins, the plugin name
|
6
|
+
# @yieldparam source_type [Symbol] one of :built_in, :user, :plugin, or :project
|
7
|
+
# @yieldparam path [String] a path to a cog resource, such as a template or generator
|
8
|
+
# @return [Object] the return value of the block
|
9
|
+
def each_with_cog_source(&block)
|
10
|
+
each do |path|
|
11
|
+
source, source_type = if plugin = path.relative_to_which_plugin?
|
12
|
+
[plugin.name, :plugin]
|
13
|
+
else
|
14
|
+
path.cog_source_and_type
|
15
|
+
end
|
16
|
+
block.call source, source_type, path
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# @api developer
|
2
|
+
class String
|
3
|
+
|
4
|
+
# @return [String] strips {Cog::Config::ProjectConfig#project_root} from the beginning of this string
|
5
|
+
def relative_to_project_root
|
6
|
+
if Cog.show_fullpaths?
|
7
|
+
File.expand_path self
|
8
|
+
else
|
9
|
+
Cog.project? ? relative_to(Cog.project_root) : dup
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param prefix [String] path prefix to strip from the beginning of this string
|
14
|
+
# @return [String] this string as a file system path relative to the +prefix+
|
15
|
+
def relative_to(prefix)
|
16
|
+
if Cog.show_fullpaths?
|
17
|
+
File.expand_path self
|
18
|
+
else
|
19
|
+
prefix && start_with?(prefix.to_s) ? slice(prefix.to_s.length+1..-1) : dup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Cog::Plugin,nil] if this string can be interpretted as a path relative to one of the registered cog plugins, return that plugin, otherwise return +nil+
|
24
|
+
def relative_to_which_plugin?
|
25
|
+
Cog.plugins.each do |plugin|
|
26
|
+
return plugin if start_with?(plugin.path)
|
27
|
+
end
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param ext [String] file extension to remove from the end of this string
|
32
|
+
# @return [String] a copy of this string with the given extension removed. Does nothing if this string does not edit with the extension
|
33
|
+
def without_extension(ext)
|
34
|
+
return dup if ext.nil?
|
35
|
+
ext = ext.to_s
|
36
|
+
ext = '.' + ext unless ext.start_with? '.'
|
37
|
+
end_with?(ext) ? slice(0..(-ext.length - 1)) : dup
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [String, String] source and type, where type is one of <tt>:project</tt>, <tt>:user</tt>, <tt>:built_in</tt>, <tt>:gem</tt>, or <tt>:unknown</tt>
|
41
|
+
def cog_source_and_type
|
42
|
+
if start_with?(Cog.project_root) || start_with?(Cog.project_template_path) || start_with?(Cog.project_generator_path) || start_with?(Cog.project_plugin_path)
|
43
|
+
[File.basename(Cog.project_root), :project]
|
44
|
+
elsif start_with? Cog.user_dir
|
45
|
+
[File.basename(ENV['HOME']), :user]
|
46
|
+
elsif start_with? Cog.gem_dir
|
47
|
+
['cog', :built_in]
|
48
|
+
elsif start_with? File.expand_path(File.join(Cog.gem_dir, '..'))
|
49
|
+
['gem', :gem]
|
50
|
+
else
|
51
|
+
['unknown', :unknown]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/cog/plugin.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Cog
|
2
|
+
|
3
|
+
# Describes a plugin found on the {Config#plugin_path}. The plugin {DSL::Cogfile} will have already been processed, and should have contained a call to {DSL::Cogfile#autoload_plugin}, which will make it's DSL available to generators via a {GeneratorSandbox}.
|
4
|
+
class Plugin
|
5
|
+
|
6
|
+
# @return [String] name of the plugin
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# @return [String] path to the plugin directory
|
10
|
+
attr_reader :path
|
11
|
+
|
12
|
+
# @return [String] path to the plugin's cogfile
|
13
|
+
attr_reader :cogfile_path
|
14
|
+
|
15
|
+
# @return [Block] the block to use to stamp the generator
|
16
|
+
attr_accessor :stamp_generator_block
|
17
|
+
|
18
|
+
# @param cogfile_path [String] path to the plugin Cogfile
|
19
|
+
def initialize(cogfile_path)
|
20
|
+
unless File.exists?(cogfile_path)
|
21
|
+
raise Errors::InvalidPluginConfiguration.new(cogfile_path)
|
22
|
+
end
|
23
|
+
@cogfile_path = File.expand_path cogfile_path
|
24
|
+
@path = File.dirname @cogfile_path
|
25
|
+
@name = File.basename @path
|
26
|
+
@name = $1 if /^(.+?)\-(\d|\.)+(rc2)?$/ =~ @name
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sort plugins by name
|
30
|
+
def <=>(other)
|
31
|
+
@name <=> other.name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/cog/spec_helpers.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'cog/spec_helpers/runner'
|
2
2
|
require 'cog/spec_helpers/matchers'
|
3
|
-
require 'fileutils'
|
4
3
|
|
5
4
|
module Cog
|
6
5
|
|
@@ -44,10 +43,15 @@ module Cog
|
|
44
43
|
File.expand_path File.join(File.dirname(__FILE__), '..', '..', 'spec')
|
45
44
|
end
|
46
45
|
|
47
|
-
# @return [String] directory of
|
46
|
+
# @return [String] directory of the active fixture.
|
48
47
|
def active_fixture_dir
|
49
48
|
File.join spec_root, 'active_fixture'
|
50
49
|
end
|
50
|
+
|
51
|
+
# @return [String] directory of the active home fixture.
|
52
|
+
def active_home_fixture_dir
|
53
|
+
File.join spec_root, 'active_home_fixture'
|
54
|
+
end
|
51
55
|
|
52
56
|
# @return [String] path to the Cogfile in the active spec fixture
|
53
57
|
def cogfile_path
|
@@ -68,15 +72,15 @@ module Cog
|
|
68
72
|
# @param name [String] template identifier (without the .erb extension)
|
69
73
|
# @return [String] absolute file system path to the template
|
70
74
|
def template(name)
|
71
|
-
File.expand_path File.join(active_fixture_dir, 'cog', 'templates',
|
75
|
+
File.expand_path File.join(active_fixture_dir, 'cog', 'templates', name.to_s)
|
72
76
|
end
|
73
77
|
|
74
|
-
# @param name [String]
|
75
|
-
# @return [String]
|
76
|
-
def
|
77
|
-
File.expand_path File.join(
|
78
|
+
# @param name [String] plugin name
|
79
|
+
# @return [String] absolute file system path to the plugin directory
|
80
|
+
def plugin(name)
|
81
|
+
File.expand_path File.join(active_fixture_dir, 'cog', 'plugins', name.to_s)
|
78
82
|
end
|
79
|
-
|
83
|
+
|
80
84
|
# @param filename [String] name of a generated source file
|
81
85
|
# @return [String] absolute path to the generated file
|
82
86
|
def generated_file(filename)
|
@@ -89,16 +93,34 @@ module Cog
|
|
89
93
|
# @return [nil]
|
90
94
|
def use_fixture(name)
|
91
95
|
path = File.join spec_root, 'fixtures', name.to_s
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
+
copy_fixture path, active_fixture_dir
|
97
|
+
Dir.chdir active_fixture_dir
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
101
|
+
# The next cog spec will execute in a fresh copy of the given home fixture
|
102
|
+
# Home fixture directories are stored in <tt>spec/home_fixtures</tt>
|
103
|
+
# @param name [String] name of the home fixture
|
104
|
+
# @return [nil]
|
105
|
+
def use_home_fixture(name)
|
106
|
+
path = File.join spec_root, 'home_fixtures', name.to_s
|
107
|
+
copy_fixture path, active_home_fixture_dir
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def copy_fixture(source, dest)
|
114
|
+
if File.exists?(source) && File.directory?(source)
|
115
|
+
FileUtils.rm_rf dest if File.exists? dest
|
116
|
+
FileUtils.cp_r source, dest
|
96
117
|
else
|
97
118
|
throw :invalid_fixture_name
|
98
119
|
end
|
99
|
-
nil
|
100
120
|
end
|
101
121
|
|
122
|
+
public
|
123
|
+
|
124
|
+
extend self
|
102
125
|
end
|
103
|
-
|
104
126
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'cog/spec_helpers/matchers/match_maker'
|
2
1
|
require 'rspec'
|
2
|
+
require 'cog/spec_helpers/matchers/match_maker'
|
3
3
|
|
4
4
|
module Cog
|
5
5
|
module SpecHelpers
|
@@ -42,13 +42,23 @@ module Cog
|
|
42
42
|
end
|
43
43
|
|
44
44
|
# The target {Invocation} should write the given list of lines to standard output
|
45
|
-
# @param x [Array<String
|
45
|
+
# @param x [Array<String>, Regexp] a list of lines to match against standard output
|
46
46
|
# @return [nil]
|
47
47
|
def output(x)
|
48
48
|
match_maker do
|
49
|
-
message
|
49
|
+
message do
|
50
|
+
if x.is_a? Regexp
|
51
|
+
"to [write|not write] #{x.inspect} to STDOUT"
|
52
|
+
else
|
53
|
+
"to [write|not write] #{x.join "\n"} to STDOUT"
|
54
|
+
end
|
55
|
+
end
|
50
56
|
test do
|
51
|
-
|
57
|
+
if x.is_a? Regexp
|
58
|
+
x =~ lines.join("\n")
|
59
|
+
else
|
60
|
+
lines.zip(x).all? {|a, b| a.strip == b.to_s.strip}
|
61
|
+
end
|
52
62
|
end
|
53
63
|
end
|
54
64
|
end
|
@@ -6,13 +6,9 @@ module Cog
|
|
6
6
|
# Points to the +cog+ command-line app
|
7
7
|
class Runner
|
8
8
|
|
9
|
-
# Value of the COG_TOOLS environment variable for invocations returned from #run
|
10
|
-
attr_accessor :tools
|
11
|
-
|
12
9
|
# @param path_to_cl_app [String] path
|
13
10
|
def initialize
|
14
11
|
@cog = File.expand_path File.join(File.dirname(__FILE__), '..', '..', '..', 'bin', 'cog')
|
15
|
-
@tools = []
|
16
12
|
end
|
17
13
|
|
18
14
|
# Run cog with the given arguments
|
@@ -20,7 +16,7 @@ module Cog
|
|
20
16
|
# @return [Invocation] an object which can be used with custom {Matchers}
|
21
17
|
def run(*args)
|
22
18
|
args = [@cog, '--colorless'] + args
|
23
|
-
Invocation.new(args.collect {|x| x.to_s}
|
19
|
+
Invocation.new(args.collect {|x| x.to_s})
|
24
20
|
end
|
25
21
|
end
|
26
22
|
|
@@ -31,10 +27,8 @@ module Cog
|
|
31
27
|
|
32
28
|
# @api developer
|
33
29
|
# @param cmd [Array<String>] path to +cog+ executable and arguments
|
34
|
-
# @option opt [Array<String>] :tools ([]) a list of tools to add to the +COG_TOOLS+ environment variable just before running the command with {#exec}
|
35
30
|
def initialize(cmd, opt={})
|
36
31
|
@cmd = cmd
|
37
|
-
@tools = (opt[:tools] || []).join ':'
|
38
32
|
end
|
39
33
|
|
40
34
|
# Execute the command
|
@@ -43,7 +37,7 @@ module Cog
|
|
43
37
|
# @return [nil]
|
44
38
|
def exec(&block)
|
45
39
|
@cmd = ['bundle', 'exec'] + @cmd
|
46
|
-
ENV['
|
40
|
+
ENV['HOME'] = SpecHelpers.active_home_fixture_dir
|
47
41
|
Open3.popen3 *@cmd do |i,o,e,t|
|
48
42
|
block.call i,o,e
|
49
43
|
end
|
@@ -52,7 +46,7 @@ module Cog
|
|
52
46
|
# @api developer
|
53
47
|
# @return [String] loggable representation
|
54
48
|
def to_s
|
55
|
-
"
|
49
|
+
"`#{@cmd.compact.join ' '}`"
|
56
50
|
end
|
57
51
|
end
|
58
52
|
|
data/lib/cog/version.rb
CHANGED