cog 0.0.3 → 0.0.4
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/API.rdoc +6 -0
- data/Master.cogfile +10 -0
- data/bin/cog +5 -18
- data/lib/cog.rb +9 -32
- data/lib/cog/cogfile.rb +65 -13
- data/lib/cog/mixins.rb +11 -0
- data/lib/cog/mixins/uses_templates.rb +107 -0
- data/lib/cog/spec_helpers.rb +29 -0
- data/lib/cog/spec_helpers/matchers.rb +36 -0
- data/lib/cog/spec_helpers/runner.rb +41 -0
- data/lib/cog/template_controller.rb +16 -0
- data/lib/cog_version.rb +1 -1
- data/templates/Default.cogfile +8 -0
- metadata +12 -5
- data/lib/cog/has_template.rb +0 -59
data/API.rdoc
CHANGED
@@ -23,3 +23,9 @@ and it will look something like this:
|
|
23
23
|
# The directory in which application code can be found. This is where
|
24
24
|
# generated code will go. Probably along side non-generated code.
|
25
25
|
code_dir 'src'
|
26
|
+
|
27
|
+
== Testing
|
28
|
+
|
29
|
+
The tests are written in +RSpec+ and can be found in the <tt>spec/</tt>
|
30
|
+
directory. Custom matchers are available to make writing specs easier. See
|
31
|
+
Cog::SpecHelpers for more details.
|
data/Master.cogfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# This is the master Cogfile. It is used by cog.
|
2
|
+
|
3
|
+
# All paths are relative to the directory containing this file.
|
4
|
+
|
5
|
+
# The directory in which to find ERB template files.
|
6
|
+
template_dir 'templates'
|
7
|
+
|
8
|
+
# The directory in which application code can be found. This is where
|
9
|
+
# generated code will go. Probably along side non-generated code.
|
10
|
+
code_dir Cogfile.for_project.template_dir, :absolute => true
|
data/bin/cog
CHANGED
@@ -26,21 +26,9 @@ desc 'Write extra information'
|
|
26
26
|
switch [:v,:verbose]
|
27
27
|
|
28
28
|
desc 'Add cog to a project by generating a Cogfile in the current directory'
|
29
|
-
command :
|
29
|
+
command :init do |c|
|
30
30
|
c.action do |global_options, options, args|
|
31
|
-
|
32
|
-
File.open('Cogfile', 'w') do |file|
|
33
|
-
# TODO: use a template to do this instead
|
34
|
-
file.write("# All paths are relative to the directory containing this file.
|
35
|
-
|
36
|
-
# The directory in which to find ERB template files.
|
37
|
-
template_dir 'templates'
|
38
|
-
|
39
|
-
# The directory in which application code can be found. This is where
|
40
|
-
# generated code will go. Probably along side non-generated code.
|
41
|
-
code_dir 'src'
|
42
|
-
")
|
43
|
-
end
|
31
|
+
Cog.copy_default_cogfile 'Cogfile'
|
44
32
|
end
|
45
33
|
end
|
46
34
|
|
@@ -48,7 +36,6 @@ desc 'Create a new template'
|
|
48
36
|
arg_name 'NAME'
|
49
37
|
command :template do |c|
|
50
38
|
c.action do |global_options, options, args|
|
51
|
-
puts 'Not implemented'
|
52
39
|
end
|
53
40
|
end
|
54
41
|
|
@@ -58,11 +45,11 @@ pre do |global, command, options, args|
|
|
58
45
|
# chosen command
|
59
46
|
# Use skips_pre before a command to skip this block
|
60
47
|
# on that command only
|
61
|
-
|
62
|
-
if !cogfile && command && command.name != :
|
48
|
+
cogfile = Cog::Cogfile.for_project
|
49
|
+
if !cogfile && command && command.name != :init
|
63
50
|
puts 'No Cogfile could be found'
|
64
51
|
false
|
65
|
-
elsif cogfile && command && command.name == :
|
52
|
+
elsif cogfile && command && command.name == :init
|
66
53
|
puts "A Cogfile already exists at #{cogfile.cogfile_path.inspect}"
|
67
54
|
false
|
68
55
|
else
|
data/lib/cog.rb
CHANGED
@@ -1,37 +1,14 @@
|
|
1
1
|
require 'cog/cogfile'
|
2
|
-
require 'cog/
|
2
|
+
require 'cog/template_controller'
|
3
|
+
require 'cog/mixins'
|
4
|
+
require 'fileutils'
|
3
5
|
|
4
6
|
module Cog
|
5
|
-
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# grandparent, and so on.
|
11
|
-
#
|
12
|
-
# === Returns
|
13
|
-
# An instance of Cogfile which has been configured with a Cogfile. Or +nil+ if
|
14
|
-
# no +Cogfile+ could be found.
|
15
|
-
def self.load_cogfile
|
16
|
-
parts = Dir.pwd.split File::SEPARATOR
|
17
|
-
i = parts.length
|
18
|
-
while i >= 0 && !File.exists?(File.join(parts.slice(0, i) + ['Cogfile']))
|
19
|
-
i -= 1
|
20
|
-
end
|
21
|
-
path = File.join(parts.slice(0, i) + ['Cogfile']) if i >= 0
|
22
|
-
if path
|
23
|
-
Cogfile.instance.instance_eval do
|
24
|
-
@cogfile_path = path
|
25
|
-
@project_root = File.dirname path
|
26
|
-
end
|
27
|
-
begin
|
28
|
-
b = Cogfile.instance.instance_eval {binding}
|
29
|
-
eval File.read(path), b
|
30
|
-
rescue Exception => e
|
31
|
-
raise CogfileError.new(e.to_s)
|
32
|
-
end
|
33
|
-
Cogfile.instance
|
34
|
-
end
|
7
|
+
|
8
|
+
# Make a cogfile at the given destination path.
|
9
|
+
def self.copy_default_cogfile(dest)
|
10
|
+
puts "Generated #{dest}"
|
11
|
+
FileUtils.cp Cogfile.default_cogfile_path, dest
|
35
12
|
end
|
36
|
-
|
13
|
+
|
37
14
|
end
|
data/lib/cog/cogfile.rb
CHANGED
@@ -5,7 +5,55 @@ module Cog
|
|
5
5
|
# When the +Cogfile+ is processed, +self+ will be the singleton instance of
|
6
6
|
# this object.
|
7
7
|
class Cogfile
|
8
|
-
|
8
|
+
|
9
|
+
# Path to the master +Cogfile+.
|
10
|
+
def self.master_cogfile_path
|
11
|
+
File.join File.dirname($0), '../Master.cogfile'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Path to the default +Cogfile+.
|
15
|
+
def self.default_cogfile_path
|
16
|
+
File.join File.dirname($0), '../templates/Default.cogfile'
|
17
|
+
end
|
18
|
+
|
19
|
+
# Loads the default +Cogfile+ for the current project.
|
20
|
+
#
|
21
|
+
# The +Cogfile+ will be looked for in the present working directory. If none
|
22
|
+
# is found there the parent directory will be checked, and then the
|
23
|
+
# grandparent, and so on.
|
24
|
+
#
|
25
|
+
# === Returns
|
26
|
+
# An instance of Cogfile which has been configured with a +Cogfile+. If no
|
27
|
+
# such file was found then +nil+.
|
28
|
+
def self.for_project
|
29
|
+
return @for_project if @for_project
|
30
|
+
parts = Dir.pwd.split File::SEPARATOR
|
31
|
+
i = parts.length
|
32
|
+
while i >= 0 && !File.exists?(File.join(parts.slice(0, i) + ['Cogfile']))
|
33
|
+
i -= 1
|
34
|
+
end
|
35
|
+
path = File.join(parts.slice(0, i) + ['Cogfile']) if i >= 0
|
36
|
+
if path && File.exists?(path)
|
37
|
+
@for_project = self.new path
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Loads the master +Cogfile+.
|
42
|
+
def self.master
|
43
|
+
return @master if @master
|
44
|
+
@master = self.new master_cogfile_path
|
45
|
+
end
|
46
|
+
|
47
|
+
# Initialize from a +Cogfile+ at the given path.
|
48
|
+
def initialize(path)
|
49
|
+
@cogfile_path = File.expand_path path
|
50
|
+
@project_root = File.dirname @cogfile_path
|
51
|
+
begin
|
52
|
+
eval File.read(path), binding
|
53
|
+
rescue Exception => e
|
54
|
+
raise CogfileError.new(e.to_s)
|
55
|
+
end
|
56
|
+
end
|
9
57
|
|
10
58
|
def get_or_set(name, val, &block) # :nodoc:
|
11
59
|
if val.nil?
|
@@ -23,29 +71,33 @@ module Cog
|
|
23
71
|
attr_reader :cogfile_path
|
24
72
|
|
25
73
|
# The directory in which to find ERB template files. This is relative to
|
26
|
-
# project_root
|
74
|
+
# project_root unless the option <tt>:absolute</tt> is passed.
|
27
75
|
#
|
28
76
|
# Can be used as a getter or a setter (within the +Cogfile+).
|
29
|
-
def template_dir(val=nil)
|
30
|
-
get_or_set
|
31
|
-
|
77
|
+
def template_dir(val=nil, opt={})
|
78
|
+
get_or_set(:template_dir, val) do |val|
|
79
|
+
if opt[:absolute]
|
80
|
+
val
|
81
|
+
else
|
82
|
+
File.join project_root, val
|
83
|
+
end
|
32
84
|
end
|
33
85
|
end
|
34
86
|
|
35
87
|
# The directory in which application code can be found. This is where
|
36
88
|
# generated code will go. Probably along side non-generated code. It is
|
37
|
-
# relative to project_root
|
89
|
+
# relative to project_root unless the option <tt>:absolute</tt> is passed.
|
38
90
|
#
|
39
91
|
# Can be used as a getter or a setter (within the +Cogfile+).
|
40
|
-
def code_dir(val=nil)
|
41
|
-
get_or_set
|
42
|
-
|
92
|
+
def code_dir(val=nil, opt={})
|
93
|
+
get_or_set(:code_dir, val) do |val|
|
94
|
+
if opt[:absolute]
|
95
|
+
val
|
96
|
+
else
|
97
|
+
File.join project_root, val
|
98
|
+
end
|
43
99
|
end
|
44
100
|
end
|
45
|
-
|
46
|
-
def to_s # :nodoc:
|
47
|
-
"#{project_root} #{code_dir}"
|
48
|
-
end
|
49
101
|
end
|
50
102
|
|
51
103
|
# For wrapping errors which occur during the processing of a +Cogfile+.
|
data/lib/cog/mixins.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'cog/cogfile'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module Cog
|
5
|
+
module Mixins
|
6
|
+
|
7
|
+
# Mixin for classes that can use templates to generate code
|
8
|
+
module UsesTemplates
|
9
|
+
|
10
|
+
# Use an alternate Cogfile
|
11
|
+
def using_cogfile(cogfile)
|
12
|
+
@cogfile = cogfile
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get the template with the given name.
|
16
|
+
#
|
17
|
+
# === Parameters
|
18
|
+
# * +path+ - a path to a template file which is relative to
|
19
|
+
# Cogfile#template_dir and does not include the +.erb+ extension.
|
20
|
+
#
|
21
|
+
# === Returns
|
22
|
+
# An instance of ERB.
|
23
|
+
def get_template(path)
|
24
|
+
@cogfile ||= Cogfile.for_project
|
25
|
+
path = File.join @cogfile.template_dir, "#{path}.erb"
|
26
|
+
unless File.exists? path
|
27
|
+
raise MissingTemplate.new path
|
28
|
+
end
|
29
|
+
ERB.new File.read(path)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Stamp this object using the template at the given path.
|
33
|
+
#
|
34
|
+
# === Parameters
|
35
|
+
# * +path+ - the path to the template to use which is relative to
|
36
|
+
# Cogfile#template_dir and does not include the +.erb+ extension.
|
37
|
+
# * +dest+ - the destination path to the generated file which is relative
|
38
|
+
# to Cogfile#code_dir. If this is not provided, then the generated code
|
39
|
+
# will be returned as a string.
|
40
|
+
#
|
41
|
+
# === Context
|
42
|
+
# All keys are added as instance variables to the context that is passed
|
43
|
+
# into the template.
|
44
|
+
#
|
45
|
+
# === Returns
|
46
|
+
# +nil+ if generated to a file, otherwise returns the generated code as a
|
47
|
+
# string.
|
48
|
+
def stamp(path, dest=nil, context={})
|
49
|
+
t = get_template path
|
50
|
+
context = TemplateContext.new self, context
|
51
|
+
b = context.instance_eval {binding}
|
52
|
+
if dest.nil?
|
53
|
+
t.result(b)
|
54
|
+
else
|
55
|
+
dest = File.join @cogfile.code_dir, dest
|
56
|
+
FileUtils.mkpath File.dirname(dest) unless File.exists? dest
|
57
|
+
scratch = "#{path}.scratch"
|
58
|
+
File.open(scratch, 'w') {|file| file.write t.result(b)}
|
59
|
+
unless same? dest, scratch
|
60
|
+
puts "Generated #{dest}"
|
61
|
+
FileUtils.mv scratch, dest
|
62
|
+
else
|
63
|
+
FileUtils.rm scratch
|
64
|
+
end
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def same?(original, scratch) # :nodoc:
|
71
|
+
if File.exists? original
|
72
|
+
File.read(original) == File.read(scratch)
|
73
|
+
else
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class TemplateContext
|
79
|
+
def initialize(obj, context={})
|
80
|
+
@obj = obj
|
81
|
+
context.each do |key, value|
|
82
|
+
instance_variable_set "@#{key}", value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_missing(meth, *args, &block)
|
87
|
+
if @obj.respond_to? meth
|
88
|
+
@obj.method(meth).call *args, &block
|
89
|
+
else
|
90
|
+
super
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
module Errors
|
99
|
+
# Indiciates an attempt to use a non-existant template.
|
100
|
+
class MissingTemplate < Exception
|
101
|
+
def message
|
102
|
+
'could not find the template ' + super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'cog/spec_helpers/runner'
|
2
|
+
require 'cog/spec_helpers/matchers'
|
3
|
+
|
4
|
+
module Cog
|
5
|
+
|
6
|
+
# Modules and classes to help write specs for testing +cog+
|
7
|
+
#
|
8
|
+
# === Example
|
9
|
+
# Requiring the helpers will make extra SpecHelpers::Matchers available to
|
10
|
+
# your RSpec tests. These are useful for testing a SpecHelpers::Invocation,
|
11
|
+
# which is returned from a call to SpecHelpers::App#run
|
12
|
+
#
|
13
|
+
# require 'cog/spec_helpers'
|
14
|
+
#
|
15
|
+
# describe 'The command line interface' do
|
16
|
+
#
|
17
|
+
# before :all do
|
18
|
+
# @cog = Cog::SpecHelpers::App.new 'bin/cog'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# it 'should print help when no args are passed' do
|
22
|
+
# @cog.run.should show_help
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# end
|
26
|
+
module SpecHelpers
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
module Cog
|
4
|
+
module SpecHelpers
|
5
|
+
|
6
|
+
# Extra should or should_not matchers for RSpec.
|
7
|
+
module Matchers
|
8
|
+
|
9
|
+
class ShowHelp # :nodoc:
|
10
|
+
def matches?(runner)
|
11
|
+
@runner = runner
|
12
|
+
@runner.exec do |i,o,e|
|
13
|
+
@first_line = o.readlines.first
|
14
|
+
/help.*code gen/ =~ @first_line
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def failure_message
|
18
|
+
"expected #{@runner} to show the default help text, got #{@first_line.inspect}"
|
19
|
+
end
|
20
|
+
def negative_failure_message
|
21
|
+
"expected #{@runner} to not show the default help text, got #{@first_line.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# The target Invocation should output the default help text
|
26
|
+
def show_help
|
27
|
+
ShowHelp.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec.configure do |config|
|
35
|
+
config.include Cog::SpecHelpers::Matchers
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Cog
|
4
|
+
module SpecHelpers
|
5
|
+
|
6
|
+
# Points to the +cog+ command-line app
|
7
|
+
class App
|
8
|
+
|
9
|
+
def initialize(path_to_cl_app)
|
10
|
+
@cog = path_to_cl_app
|
11
|
+
end
|
12
|
+
|
13
|
+
# Run cog with the given arguments
|
14
|
+
#
|
15
|
+
# === Returns
|
16
|
+
# An instance of Invocation configured with the arguments. Use should and
|
17
|
+
# should_not with the custom Matchers
|
18
|
+
def run(*args)
|
19
|
+
args.unshift @cog
|
20
|
+
Invocation.new args
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Represents a +cog+ command line invocation, which can be tested with
|
25
|
+
# +RSpec+ +should+ and +should_not+ custom Matchers
|
26
|
+
class Invocation
|
27
|
+
|
28
|
+
def initialize(cmd) # :nodoc:
|
29
|
+
@cmd = cmd
|
30
|
+
end
|
31
|
+
|
32
|
+
def exec(*args, &block) # :nodoc:
|
33
|
+
Open3.popen3 *@cmd do |i,o,e,t|
|
34
|
+
block.call i,o,e
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/cog_version.rb
CHANGED
@@ -0,0 +1,8 @@
|
|
1
|
+
# All paths are relative to the directory containing this file.
|
2
|
+
|
3
|
+
# The directory in which to find ERB template files.
|
4
|
+
template_dir 'templates'
|
5
|
+
|
6
|
+
# The directory in which application code can be found. This is where
|
7
|
+
# generated code will go. Probably along side non-generated code.
|
8
|
+
code_dir 'src'
|
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: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
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-05-
|
18
|
+
date: 2012-05-13 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rake
|
@@ -54,9 +54,16 @@ extensions: []
|
|
54
54
|
extra_rdoc_files:
|
55
55
|
- API.rdoc
|
56
56
|
files:
|
57
|
+
- Master.cogfile
|
57
58
|
- bin/cog
|
59
|
+
- templates/Default.cogfile
|
58
60
|
- lib/cog/cogfile.rb
|
59
|
-
- lib/cog/
|
61
|
+
- lib/cog/mixins/uses_templates.rb
|
62
|
+
- lib/cog/mixins.rb
|
63
|
+
- lib/cog/spec_helpers/matchers.rb
|
64
|
+
- lib/cog/spec_helpers/runner.rb
|
65
|
+
- lib/cog/spec_helpers.rb
|
66
|
+
- lib/cog/template_controller.rb
|
60
67
|
- lib/cog.rb
|
61
68
|
- lib/cog_version.rb
|
62
69
|
- API.rdoc
|
data/lib/cog/has_template.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
|
-
module Cog
|
4
|
-
|
5
|
-
# Mixin for classes that can use templates to generate code
|
6
|
-
module HasTemplate
|
7
|
-
|
8
|
-
# Get the template with the given name.
|
9
|
-
#
|
10
|
-
# === Parameters
|
11
|
-
# * +path+ - a path to a template file which is relative to
|
12
|
-
# Cogfile#template_dir and does not include the +.erb+ extension.
|
13
|
-
#
|
14
|
-
# === Returns
|
15
|
-
# An instance of ERB.
|
16
|
-
def get_template(path)
|
17
|
-
path = File.join Cogfile.instance.template_dir, "#{path}.erb"
|
18
|
-
unless File.exists? path
|
19
|
-
raise MissingTemplate.new path
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# Stamp this object using the template at the given path.
|
24
|
-
#
|
25
|
-
# === Parameters
|
26
|
-
# * +path+ - the path to the template to use which is relative to
|
27
|
-
# Cogfile#template_dir and does not include the +.erb+ extension.
|
28
|
-
# * +dest+ - the destination path to the generated file which is relative
|
29
|
-
# to Cogfile#code_dir. If this is not provided, then the generated code
|
30
|
-
# will be returned as a string.
|
31
|
-
#
|
32
|
-
# === Returns
|
33
|
-
# +nil+ if generated to a file, otherwise returns the generated code as a
|
34
|
-
# string.
|
35
|
-
def stamp(path, dest=nil)
|
36
|
-
t = get_template path
|
37
|
-
scratch = "#{path}.scratch"
|
38
|
-
File.open(scratch, 'w') do |file|
|
39
|
-
file.write t.result(make_protected_binding)
|
40
|
-
# TODO: ...
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
def same?(original, scratch) # :nodoc:
|
46
|
-
if File.exists? original
|
47
|
-
File.read(original) == File.read(scratch)
|
48
|
-
else
|
49
|
-
false
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class MissingTemplate < Exception
|
55
|
-
def message
|
56
|
-
'could not find the template ' + super
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|