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