bones 2.1.1 → 2.2.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 +12 -0
- data/Manifest.txt +22 -11
- data/README.rdoc +34 -10
- data/Rakefile +2 -2
- data/bin/bones +1 -1
- data/bones.gemspec +40 -0
- data/data/{History.txt.erb → History.txt.bns} +0 -0
- data/data/{README.txt.erb → README.txt.bns} +0 -0
- data/data/{Rakefile.erb → Rakefile.bns} +5 -1
- data/data/bin/{NAME.erb → NAME.bns} +0 -0
- data/data/lib/NAME.rb.bns +49 -0
- data/data/spec/{NAME_spec.rb.erb → NAME_spec.rb.bns} +0 -0
- data/data/spec/{spec_helper.rb.erb → spec_helper.rb.bns} +0 -0
- data/lib/bones.rb +3 -1
- data/lib/bones/app.rb +92 -0
- data/lib/bones/app/command.rb +130 -0
- data/lib/bones/app/create_command.rb +87 -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/tasks/gem.rake +11 -2
- data/spec/bones/app/file_manager_spec.rb +150 -0
- data/spec/bones/app_spec.rb +97 -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/{data/lib/NAME.rb.erb → spec/data/data/lib/NAME.rb.bns} +0 -0
- data/spec/spec_helper.rb +22 -0
- data/tasks/gem.rake +11 -2
- metadata +24 -14
- data/lib/bones/main.rb +0 -380
- data/spec/bones/main_spec.rb +0 -157
- data/spec/data/data/README.txt +0 -0
- data/spec/data/data/Rakefile +0 -0
@@ -0,0 +1,87 @@
|
|
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
|
+
system "rake manifest:create 2>&1 > #{::Bones::DEV_NULL}"
|
33
|
+
@out.puts "Now you need to fix these files"
|
34
|
+
system "rake notes"
|
35
|
+
ensure
|
36
|
+
FileUtils.cd pwd
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue Exception => err
|
40
|
+
FileUtils.rm_rf output_dir
|
41
|
+
msg = "Could not create '#{name}'"
|
42
|
+
msg << " in directory '#{output_dir}'" if name != output_dir
|
43
|
+
raise msg
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse( args )
|
48
|
+
std_opts = standard_options
|
49
|
+
|
50
|
+
opts = OptionParser.new
|
51
|
+
opts.banner = 'Usage: bones create [options] <project_name>'
|
52
|
+
|
53
|
+
opts.separator ''
|
54
|
+
opts.separator " Create a new project from a Mr Bones project skeleton. The skeleton can"
|
55
|
+
opts.separator " be the default project skeleton from the Mr Bones gem or one of the named"
|
56
|
+
opts.separator " skeletons found in the '~/.mrbones/' folder. A git or svn repository can"
|
57
|
+
opts.separator " be used as the skeleton if the '--repository' flag is given."
|
58
|
+
|
59
|
+
opts.separator ''
|
60
|
+
opts.on(*std_opts[:directory])
|
61
|
+
opts.on(*std_opts[:skeleton])
|
62
|
+
opts.on(*std_opts[:repository])
|
63
|
+
opts.on(*std_opts[:with_tasks])
|
64
|
+
|
65
|
+
opts.separator ''
|
66
|
+
opts.separator ' Common Options:'
|
67
|
+
opts.on_tail( '-h', '--help', 'show this message' ) {
|
68
|
+
@out.puts opts
|
69
|
+
exit
|
70
|
+
}
|
71
|
+
|
72
|
+
# parse the command line arguments
|
73
|
+
opts.parse! args
|
74
|
+
options[:name] = args.empty? ? nil : args.join('_')
|
75
|
+
|
76
|
+
if name.nil?
|
77
|
+
@out.puts opts
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
options[:output_dir] = name if output_dir.nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
end # class CreateCommand
|
84
|
+
end # class App
|
85
|
+
end # module Bones
|
86
|
+
|
87
|
+
# 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, '**', '*')).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
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
module Bones
|
3
|
+
class App
|
4
|
+
|
5
|
+
class InfoCommand < Command
|
6
|
+
|
7
|
+
def run( args )
|
8
|
+
parse args
|
9
|
+
|
10
|
+
skeleton_dir = File.join(mrbones_dir, 'data')
|
11
|
+
skeleton_dir = ::Bones.path('data') unless test(?d, skeleton_dir)
|
12
|
+
|
13
|
+
msg = "\n"
|
14
|
+
msg << "The default project skeleton will be copied from:\n"
|
15
|
+
msg << " " << skeleton_dir << "\n\n"
|
16
|
+
|
17
|
+
fmt = " %-12s => %s\n"
|
18
|
+
msg << "Available projects skeletons are:\n"
|
19
|
+
Dir.glob(File.join(mrbones_dir, '*')).sort.each do |fn|
|
20
|
+
next if fn =~ %r/\.archive$/
|
21
|
+
next if File.basename(fn) == 'data'
|
22
|
+
|
23
|
+
if test(?f, fn)
|
24
|
+
msg << fmt % [File.basename(fn), File.read(fn).strip]
|
25
|
+
else
|
26
|
+
msg << " " << File.basename(fn) << "\n"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@out.puts msg
|
31
|
+
@out.puts
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse( args )
|
35
|
+
std_opts = standard_options
|
36
|
+
|
37
|
+
opts = OptionParser.new
|
38
|
+
opts.banner = 'Usage: bones info'
|
39
|
+
|
40
|
+
opts.separator ''
|
41
|
+
opts.separator ' Shows information about available skeletons'
|
42
|
+
|
43
|
+
opts.separator ''
|
44
|
+
opts.separator ' Common Options:'
|
45
|
+
opts.on_tail( '-h', '--help', 'show this message' ) {
|
46
|
+
@out.puts opts
|
47
|
+
exit
|
48
|
+
}
|
49
|
+
|
50
|
+
# parse the command line arguments
|
51
|
+
opts.parse! args
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class InfoCommand
|
55
|
+
end # class App
|
56
|
+
end # module Bones
|
57
|
+
|
58
|
+
# EOF
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
module Bones
|
3
|
+
class App
|
4
|
+
|
5
|
+
class UnfreezeCommand < 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
|
+
if fm.archive_destination
|
19
|
+
@out.puts "Project skeleton #{name.inspect} has been unfrozen"
|
20
|
+
else
|
21
|
+
@out.puts "Project skeleton #{name.inspect} is not frozen " <<
|
22
|
+
"(no action taken)"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse( args )
|
27
|
+
std_opts = standard_options
|
28
|
+
|
29
|
+
opts = OptionParser.new
|
30
|
+
opts.banner = 'Usage: bones unfreeze [skeleton_name]'
|
31
|
+
|
32
|
+
opts.separator ''
|
33
|
+
opts.separator " Removes the named skeleton from the '~/.mrbones/' folder. If a name is"
|
34
|
+
opts.separator " not given then the default skeleton is removed."
|
35
|
+
|
36
|
+
opts.separator ''
|
37
|
+
opts.separator ' Common Options:'
|
38
|
+
opts.on_tail( '-h', '--help', 'show this message' ) {
|
39
|
+
@out.puts opts
|
40
|
+
exit
|
41
|
+
}
|
42
|
+
|
43
|
+
# parse the command line arguments
|
44
|
+
opts.parse! args
|
45
|
+
options[:name] = args.empty? ? 'data' : args.join('_')
|
46
|
+
options[:output_dir] = File.join(mrbones_dir, name)
|
47
|
+
end
|
48
|
+
|
49
|
+
end # class UnfreezeCommand
|
50
|
+
end # class App
|
51
|
+
end # module Bones
|
52
|
+
|
53
|
+
# EOF
|