bones 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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