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