pluginfactory 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +70 -319
- data/LICENSE +1 -1
- data/README +0 -4
- data/Rakefile +90 -68
- data/lib/pluginfactory.rb +7 -11
- data/rake/dependencies.rb +1 -1
- data/rake/helpers.rb +75 -48
- data/rake/hg.rb +261 -0
- data/rake/manual.rb +74 -76
- data/rake/packaging.rb +40 -8
- data/rake/publishing.rb +70 -69
- data/rake/rdoc.rb +11 -26
- data/rake/style.rb +1 -1
- data/rake/svn.rb +582 -516
- data/rake/testing.rb +6 -21
- data/rake/win32.rb +119 -23
- data/spec/pluginfactory_spec.rb +112 -124
- metadata +18 -129
data/lib/pluginfactory.rb
CHANGED
@@ -63,10 +63,6 @@ class FactoryError < RuntimeError; end
|
|
63
63
|
# driver.class #=> MysqlDriver
|
64
64
|
# pgdriver = Driver.create( "PostGresDriver" )
|
65
65
|
#
|
66
|
-
# == Subversion ID
|
67
|
-
#
|
68
|
-
# $Id: pluginfactory.rb 57 2009-02-25 17:50:55Z deveiant $
|
69
|
-
#
|
70
66
|
# == Authors
|
71
67
|
#
|
72
68
|
# * Martin Chase <stillflame@FaerieMUD.org>
|
@@ -80,8 +76,8 @@ class FactoryError < RuntimeError; end
|
|
80
76
|
#
|
81
77
|
module PluginFactory
|
82
78
|
|
83
|
-
VERSION = '1.0.
|
84
|
-
|
79
|
+
VERSION = '1.0.5'
|
80
|
+
|
85
81
|
|
86
82
|
### Logging
|
87
83
|
@default_logger = Logger.new( $stderr )
|
@@ -93,7 +89,7 @@ module PluginFactory
|
|
93
89
|
class << self
|
94
90
|
# The logger that will be used when the logging subsystem is reset
|
95
91
|
attr_accessor :default_logger
|
96
|
-
|
92
|
+
|
97
93
|
# The logger that's currently in effect
|
98
94
|
attr_accessor :logger
|
99
95
|
alias_method :log, :logger
|
@@ -113,14 +109,14 @@ module PluginFactory
|
|
113
109
|
}
|
114
110
|
end
|
115
111
|
end
|
116
|
-
|
112
|
+
|
117
113
|
|
118
114
|
### Reset the global logger object to the default
|
119
115
|
def self::reset_logger
|
120
116
|
self.logger = self.default_logger
|
121
117
|
self.logger.level = Logger::WARN
|
122
118
|
end
|
123
|
-
|
119
|
+
|
124
120
|
|
125
121
|
### Returns +true+ if the global logger has not been set to something other than
|
126
122
|
### the default one.
|
@@ -181,7 +177,7 @@ module PluginFactory
|
|
181
177
|
end
|
182
178
|
alias_method :factoryType, :factory_type
|
183
179
|
|
184
|
-
|
180
|
+
|
185
181
|
### Inheritance callback -- Register subclasses in the derivatives hash
|
186
182
|
### so that ::create knows about them.
|
187
183
|
def inherited( subclass )
|
@@ -273,7 +269,7 @@ module PluginFactory
|
|
273
269
|
return self.derivatives[ class_name.downcase ]
|
274
270
|
end
|
275
271
|
alias_method :getSubclass, :get_subclass
|
276
|
-
|
272
|
+
|
277
273
|
|
278
274
|
### Calculates an appropriate filename for the derived class using the
|
279
275
|
### name of the base class and tries to load it via <tt>require</tt>. If
|
data/rake/dependencies.rb
CHANGED
data/rake/helpers.rb
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
|
7
7
|
require 'pathname'
|
8
|
+
require 'uri'
|
9
|
+
require 'open3'
|
8
10
|
|
9
11
|
begin
|
10
12
|
require 'readline'
|
@@ -66,17 +68,23 @@ def trace( *msg )
|
|
66
68
|
end
|
67
69
|
|
68
70
|
|
71
|
+
### Return the specified args as a string, quoting any that have a space.
|
72
|
+
def quotelist( *args )
|
73
|
+
return args.flatten.collect {|part| part =~ /\s/ ? part.inspect : part}
|
74
|
+
end
|
75
|
+
|
76
|
+
|
69
77
|
### Run the specified command +cmd+ with system(), failing if the execution
|
70
78
|
### fails.
|
71
79
|
def run( *cmd )
|
72
80
|
cmd.flatten!
|
73
81
|
|
74
82
|
if cmd.length > 1
|
75
|
-
trace( cmd
|
83
|
+
trace( quotelist(*cmd) )
|
76
84
|
else
|
77
85
|
trace( cmd )
|
78
86
|
end
|
79
|
-
|
87
|
+
|
80
88
|
if $dryrun
|
81
89
|
$stderr.puts "(dry run mode)"
|
82
90
|
else
|
@@ -88,6 +96,15 @@ def run( *cmd )
|
|
88
96
|
end
|
89
97
|
|
90
98
|
|
99
|
+
### Run the given +cmd+ with the specified +args+ without interpolation by the shell and
|
100
|
+
### return anything written to its STDOUT.
|
101
|
+
def read_command_output( cmd, *args )
|
102
|
+
trace "Reading output from: %s" % [ cmd, quotelist(cmd, *args) ]
|
103
|
+
output = IO.read( '|-' ) or exec cmd, *args
|
104
|
+
return output
|
105
|
+
end
|
106
|
+
|
107
|
+
|
91
108
|
### Run a subordinate Rake process with the same options and the specified +targets+.
|
92
109
|
def rake( *targets )
|
93
110
|
opts = ARGV.select {|arg| arg[0,1] == '-' }
|
@@ -106,7 +123,7 @@ def pipeto( *cmd )
|
|
106
123
|
$stderr.puts "(dry run mode)"
|
107
124
|
else
|
108
125
|
open( '|-', 'w+' ) do |io|
|
109
|
-
|
126
|
+
|
110
127
|
# Parent
|
111
128
|
if io
|
112
129
|
yield( io )
|
@@ -123,52 +140,35 @@ end
|
|
123
140
|
|
124
141
|
### Download the file at +sourceuri+ via HTTP and write it to +targetfile+.
|
125
142
|
def download( sourceuri, targetfile=nil )
|
126
|
-
oldsync = $
|
127
|
-
$
|
128
|
-
require '
|
129
|
-
require 'uri'
|
143
|
+
oldsync = $stdout.sync
|
144
|
+
$stdout.sync = true
|
145
|
+
require 'open-uri'
|
130
146
|
|
131
147
|
targetpath = Pathname.new( targetfile )
|
132
148
|
|
133
149
|
log "Downloading %s to %s" % [sourceuri, targetfile]
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
if res.is_a?( Net::HTTPSuccess )
|
146
|
-
log "Downloading..."
|
147
|
-
res.read_body do |buf|
|
148
|
-
ofh.print( buf )
|
149
|
-
end
|
150
|
-
downloaded = true
|
151
|
-
puts "done."
|
152
|
-
|
153
|
-
elsif res.is_a?( Net::HTTPRedirection )
|
154
|
-
url = URI( res['location'] )
|
155
|
-
log "...following redirection to: %s" % [ url ]
|
156
|
-
limit -= 1
|
157
|
-
sleep 0.2
|
158
|
-
next
|
159
|
-
|
160
|
-
else
|
161
|
-
res.error!
|
162
|
-
end
|
150
|
+
trace " connecting..."
|
151
|
+
ifh = open( sourceuri ) do |ifh|
|
152
|
+
trace " connected..."
|
153
|
+
targetpath.open( File::WRONLY|File::TRUNC|File::CREAT, 0644 ) do |ofh|
|
154
|
+
log "Downloading..."
|
155
|
+
buf = ''
|
156
|
+
|
157
|
+
while ifh.read( 16384, buf )
|
158
|
+
until buf.empty?
|
159
|
+
bytes = ofh.write( buf )
|
160
|
+
buf.slice!( 0, bytes )
|
163
161
|
end
|
164
162
|
end
|
163
|
+
|
164
|
+
log "Done."
|
165
165
|
end
|
166
|
-
|
166
|
+
|
167
167
|
end
|
168
|
-
|
168
|
+
|
169
169
|
return targetpath
|
170
170
|
ensure
|
171
|
-
$
|
171
|
+
$stdout.sync = oldsync
|
172
172
|
end
|
173
173
|
|
174
174
|
|
@@ -202,13 +202,13 @@ end
|
|
202
202
|
### line-endings, color reset, etc.
|
203
203
|
def colorize( *args )
|
204
204
|
string = ''
|
205
|
-
|
205
|
+
|
206
206
|
if block_given?
|
207
207
|
string = yield
|
208
208
|
else
|
209
209
|
string = args.shift
|
210
210
|
end
|
211
|
-
|
211
|
+
|
212
212
|
ending = string[/(\s)$/] || ''
|
213
213
|
string = string.rstrip
|
214
214
|
|
@@ -290,7 +290,7 @@ def prompt_for_multiple_values( label, default=nil )
|
|
290
290
|
|
291
291
|
results = []
|
292
292
|
result = nil
|
293
|
-
|
293
|
+
|
294
294
|
begin
|
295
295
|
result = readline( make_prompt_string("> ") )
|
296
296
|
if result.nil? || result.empty?
|
@@ -299,7 +299,7 @@ def prompt_for_multiple_values( label, default=nil )
|
|
299
299
|
results << result
|
300
300
|
end
|
301
301
|
end until result.nil? || result.empty?
|
302
|
-
|
302
|
+
|
303
303
|
return results.flatten
|
304
304
|
end
|
305
305
|
|
@@ -322,7 +322,7 @@ def noecho( masked=false )
|
|
322
322
|
ensure
|
323
323
|
Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
|
324
324
|
end
|
325
|
-
|
325
|
+
|
326
326
|
return rval
|
327
327
|
end
|
328
328
|
|
@@ -365,7 +365,7 @@ alias :prompt_for_confirmation :ask_for_confirmation
|
|
365
365
|
### those will be returned in an Array, else the whole matching line is returned.
|
366
366
|
def find_pattern_in_file( regexp, file )
|
367
367
|
rval = nil
|
368
|
-
|
368
|
+
|
369
369
|
File.open( file, 'r' ).each do |line|
|
370
370
|
if (( match = regexp.match(line) ))
|
371
371
|
rval = match.captures.empty? ? match[0] : match.captures
|
@@ -377,12 +377,32 @@ def find_pattern_in_file( regexp, file )
|
|
377
377
|
end
|
378
378
|
|
379
379
|
|
380
|
+
### Search line-by-line in the output of the specified +cmd+ for the given +regexp+,
|
381
|
+
### returning the first match, or nil if no match was found. If the +regexp+ has any
|
382
|
+
### capture groups, those will be returned in an Array, else the whole matching line
|
383
|
+
### is returned.
|
384
|
+
def find_pattern_in_pipe( regexp, *cmd )
|
385
|
+
output = []
|
386
|
+
|
387
|
+
log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
|
388
|
+
Open3.popen3( *cmd ) do |stdin, stdout, stderr|
|
389
|
+
stdin.close
|
390
|
+
|
391
|
+
output << stdout.gets until stdout.eof?
|
392
|
+
output << stderr.gets until stderr.eof?
|
393
|
+
end
|
394
|
+
|
395
|
+
result = output.find { |line| regexp.match(line) }
|
396
|
+
return $1 || result
|
397
|
+
end
|
398
|
+
|
399
|
+
|
380
400
|
### Invoke the user's editor on the given +filename+ and return the exit code
|
381
401
|
### from doing so.
|
382
402
|
def edit( filename )
|
383
403
|
editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
|
384
404
|
system editor, filename
|
385
|
-
unless $?.success?
|
405
|
+
unless $?.success? || editor =~ /vim/i
|
386
406
|
fail "Editor exited uncleanly."
|
387
407
|
end
|
388
408
|
end
|
@@ -398,10 +418,17 @@ end
|
|
398
418
|
### Log a subdirectory change, execute a block, and exit the subdirectory
|
399
419
|
def in_subdirectory( subdir )
|
400
420
|
block = Proc.new
|
401
|
-
|
421
|
+
|
402
422
|
log "Entering #{subdir}"
|
403
423
|
Dir.chdir( subdir, &block )
|
404
424
|
log "Leaving #{subdir}"
|
405
425
|
end
|
406
|
-
|
426
|
+
|
427
|
+
|
428
|
+
### Make an easily-comparable version vector out of +ver+ and return it.
|
429
|
+
def vvec( ver )
|
430
|
+
return ver.split('.').collect {|char| char.to_i }.pack('N*')
|
431
|
+
end
|
432
|
+
|
433
|
+
|
407
434
|
|
data/rake/hg.rb
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
#
|
2
|
+
# Mercurial Rake Tasks
|
3
|
+
|
4
|
+
require 'enumerator'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Authors:
|
8
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
9
|
+
#
|
10
|
+
|
11
|
+
unless defined?( HG_DOTDIR )
|
12
|
+
|
13
|
+
# Mercurial constants
|
14
|
+
HG_DOTDIR = BASEDIR + '.hg'
|
15
|
+
HG_STORE = HG_DOTDIR + 'store'
|
16
|
+
|
17
|
+
IGNORE_FILE = BASEDIR + '.hgignore'
|
18
|
+
|
19
|
+
|
20
|
+
###
|
21
|
+
### Helpers
|
22
|
+
###
|
23
|
+
|
24
|
+
module MercurialHelpers
|
25
|
+
|
26
|
+
###############
|
27
|
+
module_function
|
28
|
+
###############
|
29
|
+
|
30
|
+
### Generate a commit log from a diff and return it as a String.
|
31
|
+
def make_commit_log
|
32
|
+
diff = read_command_output( 'hg', 'diff' )
|
33
|
+
fail "No differences." if diff.empty?
|
34
|
+
|
35
|
+
return diff
|
36
|
+
end
|
37
|
+
|
38
|
+
### Generate a commit log and invoke the user's editor on it.
|
39
|
+
def edit_commit_log
|
40
|
+
diff = make_commit_log()
|
41
|
+
|
42
|
+
File.open( COMMIT_MSG_FILE, File::WRONLY|File::TRUNC|File::CREAT ) do |fh|
|
43
|
+
fh.print( diff )
|
44
|
+
end
|
45
|
+
|
46
|
+
edit( COMMIT_MSG_FILE )
|
47
|
+
end
|
48
|
+
|
49
|
+
### Generate a changelog.
|
50
|
+
def make_changelog
|
51
|
+
log = read_command_output( 'hg', 'log', '--style', 'compact' )
|
52
|
+
return log
|
53
|
+
end
|
54
|
+
|
55
|
+
### Get the 'tip' info and return it as a Hash
|
56
|
+
def get_tip_info
|
57
|
+
data = read_command_output( 'hg', 'tip' )
|
58
|
+
return YAML.load( data )
|
59
|
+
end
|
60
|
+
|
61
|
+
### Return the ID for the current rev
|
62
|
+
def get_current_rev
|
63
|
+
id = read_command_output( 'hg', '-q', 'identify' )
|
64
|
+
return id.chomp
|
65
|
+
end
|
66
|
+
|
67
|
+
### Read the list of existing tags and return them as an Array
|
68
|
+
def get_tags
|
69
|
+
taglist = read_command_output( 'hg', 'tags' )
|
70
|
+
return taglist.split( /\n/ )
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
### Read any remote repo paths known by the current repo and return them as a hash.
|
75
|
+
def get_repo_paths
|
76
|
+
paths = {}
|
77
|
+
pathspec = read_command_output( 'hg', 'paths' )
|
78
|
+
pathspec.split.each_slice( 3 ) do |name, _, url|
|
79
|
+
paths[ name ] = url
|
80
|
+
end
|
81
|
+
return paths
|
82
|
+
end
|
83
|
+
|
84
|
+
### Return the list of files which are of status 'unknown'
|
85
|
+
def get_unknown_files
|
86
|
+
list = read_command_output( 'hg', 'status', '-un', '--no-color' )
|
87
|
+
list = list.split( /\n/ )
|
88
|
+
|
89
|
+
trace "New files: %p" % [ list ]
|
90
|
+
return list
|
91
|
+
end
|
92
|
+
|
93
|
+
### Returns a human-scannable file list by joining and truncating the list if it's too long.
|
94
|
+
def humanize_file_list( list, indent=FILE_INDENT )
|
95
|
+
listtext = list[0..5].join( "\n#{indent}" )
|
96
|
+
if list.length > 5
|
97
|
+
listtext << " (and %d other/s)" % [ list.length - 5 ]
|
98
|
+
end
|
99
|
+
|
100
|
+
return listtext
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
### Add the list of +pathnames+ to the .hgignore list.
|
105
|
+
def hg_ignore_files( *pathnames )
|
106
|
+
patterns = pathnames.flatten.collect do |path|
|
107
|
+
'^' + Regexp.escape(path) + '$'
|
108
|
+
end
|
109
|
+
trace "Ignoring %d files." % [ pathnames.length ]
|
110
|
+
|
111
|
+
IGNORE_FILE.open( File::CREAT|File::WRONLY|File::APPEND, 0644 ) do |fh|
|
112
|
+
fh.puts( patterns )
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
### Delete the files in the given +filelist+ after confirming with the user.
|
118
|
+
def delete_extra_files( filelist )
|
119
|
+
description = humanize_file_list( filelist, ' ' )
|
120
|
+
log "Files to delete:\n ", description
|
121
|
+
ask_for_confirmation( "Really delete them?", false ) do
|
122
|
+
filelist.each do |f|
|
123
|
+
rm_rf( f, :verbose => true )
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end # module MercurialHelpers
|
129
|
+
|
130
|
+
|
131
|
+
### Rakefile support
|
132
|
+
def get_vcs_rev( dir='.' )
|
133
|
+
return MercurialHelpers.get_current_rev
|
134
|
+
end
|
135
|
+
def make_changelog
|
136
|
+
return MercurialHelpers.make_changelog
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
###
|
141
|
+
### Tasks
|
142
|
+
###
|
143
|
+
|
144
|
+
desc "Mercurial tasks"
|
145
|
+
namespace :hg do
|
146
|
+
include MercurialHelpers
|
147
|
+
|
148
|
+
desc "Prepare for a new release"
|
149
|
+
task :prep_release do
|
150
|
+
tags = get_tags()
|
151
|
+
rev = get_current_rev()
|
152
|
+
|
153
|
+
# Look for a tag for the current release version, and if it exists abort
|
154
|
+
if tags.include?( PKG_VERSION )
|
155
|
+
error "Version #{PKG_VERSION} already has a tag. Did you mean " +
|
156
|
+
"to increment the version in #{VERSION_FILE}?"
|
157
|
+
fail
|
158
|
+
end
|
159
|
+
|
160
|
+
# Sign the current rev
|
161
|
+
log "Signing rev #{rev}"
|
162
|
+
run 'hg', 'sign'
|
163
|
+
|
164
|
+
# Tag the current rev
|
165
|
+
log "Tagging rev #{rev} as #{PKG_VERSION}"
|
166
|
+
run 'hg', 'tag', PKG_VERSION
|
167
|
+
|
168
|
+
# Offer to push
|
169
|
+
Rake::Task['hg:push'].invoke
|
170
|
+
end
|
171
|
+
|
172
|
+
desc "Check for new files and offer to add/ignore/delete them."
|
173
|
+
task :newfiles do
|
174
|
+
log "Checking for new files..."
|
175
|
+
|
176
|
+
entries = get_unknown_files()
|
177
|
+
|
178
|
+
unless entries.empty?
|
179
|
+
files_to_add = []
|
180
|
+
files_to_ignore = []
|
181
|
+
files_to_delete = []
|
182
|
+
|
183
|
+
entries.each do |entry|
|
184
|
+
action = prompt_with_default( " #{entry}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
|
185
|
+
case action
|
186
|
+
when 'a'
|
187
|
+
files_to_add << entry
|
188
|
+
when 'i'
|
189
|
+
files_to_ignore << entry
|
190
|
+
when 'd'
|
191
|
+
files_to_delete << entry
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
unless files_to_add.empty?
|
196
|
+
run 'hg', 'add', *files_to_add
|
197
|
+
end
|
198
|
+
|
199
|
+
unless files_to_ignore.empty?
|
200
|
+
hg_ignore_files( *files_to_ignore )
|
201
|
+
end
|
202
|
+
|
203
|
+
unless files_to_delete.empty?
|
204
|
+
delete_extra_files( files_to_delete )
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
task :add => :newfiles
|
209
|
+
|
210
|
+
|
211
|
+
desc "Check the current code in if tests pass"
|
212
|
+
task :checkin => ['hg:newfiles', 'test', COMMIT_MSG_FILE] do
|
213
|
+
targets = get_target_args()
|
214
|
+
$stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
|
215
|
+
ask_for_confirmation( "Continue with checkin?" ) do
|
216
|
+
run 'hg', 'ci', '-l', COMMIT_MSG_FILE, targets
|
217
|
+
rm_f COMMIT_MSG_FILE
|
218
|
+
end
|
219
|
+
Rake::Task['hg:push'].invoke
|
220
|
+
end
|
221
|
+
task :commit => :checkin
|
222
|
+
task :ci => :checkin
|
223
|
+
|
224
|
+
CLEAN.include( COMMIT_MSG_FILE )
|
225
|
+
|
226
|
+
desc "Push to the default origin repo (if there is one)"
|
227
|
+
task :push do
|
228
|
+
paths = get_repo_paths()
|
229
|
+
if origin_url = paths['default']
|
230
|
+
ask_for_confirmation( "Push to '#{origin_url}'?", false ) do
|
231
|
+
run 'hg', 'push'
|
232
|
+
end
|
233
|
+
else
|
234
|
+
trace "Skipping push: No 'default' path."
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
if HG_DOTDIR.exist?
|
241
|
+
trace "Defining mercurial VCS tasks"
|
242
|
+
|
243
|
+
desc "Check in all the changes in your current working copy"
|
244
|
+
task :ci => 'hg:ci'
|
245
|
+
desc "Check in all the changes in your current working copy"
|
246
|
+
task :checkin => 'hg:ci'
|
247
|
+
|
248
|
+
desc "Tag and sign revision before a release"
|
249
|
+
task :prep_release => 'hg:prep_release'
|
250
|
+
|
251
|
+
file COMMIT_MSG_FILE do
|
252
|
+
edit_commit_log()
|
253
|
+
end
|
254
|
+
|
255
|
+
else
|
256
|
+
trace "Not defining mercurial tasks: no #{HG_DOTDIR}"
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
|