sow 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +622 -0
- data/HISTORY +14 -0
- data/MANIFEST +138 -0
- data/NOTES +86 -0
- data/README +157 -0
- data/bin/sow +4 -0
- data/lib/sow.rb +12 -0
- data/lib/sow/command.rb +201 -0
- data/lib/sow/context.rb +29 -0
- data/lib/sow/core_ext.rb +47 -0
- data/lib/sow/generators/base.rb +321 -0
- data/lib/sow/generators/create.rb +111 -0
- data/lib/sow/generators/delete.rb +50 -0
- data/lib/sow/generators/update.rb +47 -0
- data/lib/sow/logger.rb +73 -0
- data/lib/sow/manager.rb +101 -0
- data/lib/sow/metadata.rb +115 -0
- data/lib/sow/plugin.rb +484 -0
- data/lib/sow/script.rb +186 -0
- data/lib/sow/session.rb +150 -0
- data/meta/authors +1 -0
- data/meta/contact +1 -0
- data/meta/description +1 -0
- data/meta/homepage +1 -0
- data/meta/loadpath +2 -0
- data/meta/package +1 -0
- data/meta/project +1 -0
- data/meta/repository +1 -0
- data/meta/requires +2 -0
- data/meta/ruby +2 -0
- data/meta/version +1 -0
- data/plug/sow/seeds/bin/.meta/created +1 -0
- data/plug/sow/seeds/bin/.meta/description +1 -0
- data/plug/sow/seeds/bin/.meta/license +1 -0
- data/plug/sow/seeds/bin/.meta/package +1 -0
- data/plug/sow/seeds/bin/.meta/requires +1 -0
- data/plug/sow/seeds/bin/.meta/title +1 -0
- data/plug/sow/seeds/bin/.meta/version +1 -0
- data/plug/sow/seeds/bin/SCRIPT.rb +22 -0
- data/plug/sow/seeds/bin/template/bin/command.rb +12 -0
- data/plug/sow/seeds/hoe/SCRIPT.rb +28 -0
- data/plug/sow/seeds/hoe/template/History.txt +7 -0
- data/plug/sow/seeds/hoe/template/Manifest.txt +8 -0
- data/plug/sow/seeds/hoe/template/README.txt +49 -0
- data/plug/sow/seeds/hoe/template/Rakefile +13 -0
- data/plug/sow/seeds/hoe/template/bin/__name__ +0 -0
- data/plug/sow/seeds/hoe/template/lib/__name__.rb +4 -0
- data/plug/sow/seeds/hoe/template/test/test___name__.rb +1 -0
- data/plug/sow/seeds/license/SCRIPT.rb +27 -0
- data/plug/sow/seeds/license/template/META/license +1 -0
- data/plug/sow/seeds/license/template/gpl/LICENSE +622 -0
- data/plug/sow/seeds/license/template/lgpl/LICENSE +789 -0
- data/plug/sow/seeds/license/template/mit/LICENSE +22 -0
- data/plug/sow/seeds/ruby/COPY.yml +14 -0
- data/plug/sow/seeds/ruby/DATA.yml +8 -0
- data/plug/sow/seeds/ruby/USAGE.txt +8 -0
- data/plug/sow/seeds/ruby/script.sow.rb +17 -0
- data/plug/sow/seeds/ruby/template/COPYING +622 -0
- data/plug/sow/seeds/ruby/template/History.rdoc +18 -0
- data/plug/sow/seeds/ruby/template/README.rdoc +43 -0
- data/plug/sow/seeds/ruby/template/README.rdoc.till +43 -0
- data/plug/sow/seeds/ruby/template/Rakefile +1 -0
- data/plug/sow/seeds/ruby/template/bin/__package__ +2 -0
- data/plug/sow/seeds/ruby/template/lib/__package__.rb +3 -0
- data/plug/sow/seeds/ruby/template/meta/created +1 -0
- data/plug/sow/seeds/ruby/template/meta/description +1 -0
- data/plug/sow/seeds/ruby/template/meta/license +1 -0
- data/plug/sow/seeds/ruby/template/meta/package +1 -0
- data/plug/sow/seeds/ruby/template/meta/requires +1 -0
- data/plug/sow/seeds/ruby/template/meta/title +1 -0
- data/plug/sow/seeds/ruby/template/meta/version +1 -0
- data/plug/sow/seeds/ruby/template/setup.rb +1467 -0
- data/plug/sow/seeds/ruby/template/test/template.rb +17 -0
- data/plug/sow/seeds/testunit/COPY.yml +12 -0
- data/plug/sow/seeds/testunit/DATA.yml +23 -0
- data/plug/sow/seeds/testunit/USAGE.txt +11 -0
- data/plug/sow/seeds/testunit/_SCRIPT.rb +42 -0
- data/plug/sow/seeds/testunit/template/form/testunit +24 -0
- data/plug/sow/seeds/testunit/template/test/test_template.rb +15 -0
- data/plug/sow/seeds/website/template/assets/styles/color.css +0 -0
- data/plug/sow/seeds/website/template/assets/styles/font.css +0 -0
- data/plug/sow/seeds/website/template/assets/styles/index.css +4 -0
- data/plug/sow/seeds/website/template/assets/styles/reset.css +0 -0
- data/plug/sow/seeds/website/template/assets/styles/struct.css +0 -0
- data/plug/sow/seeds/website/template/index.html +15 -0
- data/test/features/scaffold.feature +13 -0
- data/test/features/step_definitions/cli_steps.rb +0 -0
- data/test/features/step_definitions/fixture_steps.rb +72 -0
- data/test/features/support/env.rb +41 -0
- data/test/unit/fixtures/README +5 -0
- data/test/unit/helper.rb +23 -0
- data/test/unit/test_metadata.rb +17 -0
- data/test/unit/test_scaffold.rb +37 -0
- metadata +178 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'sow/generators/base'
|
2
|
+
|
3
|
+
module Sow
|
4
|
+
|
5
|
+
module Generators
|
6
|
+
|
7
|
+
# = Delete Generator
|
8
|
+
#
|
9
|
+
#--
|
10
|
+
# TODO: should force option be require to do this?
|
11
|
+
# And without force it just reports what would be deleted?
|
12
|
+
#++
|
13
|
+
class Delete < Base
|
14
|
+
|
15
|
+
def mark; 'delete'; end
|
16
|
+
|
17
|
+
#
|
18
|
+
def actionlist_check(list)
|
19
|
+
list
|
20
|
+
end
|
21
|
+
|
22
|
+
# Sort the manifest files before directory.
|
23
|
+
# This is opposite of Construct::Create.
|
24
|
+
def actionlist_sort(list)
|
25
|
+
list.reverse
|
26
|
+
#dirs, files = *list.partition{ |src, dest, opts| (source + src).directory? }
|
27
|
+
#files.sort{|a,b| a[1]<=>b[1]} + dirs.sort{|a,b| a[1]<=>b[1]}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Delete template file.
|
31
|
+
def delete(loc, src, dest, opts)
|
32
|
+
if File.exist?(dest)
|
33
|
+
how = 'delete'
|
34
|
+
begin
|
35
|
+
rm(dest)
|
36
|
+
rescue Errno::ENOTEMPTY
|
37
|
+
how = 'skip'
|
38
|
+
end
|
39
|
+
else
|
40
|
+
how = 'missing'
|
41
|
+
end
|
42
|
+
return how, dest
|
43
|
+
end
|
44
|
+
|
45
|
+
end#class Delete
|
46
|
+
|
47
|
+
end#module Generators
|
48
|
+
|
49
|
+
end#module Sow
|
50
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'sow/generators/create'
|
2
|
+
|
3
|
+
#--
|
4
|
+
# TODO: Could we use an "environment" settings file to specify environment variables to check for template variables?
|
5
|
+
#++
|
6
|
+
|
7
|
+
module Sow
|
8
|
+
|
9
|
+
module Generators
|
10
|
+
|
11
|
+
# = Update Generator
|
12
|
+
#
|
13
|
+
# Update is the same as the Create Generator with the exception
|
14
|
+
# that it pulls all metadata from the destination.
|
15
|
+
#
|
16
|
+
class Update < Create
|
17
|
+
|
18
|
+
=begin
|
19
|
+
### Copy the file, processing it with ERB if
|
20
|
+
### it has an .erb extension. Unlike Create's
|
21
|
+
### this will skip pre-existant non-erb files.
|
22
|
+
def copy_doc(src, dest)
|
23
|
+
tmp = File.join(source, src)
|
24
|
+
ext = File.extname(src)
|
25
|
+
case ext
|
26
|
+
when '.erb'
|
27
|
+
text = erb(tmp)
|
28
|
+
how = (File.exist?(dest) ? 'update' : 'create')
|
29
|
+
write(dest, text)
|
30
|
+
else
|
31
|
+
if File.exist?(dest)
|
32
|
+
how = 'skip'
|
33
|
+
else
|
34
|
+
how = 'create'
|
35
|
+
cp(tmp, dest)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
return how, dest
|
39
|
+
end
|
40
|
+
=end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
data/lib/sow/logger.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Sow
|
2
|
+
|
3
|
+
# The Logger class routes output to the console.
|
4
|
+
#
|
5
|
+
class Logger
|
6
|
+
|
7
|
+
#
|
8
|
+
attr :generator
|
9
|
+
|
10
|
+
#
|
11
|
+
def initialize(generator)
|
12
|
+
@generator = generator
|
13
|
+
end
|
14
|
+
|
15
|
+
# If there is nothing to generate this will be called
|
16
|
+
# to display a message to the effect.
|
17
|
+
def report_nothing_to_generate
|
18
|
+
report "Nothing to generate."
|
19
|
+
end
|
20
|
+
|
21
|
+
# Output to provide on startup of generation.
|
22
|
+
def report_startup(source, output) # FIXME: pass what info?
|
23
|
+
@time = Time.now
|
24
|
+
dir = File.basename(source) #File.basename(File.dirname(source))
|
25
|
+
report "Generating #{dir} in #{File.basename(output)}:\n\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Output to provide when generation is complete.
|
29
|
+
def report_complete
|
30
|
+
report "\nFinished in %.3f seconds." % [Time.now - @time]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Output to provide as generation is progressing.
|
34
|
+
def report_create(file, how, atime)
|
35
|
+
report "%10s [%.4fs] %s" % [how, (Time.now - atime), file]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Use this to report any "templating" that needs
|
39
|
+
# to done by hand.
|
40
|
+
def report_fixes(marker='FIXME:')
|
41
|
+
glist = check_for_fixes(marker)
|
42
|
+
unless glist.empty?
|
43
|
+
puts "\nYou need to fix the occurances of '#{marker}' in the following files:\n\n"
|
44
|
+
glist.each do |file|
|
45
|
+
puts " #{file}"
|
46
|
+
end
|
47
|
+
puts
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# TODO: don't use grep
|
52
|
+
def check_for_fixes(marker)
|
53
|
+
g = `grep -R #{marker} .` # FIXME Use ruby code instead
|
54
|
+
glist = []
|
55
|
+
g.each_line do |line|
|
56
|
+
line = line.gsub('./','')
|
57
|
+
indx = line.index(':')
|
58
|
+
file, *desc = *line.split(':')
|
59
|
+
glist << file
|
60
|
+
end
|
61
|
+
glist.uniq
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def report(message)
|
67
|
+
puts message unless generator.quiet? or generator.trial?
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
data/lib/sow/manager.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'sow/plugin'
|
2
|
+
require 'facets/plugin_manager'
|
3
|
+
|
4
|
+
module Sow
|
5
|
+
|
6
|
+
# The Manager class locates sow plugins.
|
7
|
+
#
|
8
|
+
class Manager
|
9
|
+
|
10
|
+
#
|
11
|
+
PLUGIN_DIRECTORY = "sow/seeds"
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
end
|
15
|
+
|
16
|
+
# Plugins
|
17
|
+
#--
|
18
|
+
# Note that order of paths is important here.
|
19
|
+
#++
|
20
|
+
def plugin_locations
|
21
|
+
@plugin_locations ||= (
|
22
|
+
pl = {}
|
23
|
+
pa = []
|
24
|
+
pa |= plugins_from_packages
|
25
|
+
#pa |= plugins_core)
|
26
|
+
#pa |= plugins_user
|
27
|
+
pa.each do |d|
|
28
|
+
pl[File.basename(d)] = d
|
29
|
+
end
|
30
|
+
pl
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def plugins
|
35
|
+
@plugins ||= plugin_locations.keys
|
36
|
+
end
|
37
|
+
|
38
|
+
def plugin(session, name, options)
|
39
|
+
location = plugin_locations[name]
|
40
|
+
raise "unknown scaffolding -- #{name}" unless location
|
41
|
+
Plugin.new(session, location, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Convert path into plugin.
|
45
|
+
#def plugin(path)
|
46
|
+
# path = Pathname.new(path) unless path.is_a?(Pathname)
|
47
|
+
# Plugin.new(:location => path, :project => project, :command => cli)
|
48
|
+
#end
|
49
|
+
|
50
|
+
# This routine searches for seeds (sow plugins),
|
51
|
+
#
|
52
|
+
def plugins_from_packages
|
53
|
+
match = File.join(PLUGIN_DIRECTORY, '*/')
|
54
|
+
PluginManager.find(match).map do |path|
|
55
|
+
path.chomp('/')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
=begin
|
60
|
+
# Plugins installed in other packages.
|
61
|
+
# This routine searches through the $LOAD_PATH
|
62
|
+
# looking for directories with a MANIFEST.sow file.
|
63
|
+
#
|
64
|
+
def plugins_from_packages
|
65
|
+
plugins = []
|
66
|
+
# Standard $LOAD_PATH
|
67
|
+
$LOAD_PATH.uniq.each do |path|
|
68
|
+
dirs = Dir.glob(File.join(path, PLUGIN_DIRECTORY, '*/'))
|
69
|
+
#dirs = dirs.select{ |d| File.directory?(d) }
|
70
|
+
dirs = dirs.map{ |d| d.chomp('/') }
|
71
|
+
plugins.concat(dirs)
|
72
|
+
end
|
73
|
+
# ROLL (load latest versions only)
|
74
|
+
if defined?(::Roll)
|
75
|
+
::Roll::Library.ledger.each do |name, lib|
|
76
|
+
lib = lib.sort.first if Array===lib
|
77
|
+
lib.load_path.each do |path|
|
78
|
+
path = File.join(lib.location, path)
|
79
|
+
dirs = Dir.glob(File.join(path, PLUGIN_DIRECTORY, '*/'))
|
80
|
+
#dirs = dirs.select{ |d| File.directory?(d) }
|
81
|
+
dirs = dirs.map{ |d| d.chomp('/') }
|
82
|
+
plugins.concat(dirs)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
# RubyGems (load latest versions only)
|
87
|
+
if defined?(::Gem)
|
88
|
+
Gem.latest_load_paths do |path|
|
89
|
+
dirs = Dir.glob(File.join(path, PLUGIN_DIRECTORY, '*/'))
|
90
|
+
dirs = dirs.map{ |d| d.chomp('/') }
|
91
|
+
plugins.concat(dirs)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
plugins
|
95
|
+
end
|
96
|
+
=end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end #module Sow
|
101
|
+
|
data/lib/sow/metadata.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
module Sow
|
2
|
+
|
3
|
+
# Metadata located in destination.
|
4
|
+
#
|
5
|
+
# This can be used to "prefill" template variables in some
|
6
|
+
# types of scaffolding.
|
7
|
+
#
|
8
|
+
#--
|
9
|
+
# TODO: Use POM::Metadata instead?
|
10
|
+
#
|
11
|
+
# TODO: Should this only be used when updating?
|
12
|
+
#
|
13
|
+
# TODO: If we copy meta/ first then it can be reused. ?
|
14
|
+
#++
|
15
|
+
class Metadata
|
16
|
+
alias :__id__ :object_id
|
17
|
+
|
18
|
+
instance_methods.each{ |m| private m unless m.to_s =~ /^__/ }
|
19
|
+
|
20
|
+
#
|
21
|
+
def initialize(destination)
|
22
|
+
@dir = destination
|
23
|
+
@cache = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Is there a metadata directory located in the destination directory?
|
27
|
+
def exist?
|
28
|
+
@exist ||= Dir.glob(File.join(@dir, '{.meta,meta}/')).first
|
29
|
+
end
|
30
|
+
|
31
|
+
# If method is missing, lookup metdata value by that name.
|
32
|
+
def method_missing(s, *a, &b)
|
33
|
+
s = s.to_s
|
34
|
+
if s =~ /=$/
|
35
|
+
s = s.chomp('=')
|
36
|
+
self[s] = a[0]
|
37
|
+
else
|
38
|
+
s = s.chomp('?')
|
39
|
+
self[s]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Metadata has given +entry+?
|
44
|
+
def respond_to?(entry)
|
45
|
+
self[entry] ? true : false
|
46
|
+
end
|
47
|
+
|
48
|
+
# Get a metadata value directly from the metadata cache.
|
49
|
+
def __get__(entry)
|
50
|
+
@cache[entry.to_s]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get a metadata value. If not found in the cache,
|
54
|
+
# attempt to load it from the path store.
|
55
|
+
def [](s)
|
56
|
+
s = s.to_s
|
57
|
+
if @cache.key?(s)
|
58
|
+
@cache[s]
|
59
|
+
else
|
60
|
+
#@cache[s] = HOLE + " (#{s})"
|
61
|
+
@cache[s] = load_value(s) #|| HOLE + " (#{s})"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set a metadate value.
|
66
|
+
def []=(k,v)
|
67
|
+
@cache[k.to_s] = v
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Load metadata value.
|
73
|
+
#
|
74
|
+
# In update mode, metadata is looked for in the receiving end.
|
75
|
+
#
|
76
|
+
def load_value(name)
|
77
|
+
#val = read_value_from_commandline(name)
|
78
|
+
#return val if val
|
79
|
+
|
80
|
+
#if @plugin.update?
|
81
|
+
# val = load_value_from_destination(name)
|
82
|
+
# return val if val
|
83
|
+
#end
|
84
|
+
|
85
|
+
# See if the metadata is defined in the destination.
|
86
|
+
|
87
|
+
file = Dir[File.join(@dir, '{meta,.meta}', name)].first
|
88
|
+
|
89
|
+
if file && File.file?(file)
|
90
|
+
File.read(file).strip # erb?
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#def load_template_metadata
|
97
|
+
# Dir[File.join(metasrc, '*')].each do |f|
|
98
|
+
# @metadata_source[File.basename(f)] = erb(f).strip
|
99
|
+
# end
|
100
|
+
#end
|
101
|
+
|
102
|
+
# Processes with erb.
|
103
|
+
def erb(file)
|
104
|
+
erb = ERB.new(File.read(file))
|
105
|
+
erb.result(binding!)
|
106
|
+
end
|
107
|
+
|
108
|
+
def binding!
|
109
|
+
binding
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
data/lib/sow/plugin.rb
ADDED
@@ -0,0 +1,484 @@
|
|
1
|
+
require 'sow/core_ext'
|
2
|
+
require 'sow/session'
|
3
|
+
require 'sow/logger'
|
4
|
+
require 'sow/script'
|
5
|
+
require 'sow/generators/create'
|
6
|
+
require 'sow/generators/update'
|
7
|
+
require 'sow/generators/delete'
|
8
|
+
|
9
|
+
require 'facets/erb'
|
10
|
+
require 'facets/ostruct'
|
11
|
+
require 'facets/kernel/instance_class'
|
12
|
+
|
13
|
+
module Sow
|
14
|
+
|
15
|
+
# Plugin encapsulates information about a sow plugin.
|
16
|
+
#
|
17
|
+
class Plugin
|
18
|
+
# Name of template directory.
|
19
|
+
TEMPLATE_NAME = 'template/'
|
20
|
+
# Name of plugin script file.
|
21
|
+
SCRIPT_NAME = 'SCRIPT{,.rb}'
|
22
|
+
# Name of usage document.
|
23
|
+
USAGE_NAME = 'USAGE{,.txt}'
|
24
|
+
# Name of metadata file.
|
25
|
+
DATA_NAME = 'DATA{,.yml,.yaml}'
|
26
|
+
# Name of copy file.
|
27
|
+
COPY_NAME = 'COPY{,.yml,.yaml}'
|
28
|
+
|
29
|
+
# Instanance of Session.
|
30
|
+
attr :session
|
31
|
+
# Location of plugin.
|
32
|
+
attr :location
|
33
|
+
# Argmuent
|
34
|
+
attr :argument
|
35
|
+
# Options
|
36
|
+
attr :options
|
37
|
+
# Desitnation pathname
|
38
|
+
attr :destination
|
39
|
+
|
40
|
+
#
|
41
|
+
def initialize(session, location, options)
|
42
|
+
@session = session
|
43
|
+
@options = options.to_ostruct
|
44
|
+
|
45
|
+
@location = Pathname.new(location)
|
46
|
+
|
47
|
+
@copy = []
|
48
|
+
end
|
49
|
+
|
50
|
+
# Metadata from scaffold destination.
|
51
|
+
def metadata
|
52
|
+
session.metadata
|
53
|
+
end
|
54
|
+
|
55
|
+
# Destination for scaffolding.
|
56
|
+
def destination
|
57
|
+
session.destination
|
58
|
+
end
|
59
|
+
|
60
|
+
# Argument for main command option.
|
61
|
+
def argument
|
62
|
+
options.argument
|
63
|
+
end
|
64
|
+
|
65
|
+
# Directory with template files.
|
66
|
+
def template_dir
|
67
|
+
@template_dir ||= grab(TEMPLATE_NAME)
|
68
|
+
end
|
69
|
+
|
70
|
+
# File containing usage text.
|
71
|
+
def usage_file
|
72
|
+
@usage_file ||= grab(USAGE_NAME)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Metadata file.
|
76
|
+
def meta_file
|
77
|
+
@meta_file ||= grab(DATA_NAME)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Transfer file.
|
81
|
+
def seed_file
|
82
|
+
@seed_file ||= grab(COPY_NAME)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Traditional alternative to the meta & seed file.
|
86
|
+
def script_file
|
87
|
+
@script_file ||= grab(SCRIPT_NAME)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Usage text.
|
91
|
+
def usage
|
92
|
+
@usage ||= (
|
93
|
+
if usage_file
|
94
|
+
Usage.new(usage_file)
|
95
|
+
else
|
96
|
+
"Generate #{self.class.name} scaffolding."
|
97
|
+
end
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Plugin's special metadata. These entries are
|
102
|
+
# converted to methods for rendering of the
|
103
|
+
# transfer list.
|
104
|
+
def meta
|
105
|
+
@meta ||= (
|
106
|
+
if meta_file
|
107
|
+
YAML.load(File.new(meta_file))
|
108
|
+
else
|
109
|
+
{}
|
110
|
+
end
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Plugin's transfer list.
|
115
|
+
def seed
|
116
|
+
@seed ||= (
|
117
|
+
if seed_file
|
118
|
+
res = template.erb_result(File.read(seed_file))
|
119
|
+
YAML.load(res)
|
120
|
+
else
|
121
|
+
[{'from' => '**/*'}]
|
122
|
+
end
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# Grab the first instance of a mathcing glob as a Pathname object.
|
129
|
+
def grab(glob)
|
130
|
+
file = Dir.glob(File.join(location, glob), File::FNM_CASEFOLD).first
|
131
|
+
file ? Pathname.new(file) : nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# Erb OpenTemplate
|
135
|
+
def template
|
136
|
+
@template ||= (
|
137
|
+
ERB::OpenTemplate.new(options, session, :options => options, :metadata=>metadata)
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
=begin
|
142
|
+
# Setup up the option parsing for the specific plugin.
|
143
|
+
# This method takes an instance of OptionParser.
|
144
|
+
def setup_options(parser)
|
145
|
+
txt = []
|
146
|
+
txt << "Usage: sow " + script[:usage] if script[:usage]
|
147
|
+
txt << script[:help] if script[:help]
|
148
|
+
parser.banner = txt.join("\n")
|
149
|
+
script[:options].each do |name, desc, valid|
|
150
|
+
if valid.arity == 0
|
151
|
+
parser.on("--#{name}", desc, &valid)
|
152
|
+
else
|
153
|
+
parser.on("--#{name} [VALUE]", desc, &valid)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
=end
|
158
|
+
|
159
|
+
public
|
160
|
+
|
161
|
+
# Prepare for scaffolding procedure. If a script.rb file
|
162
|
+
# was provided then uses the class defined within it
|
163
|
+
# to build the proper copylist.
|
164
|
+
#
|
165
|
+
# IMPORTANT: The class name must have the same as the
|
166
|
+
# plugin directory it is within.
|
167
|
+
#
|
168
|
+
# If not using a script.rb, but rather a seed.yml and/or
|
169
|
+
# a meta.yml file, this reads and prepares those instead.
|
170
|
+
|
171
|
+
def setup
|
172
|
+
if script_file
|
173
|
+
require(File.expand_path(script_file))
|
174
|
+
scriptClass = Script.registry[location.basename.to_s]
|
175
|
+
@script = scriptClass.new(session, options)
|
176
|
+
@script.setup
|
177
|
+
@script.manifest
|
178
|
+
@copy = @script.copylist
|
179
|
+
else
|
180
|
+
prepare_meta
|
181
|
+
prepare_seed
|
182
|
+
prepare_data
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
#setup_arguments #(arguments)
|
187
|
+
#setup_metadata
|
188
|
+
#setup_script #(session)
|
189
|
+
#init
|
190
|
+
#parse
|
191
|
+
|
192
|
+
# Any arguments defined in the script get there assigments
|
193
|
+
# defined as singleton methods via the script[]= call.
|
194
|
+
#def setup_arguments #(a)
|
195
|
+
# #h = {}
|
196
|
+
# a = script[:values]
|
197
|
+
# script[:arguments].each_with_index do |(name, desc, valid), i|
|
198
|
+
# if valid
|
199
|
+
# script[name] = valid.call(a[i])
|
200
|
+
# else
|
201
|
+
# script[name] = a[i]
|
202
|
+
# end
|
203
|
+
# end
|
204
|
+
# #@args = h #TODO: where?
|
205
|
+
#end
|
206
|
+
|
207
|
+
# Collect any metadata settings that may have been made
|
208
|
+
# in the initial script evaluation.
|
209
|
+
#def setup_metadata
|
210
|
+
# script.metadata.each do |k,v|
|
211
|
+
# metadata[k] = v
|
212
|
+
# end
|
213
|
+
#end
|
214
|
+
|
215
|
+
# Setup the plugin with information about current operation.
|
216
|
+
#
|
217
|
+
# TODO: This seems wrong. Need to figure a better way!
|
218
|
+
#
|
219
|
+
#def setup_script
|
220
|
+
# script.instance_variable_set("@session", session)
|
221
|
+
# script.instance_variable_set("@metadata", metadata)
|
222
|
+
# #script[:values].each do |v|
|
223
|
+
# #end
|
224
|
+
#end
|
225
|
+
|
226
|
+
# Define metadata entries as singleton methods of the Erb template.
|
227
|
+
def prepare_meta
|
228
|
+
meta.each do |m,c|
|
229
|
+
template.instance_class do
|
230
|
+
eval %{
|
231
|
+
def #{m}
|
232
|
+
#{c}
|
233
|
+
end
|
234
|
+
}
|
235
|
+
end
|
236
|
+
end
|
237
|
+
# validate
|
238
|
+
template.validate if meta['validate']
|
239
|
+
end
|
240
|
+
|
241
|
+
#
|
242
|
+
def prepare_seed
|
243
|
+
seed.each do |opts|
|
244
|
+
opts.rekey!(&:to_s)
|
245
|
+
from = opts.delete('from')
|
246
|
+
to = opts.delete('to') || '.'
|
247
|
+
cond = opts.delete('if')
|
248
|
+
next unless template.instance_eval(cond) if cond
|
249
|
+
@copy << [from, to, opts]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
def prepare_data
|
255
|
+
meta.each do |m,c|
|
256
|
+
metadata[m] = template.__send__(m)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Designate a copying action.
|
261
|
+
#
|
262
|
+
#def copy(*from_to_opts)
|
263
|
+
# opts = Hash===from_to_opts.last ? from_to_opts.pop : {}
|
264
|
+
# from, to = *from_to_opts
|
265
|
+
# to = to || '.'
|
266
|
+
# @copylist << [from, to, opts]
|
267
|
+
#end
|
268
|
+
|
269
|
+
# Expanded copy list.
|
270
|
+
def copylist
|
271
|
+
@copylist ||= copylist_sort(copylist_glob(@copy)) #redest(list)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Raw copylist as defined in the script.
|
275
|
+
#def script_copylist
|
276
|
+
# @script_copylist ||= (
|
277
|
+
# script[:copytemp].each{|s| s.call}
|
278
|
+
# script[:copylist]
|
279
|
+
# )
|
280
|
+
#end
|
281
|
+
|
282
|
+
# Does the script define a package name.
|
283
|
+
#def name
|
284
|
+
# script[:name]
|
285
|
+
#end
|
286
|
+
|
287
|
+
# Expand copylist by globbing entries.
|
288
|
+
|
289
|
+
def copylist_glob(copylist)
|
290
|
+
list = []
|
291
|
+
dotpaths = ['.', '..']
|
292
|
+
|
293
|
+
copylist.each do |from, into, opts|
|
294
|
+
cdir = opts['cd'] || '.'
|
295
|
+
srcs = []
|
296
|
+
|
297
|
+
Dir.chdir(template_dir + cdir) do
|
298
|
+
less = opts['less'] ? Dir.multiglob_r(opts['less']) : []
|
299
|
+
srcs = Dir.glob(from, File::FNM_DOTMATCH)
|
300
|
+
srcs = srcs - less
|
301
|
+
srcs = srcs.reject{ |d| File.basename(d) =~ /^[.]{1,2}$/ }
|
302
|
+
end
|
303
|
+
|
304
|
+
# remove +less+ option, not needed any more
|
305
|
+
opts.delete('less')
|
306
|
+
#srcs = filter_paths(srcs)
|
307
|
+
srcs.each do |src|
|
308
|
+
case into
|
309
|
+
when /\/$/
|
310
|
+
dest = File.join(into, File.basename(src))
|
311
|
+
when '.'
|
312
|
+
dest = src
|
313
|
+
else
|
314
|
+
dest = into
|
315
|
+
end
|
316
|
+
source = (cdir == '.' ? src : File.join(cdir, src))
|
317
|
+
list << [template_dir, source, template_to_filename(dest), opts] #dest
|
318
|
+
end
|
319
|
+
end
|
320
|
+
list = uniq(list)
|
321
|
+
list
|
322
|
+
end
|
323
|
+
|
324
|
+
# Reduce copylist to uniq transfers. If transfers are the same
|
325
|
+
# the later transfere takes precedence. Transfire options are
|
326
|
+
# not considered in determining uniquness.
|
327
|
+
|
328
|
+
def uniq(list)
|
329
|
+
h = {}
|
330
|
+
list.each do |dir, src, dest, opts|
|
331
|
+
h[[dir,src,dest]] = opts
|
332
|
+
end
|
333
|
+
h.inject([]){ |a,x| a << x.flatten; a }
|
334
|
+
end
|
335
|
+
|
336
|
+
=begin
|
337
|
+
# Filter out special paths from copylist.
|
338
|
+
#
|
339
|
+
def filter_paths(paths)
|
340
|
+
filter.each do |re|
|
341
|
+
paths = paths.reject do |pn|
|
342
|
+
case re
|
343
|
+
when Regexp
|
344
|
+
re =~ pn.to_s
|
345
|
+
else
|
346
|
+
re == pn.to_s
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
paths
|
351
|
+
end
|
352
|
+
=end
|
353
|
+
|
354
|
+
# Convert a template pathname into a destination pathname.
|
355
|
+
# This allows for substitution in the pathnames themselves
|
356
|
+
# by using '__name__' notation.
|
357
|
+
#
|
358
|
+
def template_to_filename(path)
|
359
|
+
name = path.dup #chomp('.erb')
|
360
|
+
name = name.gsub(/__(.*?)__/) do |md|
|
361
|
+
metadata.__get__($1) || 'FIXME' # TODO: raise error?
|
362
|
+
end
|
363
|
+
#if md =~ /^(.*?)[-]$/
|
364
|
+
# name = metadata[md[1]] || plugin.metadata(md[1]) || name
|
365
|
+
#end
|
366
|
+
name
|
367
|
+
end
|
368
|
+
|
369
|
+
# Sort the list, directory before files and in alphabetical order.
|
370
|
+
def copylist_sort(list)
|
371
|
+
dirs, files = *copylist_partition(list)
|
372
|
+
dirs.sort{|a,b| a[2]<=>b[2]} + files.sort{|a,b| a[2]<=>b[2]}
|
373
|
+
end
|
374
|
+
|
375
|
+
# Partition the list between directories and files.
|
376
|
+
def copylist_partition(list)
|
377
|
+
list.partition{ |loc, src, dest, opts| (loc + src).directory? }
|
378
|
+
end
|
379
|
+
|
380
|
+
# Complete destination.
|
381
|
+
#def copylist_dest(list)
|
382
|
+
# list.collect do |src, dest|
|
383
|
+
# case dest
|
384
|
+
# when nil
|
385
|
+
# dest = src
|
386
|
+
# when '/.'
|
387
|
+
# dest = src
|
388
|
+
# when /\/[.]$/
|
389
|
+
# dest = File.join(dest.chomp('/.'), src)
|
390
|
+
# when '/', '.'
|
391
|
+
# dest = File.basename(src)
|
392
|
+
# when /\/$/
|
393
|
+
# #dest = File.join(dest, template_to_filename(File.basename(src)))
|
394
|
+
# dest = File.join(dest, File.basename(src))
|
395
|
+
# #else
|
396
|
+
# # dest = dest
|
397
|
+
# end
|
398
|
+
# dest = template_to_filename(dest)
|
399
|
+
# [src, dest]
|
400
|
+
# end
|
401
|
+
#end
|
402
|
+
|
403
|
+
###
|
404
|
+
#def erb(file)
|
405
|
+
# text = nil
|
406
|
+
# temp = Context.new(plugin)
|
407
|
+
# begin
|
408
|
+
# text = temp.erb(file)
|
409
|
+
# rescue => e
|
410
|
+
# if trace?
|
411
|
+
# raise e
|
412
|
+
# else
|
413
|
+
# abort "template error -- #{file}"
|
414
|
+
# end
|
415
|
+
# end
|
416
|
+
# return text
|
417
|
+
#end
|
418
|
+
|
419
|
+
end
|
420
|
+
|
421
|
+
end#module Sow
|
422
|
+
|
423
|
+
|
424
|
+
|
425
|
+
|
426
|
+
|
427
|
+
=begin
|
428
|
+
#
|
429
|
+
def create(arguments, options={})
|
430
|
+
setup_for(:create, arguments, options)
|
431
|
+
|
432
|
+
#setup_arguments #(arguments)
|
433
|
+
#session.mode = :create
|
434
|
+
|
435
|
+
# collect metadata settings
|
436
|
+
#plugin.script.metadata.each do |k,v|
|
437
|
+
# metadata[k] = v
|
438
|
+
#end
|
439
|
+
|
440
|
+
# this is kind of odd here
|
441
|
+
#plugin.script_setup(session)
|
442
|
+
|
443
|
+
#session = Session.new(arguments, options)
|
444
|
+
generator = Generators::Create.new(session, location, copylist)
|
445
|
+
generator.generate
|
446
|
+
end
|
447
|
+
|
448
|
+
#
|
449
|
+
def update(arguments, options={})
|
450
|
+
setup_for(:update, arguments, options)
|
451
|
+
#setup_arguments #(arguments)
|
452
|
+
#session.mode = :update
|
453
|
+
|
454
|
+
#session = Session.new(arguments, options)
|
455
|
+
generator = Generators::Update.new(session, location, copylist)
|
456
|
+
generator.generate
|
457
|
+
end
|
458
|
+
|
459
|
+
#
|
460
|
+
def delete(arguments, options={})
|
461
|
+
setup_for(:delete, arguments, options)
|
462
|
+
#setup_arguments #(arguments)
|
463
|
+
#session.mode = :delete
|
464
|
+
#session = Session.new(arguments, options)
|
465
|
+
generator = Generators::Delete.new(session, location, copylist)
|
466
|
+
generator.generate
|
467
|
+
end
|
468
|
+
|
469
|
+
# No specific operation mode given, select one
|
470
|
+
# based on plugin and state of current location.
|
471
|
+
def select(arguments, options={})
|
472
|
+
#setup_arguments #(arguments) # FIXME
|
473
|
+
#session = Session.new(arguments, options)
|
474
|
+
if name && session.sowed? #FIXME name?
|
475
|
+
setup_for(:update, arguments, options)
|
476
|
+
generator = Generators::Update.new(session, location, copylist)
|
477
|
+
generator.generate
|
478
|
+
else
|
479
|
+
setup_for(:create, arguments, options)
|
480
|
+
generator = Generators::Create.new(session, location, copylist)
|
481
|
+
generator.generate
|
482
|
+
end
|
483
|
+
end
|
484
|
+
=end
|