sow 0.4.0
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/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
|