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 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.
@@ -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 :project do |c|
29
+ command :init do |c|
30
30
  c.action do |global_options, options, args|
31
- puts 'Created Cogfile'
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
- cogfile = Cog.load_cogfile
62
- if !cogfile && command && command.name != :project
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 == :project
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/has_template'
2
+ require 'cog/template_controller'
3
+ require 'cog/mixins'
4
+ require 'fileutils'
3
5
 
4
6
  module Cog
5
-
6
- # Loads the +Cogfile+ for the current project.
7
- #
8
- # The +Cogfile+ will be looked for in the present working directory. If none
9
- # is found there the parent directory will be checked, and then the
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
@@ -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
- include Singleton
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 :template_dir, val do |val|
31
- File.join project_root, val
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 :code_dir, val do |val|
42
- File.join project_root, val
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+.
@@ -0,0 +1,11 @@
1
+ require 'cog/mixins/uses_templates'
2
+
3
+ module Cog
4
+
5
+ # Modules that extend your code generating classes with helper methods.
6
+ #
7
+ # * Mixins::UsesTemplates has methods for using +ERB+ template files
8
+ module Mixins
9
+ end
10
+
11
+ end
@@ -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
@@ -0,0 +1,16 @@
1
+ require 'cog/mixins/uses_templates'
2
+ require 'cog/cogfile'
3
+
4
+ module Cog
5
+
6
+ class TemplateController
7
+
8
+ include Mixins::UsesTemplates
9
+
10
+ def initialize(opt={})
11
+ using_cogfile Cogfile.master
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -1,3 +1,3 @@
1
1
  module Cog
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
@@ -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: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
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-12 00:00:00 Z
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/has_template.rb
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
@@ -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