TwP-bones 2.3.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/History.txt +183 -0
- data/README.rdoc +370 -0
- data/Rakefile +59 -0
- data/bin/bones +7 -0
- data/data/.bnsignore +12 -0
- data/data/History.txt.bns +4 -0
- data/data/README.txt.bns +48 -0
- data/data/Rakefile.bns +30 -0
- data/data/bin/NAME.bns +8 -0
- data/data/lib/NAME.rb.bns +49 -0
- data/data/spec/NAME_spec.rb.bns +7 -0
- data/data/spec/spec_helper.rb.bns +16 -0
- data/data/test/test_NAME.rb +0 -0
- data/lib/bones/annotation_extractor.rb +111 -0
- data/lib/bones/app/command.rb +130 -0
- data/lib/bones/app/create_command.rb +86 -0
- data/lib/bones/app/file_manager.rb +176 -0
- data/lib/bones/app/freeze_command.rb +72 -0
- data/lib/bones/app/info_command.rb +58 -0
- data/lib/bones/app/unfreeze_command.rb +53 -0
- data/lib/bones/app/update_command.rb +47 -0
- data/lib/bones/app.rb +92 -0
- data/lib/bones/debug.rb +71 -0
- data/lib/bones/smtp_tls.rb +75 -0
- data/lib/bones/tasks/ann.rake +80 -0
- data/lib/bones/tasks/bones.rake +20 -0
- data/lib/bones/tasks/gem.rake +201 -0
- data/lib/bones/tasks/git.rake +40 -0
- data/lib/bones/tasks/notes.rake +27 -0
- data/lib/bones/tasks/post_load.rake +34 -0
- data/lib/bones/tasks/rdoc.rake +50 -0
- data/lib/bones/tasks/rubyforge.rake +55 -0
- data/lib/bones/tasks/setup.rb +296 -0
- data/lib/bones/tasks/spec.rake +54 -0
- data/lib/bones/tasks/svn.rake +47 -0
- data/lib/bones/tasks/test.rake +40 -0
- data/lib/bones.rb +62 -0
- data/spec/bones/app/file_manager_spec.rb +150 -0
- data/spec/bones/app_spec.rb +97 -0
- data/spec/bones_spec.rb +18 -0
- data/spec/data/data/NAME/NAME.rb.bns +5 -0
- data/spec/data/data/README.txt.bns +48 -0
- data/spec/data/data/Rakefile.bns +30 -0
- data/spec/data/data/lib/NAME.rb.bns +49 -0
- data/spec/data/foo/README.txt +0 -0
- data/spec/data/foo/Rakefile +0 -0
- data/spec/spec_helper.rb +44 -0
- metadata +126 -0
data/data/Rakefile.bns
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
|
3
|
+
# are where the options are used.
|
|
4
|
+
|
|
5
|
+
begin
|
|
6
|
+
require 'bones'
|
|
7
|
+
Bones.setup
|
|
8
|
+
rescue LoadError
|
|
9
|
+
begin
|
|
10
|
+
load 'tasks/setup.rb'
|
|
11
|
+
rescue LoadError
|
|
12
|
+
raise RuntimeError, '### please install the "bones" gem ###'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
ensure_in_path 'lib'
|
|
17
|
+
require '<%= name %>'
|
|
18
|
+
|
|
19
|
+
task :default => 'spec:run'
|
|
20
|
+
|
|
21
|
+
PROJ.name = '<%= name %>'
|
|
22
|
+
PROJ.authors = 'FIXME (who is writing this software)'
|
|
23
|
+
PROJ.email = 'FIXME (your e-mail)'
|
|
24
|
+
PROJ.url = 'FIXME (project homepage)'
|
|
25
|
+
PROJ.version = <%= classname %>::VERSION
|
|
26
|
+
PROJ.rubyforge.name = '<%= name %>'
|
|
27
|
+
|
|
28
|
+
PROJ.spec.opts << '--color'
|
|
29
|
+
|
|
30
|
+
# EOF
|
data/data/bin/NAME.bns
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
module <%= classname %>
|
|
3
|
+
|
|
4
|
+
# :stopdoc:
|
|
5
|
+
VERSION = '1.0.0'
|
|
6
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
|
7
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
|
8
|
+
# :startdoc:
|
|
9
|
+
|
|
10
|
+
# Returns the version string for the library.
|
|
11
|
+
#
|
|
12
|
+
def self.version
|
|
13
|
+
VERSION
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns the library path for the module. If any arguments are given,
|
|
17
|
+
# they will be joined to the end of the libray path using
|
|
18
|
+
# <tt>File.join</tt>.
|
|
19
|
+
#
|
|
20
|
+
def self.libpath( *args )
|
|
21
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns the lpath for the module. If any arguments are given,
|
|
25
|
+
# they will be joined to the end of the path using
|
|
26
|
+
# <tt>File.join</tt>.
|
|
27
|
+
#
|
|
28
|
+
def self.path( *args )
|
|
29
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Utility method used to rquire all files ending in .rb that lie in the
|
|
33
|
+
# directory below this file that has the same name as the filename passed
|
|
34
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
|
35
|
+
# the _filename_ does not have to be equivalent to the directory.
|
|
36
|
+
#
|
|
37
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
|
38
|
+
dir ||= ::File.basename(fname, '.*')
|
|
39
|
+
search_me = ::File.expand_path(
|
|
40
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
|
41
|
+
|
|
42
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end # module <%= classname %>
|
|
46
|
+
|
|
47
|
+
<%= classname %>.require_all_libs_relative_to(__FILE__)
|
|
48
|
+
|
|
49
|
+
# EOF
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
require File.expand_path(
|
|
3
|
+
File.join(File.dirname(__FILE__), %w[.. lib <%= name %>]))
|
|
4
|
+
|
|
5
|
+
Spec::Runner.configure do |config|
|
|
6
|
+
# == Mock Framework
|
|
7
|
+
#
|
|
8
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
|
9
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
|
10
|
+
#
|
|
11
|
+
# config.mock_with :mocha
|
|
12
|
+
# config.mock_with :flexmock
|
|
13
|
+
# config.mock_with :rr
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# EOF
|
|
File without changes
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
|
|
2
|
+
begin
|
|
3
|
+
require 'facets/ansicode'
|
|
4
|
+
HAVE_COLOR = true
|
|
5
|
+
rescue LoadError
|
|
6
|
+
HAVE_COLOR = false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module Bones
|
|
10
|
+
|
|
11
|
+
# A helper class used to find and display any annotations in a collection of
|
|
12
|
+
# project files.
|
|
13
|
+
#
|
|
14
|
+
class AnnotationExtractor
|
|
15
|
+
|
|
16
|
+
class Annotation < Struct.new(:line, :tag, :text)
|
|
17
|
+
# Returns a string representation of the annotation. If the
|
|
18
|
+
# <tt>:tag</tt> parameter is given as +true+, then the annotation tag
|
|
19
|
+
# will be included in the string.
|
|
20
|
+
#
|
|
21
|
+
def to_s( opts = {} )
|
|
22
|
+
s = "[%3d] " % line
|
|
23
|
+
s << "[#{tag}] " if opts[:tag]
|
|
24
|
+
s << text
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Enumerate all the annoations for the given _project_ and _tag_. This
|
|
29
|
+
# will search for all athe annotations and display them on standard
|
|
30
|
+
# output.
|
|
31
|
+
#
|
|
32
|
+
def self.enumerate( project, tag, id = nil, opts = {} )
|
|
33
|
+
extractor = new(project, tag, id)
|
|
34
|
+
extractor.display(extractor.find, opts)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :tag, :project, :id
|
|
38
|
+
|
|
39
|
+
# Creates a new annotation extractor configured to use the _project_ open
|
|
40
|
+
# strcut and to search for the given _tag_ (which can be more than one tag
|
|
41
|
+
# via a regular expression 'or' operation -- i.e. THIS|THAT|OTHER)
|
|
42
|
+
#
|
|
43
|
+
def initialize( project, tag, id)
|
|
44
|
+
@project = project
|
|
45
|
+
@tag = tag
|
|
46
|
+
@id = @id_rgxp = nil
|
|
47
|
+
|
|
48
|
+
unless id.nil? or id.empty?
|
|
49
|
+
@id = id
|
|
50
|
+
@id_rgxp = Regexp.new(Regexp.escape(id), Regexp::IGNORECASE)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Iterate over all the files in the project and extract annotations from
|
|
55
|
+
# the those files. Returns the results as a hash for display.
|
|
56
|
+
#
|
|
57
|
+
def find
|
|
58
|
+
results = {}
|
|
59
|
+
rgxp = %r/(#{tag}):?\s*(.*?)(?:\s*(?:-?%>|\*+\/))?$/o
|
|
60
|
+
|
|
61
|
+
extensions = project.notes.extensions.dup
|
|
62
|
+
exclude = if project.notes.exclude.empty? then nil
|
|
63
|
+
else Regexp.new(project.notes.exclude.join('|')) end
|
|
64
|
+
|
|
65
|
+
project.gem.files.each do |fn|
|
|
66
|
+
next if exclude && exclude =~ fn
|
|
67
|
+
next unless extensions.include? File.extname(fn)
|
|
68
|
+
results.update(extract_annotations_from(fn, rgxp))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
results
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Extract any annotations from the given _file_ using the regular
|
|
75
|
+
# expression _pattern_ provided.
|
|
76
|
+
#
|
|
77
|
+
def extract_annotations_from( file, pattern )
|
|
78
|
+
lineno = 0
|
|
79
|
+
result = File.readlines(file).inject([]) do |list, line|
|
|
80
|
+
lineno += 1
|
|
81
|
+
next list unless m = pattern.match(line)
|
|
82
|
+
next list << Annotation.new(lineno, m[1], m[2]) unless id
|
|
83
|
+
|
|
84
|
+
text = m[2]
|
|
85
|
+
if text =~ @id_rgxp
|
|
86
|
+
text.gsub!(@id_rgxp) {|str| ANSICode.green(str)} if HAVE_COLOR
|
|
87
|
+
list << Annotation.new(lineno, m[1], text)
|
|
88
|
+
end
|
|
89
|
+
list
|
|
90
|
+
end
|
|
91
|
+
result.empty? ? {} : { file => result }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Print the results of the annotation extraction to the screen. If the
|
|
95
|
+
# <tt>:tags</tt> option is set to +true+, then the annotation tag will be
|
|
96
|
+
# displayed.
|
|
97
|
+
#
|
|
98
|
+
def display( results, opts = {} )
|
|
99
|
+
results.keys.sort.each do |file|
|
|
100
|
+
puts "#{file}:"
|
|
101
|
+
results[file].each do |note|
|
|
102
|
+
puts " * #{note.to_s(opts)}"
|
|
103
|
+
end
|
|
104
|
+
puts
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end # class AnnotationExtractor
|
|
109
|
+
end # module Bones
|
|
110
|
+
|
|
111
|
+
# EOF
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
|
|
2
|
+
module Bones
|
|
3
|
+
class App
|
|
4
|
+
|
|
5
|
+
class Command
|
|
6
|
+
|
|
7
|
+
attr_reader :options
|
|
8
|
+
|
|
9
|
+
def initialize( out = STDOUT, err = STDERR )
|
|
10
|
+
@out = out
|
|
11
|
+
@err = err
|
|
12
|
+
@options = {
|
|
13
|
+
:skeleton_dir => File.join(mrbones_dir, 'data'),
|
|
14
|
+
:with_tasks => false,
|
|
15
|
+
:verbose => false,
|
|
16
|
+
:name => nil,
|
|
17
|
+
:output_dir => nil
|
|
18
|
+
}
|
|
19
|
+
@options[:skeleton_dir] = ::Bones.path('data') unless test(?d, skeleton_dir)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run( args )
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The output directory where files will be written.
|
|
27
|
+
#
|
|
28
|
+
def output_dir
|
|
29
|
+
options[:output_dir]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The directory where the project skeleton is located.
|
|
33
|
+
#
|
|
34
|
+
def skeleton_dir
|
|
35
|
+
options[:skeleton_dir]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The project name from the command line.
|
|
39
|
+
#
|
|
40
|
+
def name
|
|
41
|
+
options[:name]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A git or svn repository URL from the command line.
|
|
45
|
+
#
|
|
46
|
+
def repository
|
|
47
|
+
return options[:repository] if options.has_key? :repository
|
|
48
|
+
return IO.read(skeleton_dir).strip if skeleton_dir and test(?f, skeleton_dir)
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns +true+ if we are going to copy the Mr Bones tasks into the
|
|
53
|
+
# destination directory. Normally this will return +false+.
|
|
54
|
+
#
|
|
55
|
+
def with_tasks?
|
|
56
|
+
options[:with_tasks]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
#
|
|
61
|
+
def copy_tasks( to )
|
|
62
|
+
fm = FileManager.new(
|
|
63
|
+
:source => ::Bones.path(%w[lib bones tasks]),
|
|
64
|
+
:destination => to,
|
|
65
|
+
:stdout => @out,
|
|
66
|
+
:stderr => @err,
|
|
67
|
+
:verbose => verbose?
|
|
68
|
+
)
|
|
69
|
+
fm.archive_destination
|
|
70
|
+
fm.copy
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Returns +true+ if the user has requested verbose messages.
|
|
74
|
+
#
|
|
75
|
+
def verbose?
|
|
76
|
+
options[:verbose]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns the .bones resource directory in the user's home directory.
|
|
80
|
+
#
|
|
81
|
+
def mrbones_dir
|
|
82
|
+
return @mrbones_dir if defined? @mrbones_dir
|
|
83
|
+
|
|
84
|
+
path = (::Bones::WIN32 ? ENV['HOMEPATH'].tr("\\", "/") : ENV['HOME'])
|
|
85
|
+
path = File.join(path, '.mrbones')
|
|
86
|
+
@mrbones_dir = File.expand_path(path)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
#
|
|
90
|
+
#
|
|
91
|
+
def standard_options
|
|
92
|
+
{
|
|
93
|
+
:verbose => ['-v', '--verbose', 'enable verbose output',
|
|
94
|
+
lambda {
|
|
95
|
+
options[:verbose] = true
|
|
96
|
+
}],
|
|
97
|
+
:directory => ['-d', '--directory DIRECTORY', String,
|
|
98
|
+
'project directory to create', '(defaults to project_name)',
|
|
99
|
+
lambda { |value|
|
|
100
|
+
options[:output_dir] = value
|
|
101
|
+
}],
|
|
102
|
+
:skeleton => ['-s', '--skeleton NAME', String,
|
|
103
|
+
'project skeleton to use',
|
|
104
|
+
lambda { |value|
|
|
105
|
+
path = File.join(mrbones_dir, value)
|
|
106
|
+
if test(?e, path)
|
|
107
|
+
options[:skeleton_dir] = path
|
|
108
|
+
elsif test(?e, value)
|
|
109
|
+
options[:skeleton_dir] = value
|
|
110
|
+
else
|
|
111
|
+
raise ArgumentError, "Unknown skeleton '#{value}'"
|
|
112
|
+
end
|
|
113
|
+
}],
|
|
114
|
+
:with_tasks => ['--with-tasks', 'copy rake tasks to the project folder',
|
|
115
|
+
lambda {
|
|
116
|
+
options[:with_tasks] = true
|
|
117
|
+
}],
|
|
118
|
+
:repository => ['-r', '--repository URL', String,
|
|
119
|
+
'svn or git repository path',
|
|
120
|
+
lambda { |value|
|
|
121
|
+
options[:repository] = value
|
|
122
|
+
}]
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
end # class Command
|
|
127
|
+
end # class App
|
|
128
|
+
end # module Bones
|
|
129
|
+
|
|
130
|
+
# EOF
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
module Bones
|
|
3
|
+
class App
|
|
4
|
+
|
|
5
|
+
class CreateCommand < Command
|
|
6
|
+
|
|
7
|
+
def run( args )
|
|
8
|
+
parse args
|
|
9
|
+
|
|
10
|
+
fm = FileManager.new(
|
|
11
|
+
:source => repository || skeleton_dir,
|
|
12
|
+
:destination => output_dir,
|
|
13
|
+
:stdout => @out,
|
|
14
|
+
:stderr => @err,
|
|
15
|
+
:verbose => verbose?
|
|
16
|
+
)
|
|
17
|
+
raise "Output directory already exists #{output_dir.inspect}" if test(?e, fm.destination)
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
fm.copy
|
|
21
|
+
copy_tasks(File.join(output_dir, 'tasks')) if with_tasks?
|
|
22
|
+
fm.finalize name
|
|
23
|
+
|
|
24
|
+
pwd = File.expand_path(FileUtils.pwd)
|
|
25
|
+
msg = "Created '#{name}'"
|
|
26
|
+
msg << " in directory '#{output_dir}'" if name != output_dir
|
|
27
|
+
@out.puts msg
|
|
28
|
+
|
|
29
|
+
if test(?f, File.join(output_dir, 'Rakefile'))
|
|
30
|
+
begin
|
|
31
|
+
FileUtils.cd output_dir
|
|
32
|
+
@out.puts "Now you need to fix these files"
|
|
33
|
+
system "rake notes"
|
|
34
|
+
ensure
|
|
35
|
+
FileUtils.cd pwd
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
rescue Exception => err
|
|
39
|
+
FileUtils.rm_rf output_dir
|
|
40
|
+
msg = "Could not create '#{name}'"
|
|
41
|
+
msg << " in directory '#{output_dir}'" if name != output_dir
|
|
42
|
+
raise msg
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse( args )
|
|
47
|
+
std_opts = standard_options
|
|
48
|
+
|
|
49
|
+
opts = OptionParser.new
|
|
50
|
+
opts.banner = 'Usage: bones create [options] <project_name>'
|
|
51
|
+
|
|
52
|
+
opts.separator ''
|
|
53
|
+
opts.separator " Create a new project from a Mr Bones project skeleton. The skeleton can"
|
|
54
|
+
opts.separator " be the default project skeleton from the Mr Bones gem or one of the named"
|
|
55
|
+
opts.separator " skeletons found in the '~/.mrbones/' folder. A git or svn repository can"
|
|
56
|
+
opts.separator " be used as the skeleton if the '--repository' flag is given."
|
|
57
|
+
|
|
58
|
+
opts.separator ''
|
|
59
|
+
opts.on(*std_opts[:directory])
|
|
60
|
+
opts.on(*std_opts[:skeleton])
|
|
61
|
+
opts.on(*std_opts[:repository])
|
|
62
|
+
opts.on(*std_opts[:with_tasks])
|
|
63
|
+
|
|
64
|
+
opts.separator ''
|
|
65
|
+
opts.separator ' Common Options:'
|
|
66
|
+
opts.on_tail( '-h', '--help', 'show this message' ) {
|
|
67
|
+
@out.puts opts
|
|
68
|
+
exit
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# parse the command line arguments
|
|
72
|
+
opts.parse! args
|
|
73
|
+
options[:name] = args.empty? ? nil : args.join('_')
|
|
74
|
+
|
|
75
|
+
if name.nil?
|
|
76
|
+
@out.puts opts
|
|
77
|
+
exit 1
|
|
78
|
+
end
|
|
79
|
+
options[:output_dir] = name if output_dir.nil?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end # class CreateCommand
|
|
83
|
+
end # class App
|
|
84
|
+
end # module Bones
|
|
85
|
+
|
|
86
|
+
# EOF
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module Bones
|
|
5
|
+
class App
|
|
6
|
+
|
|
7
|
+
class FileManager
|
|
8
|
+
|
|
9
|
+
attr_accessor :source, :destination, :archive, :verbose
|
|
10
|
+
alias :verbose? :verbose
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
#
|
|
14
|
+
def initialize( opts = {} )
|
|
15
|
+
self.source = opts[:source]
|
|
16
|
+
self.destination = opts[:destination]
|
|
17
|
+
self.verbose = opts[:verbose]
|
|
18
|
+
|
|
19
|
+
@out = opts[:stdout] || $stdout
|
|
20
|
+
@err = opts[:stderr] || $stderr
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Sets the destination where files will be copied to. At the same time an
|
|
24
|
+
# archive directory is configured. This is simply the destination
|
|
25
|
+
# directory with a '.archive' extension.
|
|
26
|
+
#
|
|
27
|
+
def destination=( str )
|
|
28
|
+
@destination = str
|
|
29
|
+
@archive = str + '.archive' if str
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# If the source is a repository this method returns the type of
|
|
33
|
+
# repository. This will be :git for Git repositories and :svn for
|
|
34
|
+
# Subversion repositories. Otherwise, +nil+ is returned.
|
|
35
|
+
#
|
|
36
|
+
def repository
|
|
37
|
+
return :git if source =~ %r/\.git\/?$/i
|
|
38
|
+
return :svn if source =~ %r/^(svn(\+ssh)?|https?|file):\/\//i
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
alias :repository? :repository
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
#
|
|
45
|
+
def archive_destination
|
|
46
|
+
return false unless test(?e, destination)
|
|
47
|
+
|
|
48
|
+
@out.puts "archiving #{destination}" if verbose?
|
|
49
|
+
FileUtils.rm_rf(archive)
|
|
50
|
+
FileUtils.mv(destination, archive)
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
#
|
|
56
|
+
def copy
|
|
57
|
+
if repository?
|
|
58
|
+
_checkout(repository)
|
|
59
|
+
else
|
|
60
|
+
_files_to_copy.each {|fn| _cp(fn)}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
#
|
|
66
|
+
def finalize( name )
|
|
67
|
+
name = name.to_s
|
|
68
|
+
return if name.empty?
|
|
69
|
+
|
|
70
|
+
self.destination = _rename(destination, name)
|
|
71
|
+
_erb(name)
|
|
72
|
+
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#
|
|
77
|
+
#
|
|
78
|
+
def _checkout( repotype )
|
|
79
|
+
case repotype
|
|
80
|
+
when :git
|
|
81
|
+
system('git-clone', source, destination)
|
|
82
|
+
FileUtils.rm_rf(File.join(destination, '.git'))
|
|
83
|
+
when :svn
|
|
84
|
+
system('svn', 'export', source, destination)
|
|
85
|
+
else
|
|
86
|
+
raise "unknown repository type '#{repotype}'"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
#
|
|
92
|
+
def _rename( filename, name )
|
|
93
|
+
newname = filename.gsub(%r/NAME/, name)
|
|
94
|
+
|
|
95
|
+
if filename != newname
|
|
96
|
+
raise "cannot rename '#{filename}' to '#{newname}' - file already exists" if test(?e, newname)
|
|
97
|
+
FileUtils.mv(filename, newname)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if test(?d, newname)
|
|
101
|
+
Dir.glob(File.join(newname, '*')).each {|fn| _rename(fn, name)}
|
|
102
|
+
end
|
|
103
|
+
newname
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
#
|
|
107
|
+
#
|
|
108
|
+
def _erb( name )
|
|
109
|
+
binding = _erb_binding(name)
|
|
110
|
+
|
|
111
|
+
Dir.glob(File.join(destination, '**', '*')).each do |fn|
|
|
112
|
+
next unless test(?f, fn) and '.bns' == File.extname(fn)
|
|
113
|
+
|
|
114
|
+
txt = ERB.new(File.read(fn), nil, '-').result(binding)
|
|
115
|
+
File.open(fn.sub(%r/\.bns$/, ''), 'w') {|fd| fd.write(txt)}
|
|
116
|
+
FileUtils.rm_f(fn)
|
|
117
|
+
end
|
|
118
|
+
self
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
#
|
|
122
|
+
#
|
|
123
|
+
def _erb_binding( name )
|
|
124
|
+
obj = Object.new
|
|
125
|
+
class << obj
|
|
126
|
+
alias :__binding__ :binding
|
|
127
|
+
instance_methods.each do |m|
|
|
128
|
+
undef_method m unless m[%r/^__/]
|
|
129
|
+
end
|
|
130
|
+
attr_accessor :name, :classname
|
|
131
|
+
end
|
|
132
|
+
obj.name = name
|
|
133
|
+
obj.classname = name.tr('-','_').split('_').map {|x| x.capitalize}.join
|
|
134
|
+
obj.__send__(:__binding__)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Returns a list of the files to copy from the source directory to
|
|
138
|
+
# the destination directory.
|
|
139
|
+
#
|
|
140
|
+
def _files_to_copy
|
|
141
|
+
rgxp = %r/\A#{source}\/?/
|
|
142
|
+
exclude = %r/tmp$|bak$|~$|CVS|\.svn/
|
|
143
|
+
|
|
144
|
+
ary = Dir.glob(File.join(source, '**', '*.*'), File::FNM_DOTMATCH).map do |filename|
|
|
145
|
+
next if exclude =~ filename
|
|
146
|
+
next if test(?d, filename)
|
|
147
|
+
filename.sub rgxp, ''
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
ary.compact!
|
|
151
|
+
ary.sort!
|
|
152
|
+
ary
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Copy a file from the Bones prototype project location to the user
|
|
156
|
+
# specified project location. A message will be displayed to the screen
|
|
157
|
+
# indicating that the file is being created.
|
|
158
|
+
#
|
|
159
|
+
def _cp( file )
|
|
160
|
+
dir = File.dirname(file)
|
|
161
|
+
dir = (dir == '.' ? destination : File.join(destination, dir))
|
|
162
|
+
dst = File.join(dir, File.basename(file))
|
|
163
|
+
src = File.join(source, file)
|
|
164
|
+
|
|
165
|
+
@out.puts(test(?e, dst) ? "updating #{dst}" : "creating #{dst}") if verbose?
|
|
166
|
+
FileUtils.mkdir_p(dir)
|
|
167
|
+
FileUtils.cp src, dst
|
|
168
|
+
|
|
169
|
+
FileUtils.chmod(File.stat(src).mode, dst)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
end # class FileManager
|
|
173
|
+
end # class App
|
|
174
|
+
end # module Bones
|
|
175
|
+
|
|
176
|
+
# EOF
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
module Bones
|
|
3
|
+
class App
|
|
4
|
+
|
|
5
|
+
class FreezeCommand < Command
|
|
6
|
+
|
|
7
|
+
def run( args )
|
|
8
|
+
parse args
|
|
9
|
+
|
|
10
|
+
fm = FileManager.new(
|
|
11
|
+
:source => repository || ::Bones.path('data'),
|
|
12
|
+
:destination => output_dir,
|
|
13
|
+
:stdout => @out,
|
|
14
|
+
:stderr => @err,
|
|
15
|
+
:verbose => verbose?
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
fm.archive_destination
|
|
19
|
+
return freeze_to_repository if repository
|
|
20
|
+
|
|
21
|
+
fm.copy
|
|
22
|
+
copy_tasks(File.join(output_dir, 'tasks')) if with_tasks?
|
|
23
|
+
|
|
24
|
+
@out.puts "Project skeleton #{name.inspect} " <<
|
|
25
|
+
"has been frozen to Mr Bones #{::Bones::VERSION}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def parse( args )
|
|
29
|
+
std_opts = standard_options
|
|
30
|
+
|
|
31
|
+
opts = OptionParser.new
|
|
32
|
+
opts.banner = 'Usage: bones freeze [options] [skeleton_name]'
|
|
33
|
+
|
|
34
|
+
opts.separator ''
|
|
35
|
+
opts.separator ' Freeze the project skeleton to the current Mr Bones project skeleton.'
|
|
36
|
+
opts.separator ' If a name is not given, then the default name "data" will be used.'
|
|
37
|
+
opts.separator ' Optionally a git or svn repository can be frozen as the project'
|
|
38
|
+
opts.separator ' skeleton.'
|
|
39
|
+
|
|
40
|
+
opts.separator ''
|
|
41
|
+
opts.on(*std_opts[:repository])
|
|
42
|
+
opts.on(*std_opts[:with_tasks])
|
|
43
|
+
|
|
44
|
+
opts.separator ''
|
|
45
|
+
opts.separator ' Common Options:'
|
|
46
|
+
opts.on_tail( '-h', '--help', 'show this message' ) {
|
|
47
|
+
@out.puts opts
|
|
48
|
+
exit
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# parse the command line arguments
|
|
52
|
+
opts.parse! args
|
|
53
|
+
options[:name] = args.empty? ? 'data' : args.join('_')
|
|
54
|
+
options[:output_dir] = File.join(mrbones_dir, name)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Freeze the project skeleton to the git or svn repository that the user
|
|
58
|
+
# passed in on the command line. This essentially creates an alias to the
|
|
59
|
+
# reposiory using the name passed in on the command line.
|
|
60
|
+
#
|
|
61
|
+
def freeze_to_repository
|
|
62
|
+
File.open(output_dir, 'w') {|fd| fd.puts repository}
|
|
63
|
+
@out.puts "Project skeleton #{name.inspect} " <<
|
|
64
|
+
"has been frozen to #{repository.inspect}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
end # class FreezeCommand
|
|
69
|
+
end # class App
|
|
70
|
+
end # module Bones
|
|
71
|
+
|
|
72
|
+
# EOF
|