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 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