p4ruby 2014.1 → 2014.2.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/ext/P4/specmgr.h ADDED
@@ -0,0 +1,102 @@
1
+ /*******************************************************************************
2
+
3
+ Copyright (c) 2001-2008, Perforce Software, Inc. All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ *******************************************************************************/
27
+
28
+ /*******************************************************************************
29
+ * Name : specmgr.h
30
+ *
31
+ * Author : Tony Smith <tony@perforce.com> or <tony@smee.org>
32
+ *
33
+ * Description : Ruby bindings for the Perforce API. Class for handling
34
+ * Perforce specs. This class provides other classes with
35
+ * generic support for parsing and formatting Perforce
36
+ * specs.
37
+ *
38
+ ******************************************************************************/
39
+
40
+ class StrBufDict;
41
+ class SpecMgr
42
+ {
43
+ public:
44
+ SpecMgr();
45
+ ~SpecMgr();
46
+ void SetDebug( int i ) { debug = i; }
47
+
48
+ // Clear the spec cache and revert to internal defaults
49
+ void Reset();
50
+
51
+ // Add a spec to the cache
52
+ void AddSpecDef( const char *type, StrPtr &specDef );
53
+ void AddSpecDef( const char *type, const char * specDef );
54
+
55
+ // Check that a type of spec is known.
56
+ int HaveSpecDef( const char *type );
57
+
58
+ //
59
+ // Parse routine: converts strings into Ruby P4::Spec objects.
60
+ //
61
+ VALUE StringToSpec( const char *type, const char *spec, Error *e );
62
+
63
+ //
64
+ // Format routine. updates a StrBuf object with the form;
65
+ // that can then be converted to a Ruby string where required.
66
+ //
67
+ void SpecToString(const char *type, VALUE hash, StrBuf &b, Error *e);
68
+
69
+ //
70
+ // Convert a Perforce StrDict into a Ruby hash. Used when we're
71
+ // parsing tagged output that is NOT a spec. e.g. output of
72
+ // fstat etc.
73
+ //
74
+ VALUE StrDictToHash( StrDict *dict, VALUE hash = Qnil );
75
+
76
+ //
77
+ // Convert a Perforce StrDict into a P4::Spec object. This is for
78
+ // 2005.2 and later servers where the forms are supplied pre-parsed
79
+ // into a dictionary - we just need to convert them. The specDef
80
+ // argument tells us what type of spec we're converting.
81
+ //
82
+ VALUE StrDictToSpec( StrDict *dict, StrPtr *specDef );
83
+
84
+
85
+ //
86
+ // Return a list of the fields in a given type of spec. Return Qnil
87
+ // if the spec type is not known.
88
+ //
89
+ VALUE SpecFields( const char *type );
90
+
91
+ private:
92
+
93
+ void SplitKey( const StrPtr *key, StrBuf &base, StrBuf &index );
94
+ void InsertItem( VALUE hash, const StrPtr *var, const StrPtr *val );
95
+ VALUE NewSpec( StrPtr *specDef );
96
+ VALUE SpecFields( StrPtr *specDef );
97
+
98
+ private:
99
+ int debug;
100
+ StrBufDict * specs;
101
+ };
102
+
@@ -0,0 +1,64 @@
1
+ /*******************************************************************************
2
+
3
+ Copyright (c) 2001-2008, Perforce Software, Inc. All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ *******************************************************************************/
27
+
28
+ /*******************************************************************************
29
+ * Name : undefdups.h
30
+ *
31
+ * Author : Tony Smith <tony@perforce.com> or <tony@smee.org>
32
+ *
33
+ * Description : Undefine portability macros defined by both Ruby's header
34
+ * files and those of the Perforce API. The idea is that you
35
+ * first include Ruby's headers, then this file, and then the
36
+ * Perforce API. This squelches any compiler warnings about
37
+ * pre-processor macros already being defined.
38
+ *
39
+ ******************************************************************************/
40
+
41
+ //
42
+ // Symbols defined by both Ruby and Perforce API headers
43
+ //
44
+ #undef HAVE_FSYNC
45
+ #undef HAVE_TRUNCATE
46
+
47
+ #ifdef OS_NT
48
+
49
+ // Stupid "#define SetPort SetPortA" in winspool.h
50
+ # ifdef SetPort
51
+ # undef SetPort
52
+ # endif
53
+
54
+ // GetMessage often #defined to GetMessageA
55
+ # ifdef GetMessage
56
+ # undef GetMessage
57
+ # endif
58
+
59
+ #endif
60
+
61
+ #ifdef HAVE_FORK
62
+ # undef HAVE_FORK
63
+ #endif
64
+
data/lib/P4.rb ADDED
@@ -0,0 +1,664 @@
1
+ #*******************************************************************************
2
+ # vim:ts=2:sw=2:et:
3
+ # Copyright (c) 2001-2008, Perforce Software, Inc. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ #
11
+ # 2. Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ # ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
19
+ # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+ #*******************************************************************************
26
+
27
+ #*******************************************************************************
28
+ #* Ruby interface to the Perforce SCM System
29
+ #*******************************************************************************
30
+
31
+ #*******************************************************************************
32
+ #* P4 class
33
+ #*******************************************************************************
34
+
35
+ require_relative 'P4/version'
36
+
37
+ #
38
+ # Get the bulk of the definition of the P4 class from the API interface
39
+ #
40
+ require 'P4.so'
41
+
42
+ #
43
+ # Add the extra's written purely in ruby.
44
+ #
45
+ class P4
46
+
47
+ #
48
+ # Named constants for the exception levels. Note they are cumulative,
49
+ # so RAISE_ALL includes RAISE_ERRORS (as you'd expect).
50
+ #
51
+ RAISE_NONE = 0
52
+ RAISE_ERRORS = 1
53
+ RAISE_ALL = 2
54
+
55
+ #
56
+ # Named values for merge actions. Values taken from clientmerge.h in
57
+ # the Perforce API
58
+ #
59
+ MERGE_SKIP = 1
60
+ MERGE_ACCEPT_MERGED = 2
61
+ MERGE_ACCEPT_EDIT = 3
62
+ MERGE_ACCEPT_THEIRS = 4
63
+ MERGE_ACCEPT_YOURS = 5
64
+
65
+ # Named values for generic error codes returned by
66
+ # P4::Message#generic
67
+
68
+ EV_NONE = 0 # misc
69
+
70
+ # The fault of the user
71
+
72
+ EV_USAGE = 0x01 # request not consistent with dox
73
+ EV_UNKNOWN = 0x02 # using unknown entity
74
+ EV_CONTEXT = 0x03 # using entity in wrong context
75
+ EV_ILLEGAL = 0x04 # trying to do something you can't
76
+ EV_NOTYET = 0x05 # something must be corrected first
77
+ EV_PROTECT = 0x06 # protections prevented operation
78
+
79
+ # No fault at all
80
+
81
+ EV_EMPTY = 0x11 # action returned empty results
82
+
83
+ # not the fault of the user
84
+
85
+ EV_FAULT = 0x21 # inexplicable program fault
86
+ EV_CLIENT = 0x22 # client side program errors
87
+ EV_ADMIN = 0x23 # server administrative action required
88
+ EV_CONFIG = 0x24 # client configuration inadequate
89
+ EV_UPGRADE = 0x25 # client or server too old to interact
90
+ EV_COMM = 0x26 # communications error
91
+ EV_TOOBIG = 0x27 # not even Perforce can handle this much
92
+
93
+ # Named values for error severities returned by
94
+ # P4::Message#severity
95
+ E_EMPTY = 0 # nothing yet
96
+ E_INFO = 1 # something good happened
97
+ E_WARN = 2 # something not good happened
98
+ E_FAILED = 3 # user did something wrong
99
+ E_FATAL = 4 # system broken -- nothing can continue
100
+
101
+ # OutputHandler return values constants
102
+
103
+ REPORT = 0
104
+ HANDLED = 1
105
+ CANCEL = 2
106
+
107
+ # Client progress 'done' state
108
+ PROG_NORMAL = 0
109
+ PROG_DONE = 1
110
+ PROG_FAILDONE = 2
111
+ PROG_FLUSH = 3
112
+
113
+ # Mappings for P4#each_<spec>
114
+ # Hash of type vs. key
115
+ SpecTypes = {
116
+ "clients" => ["client", "client"],
117
+ "labels" => ["label", "label"],
118
+ "branches" => ["branch", "branch"],
119
+ "changes" => ["change", "change"],
120
+ "streams" => ["stream", "Stream"],
121
+ "jobs" => ["job", "Job"],
122
+ "users" => ["user", "User"],
123
+ "groups" => ["group", "group"],
124
+ "depots" => ["depot", "name"],
125
+ "servers" => ["server", "Name"],
126
+ }
127
+
128
+ def method_missing( m, *a )
129
+
130
+ # Generic run_* methods
131
+ if ( m.to_s =~ /^run_(.*)/ )
132
+ return self.run( $1, a )
133
+
134
+ # Generic fetch_* methods
135
+ elsif ( m.to_s =~ /^fetch_(.*)/ )
136
+ return self.run( $1, "-o", a ).shift
137
+
138
+ # Generic save_* methods
139
+ elsif ( m.to_s =~ /^save_(.*)/ )
140
+ if ( a.length == 0 )
141
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
142
+ end
143
+ self.input = a.shift
144
+ return self.run( $1, "-i", a )
145
+
146
+ # Generic delete_* methods
147
+ elsif ( m.to_s =~ /^delete_(.*)/ )
148
+ if ( a.length == 0 )
149
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
150
+ end
151
+ return self.run( $1, "-d", a )
152
+
153
+ # Generic parse_* methods
154
+ elsif ( m.to_s == "parse_forms" )
155
+ raise( NoMethodError, "undefined method 'P4#parse_forms'", caller )
156
+ elsif ( m.to_s =~ /^parse_(.*)/ )
157
+ if ( a.length != 1 )
158
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
159
+ end
160
+ return self.parse_spec( $1, a.shift )
161
+
162
+ # Generic format_* methods
163
+ elsif ( m.to_s =~ /^format_(.*)/ )
164
+ if ( a.length != 1 )
165
+ raise( P4Exception, "Method P4##{m.to_s} requires an argument", caller)
166
+ end
167
+ return self.format_spec( $1, a.shift )
168
+
169
+ #
170
+ # Generic each_* methods
171
+ # Simple method to iterate over a particular type of spec
172
+ # This is a convenient wrapper for the pattern:
173
+ # clients = p4.run_clients
174
+ # clients.each do
175
+ # |c|
176
+ # client = p4.fetch_client( c['client'] )
177
+ # <do something with client>
178
+ # end
179
+ #
180
+ # NOTE: It's not possible to implicitly pass a block to a
181
+ # delegate method, so I've implemented it here directly. Could use
182
+ # Proc.new.call, but it looks like there is a serious performance
183
+ # impact with that method.
184
+ #
185
+ elsif ( m.to_s =~ /^each_(.*)/ )
186
+ raise( P4Exception, "No such method P4##{m.to_s}", caller) unless SpecTypes.has_key?( $1 )
187
+ raise( P4Exception, "Method P4##{m.to_s} requires block", caller) unless block_given?
188
+ specs = self.run( $1, a )
189
+ cmd = SpecTypes[ $1 ][0].downcase
190
+ key = SpecTypes[ $1 ][1]
191
+
192
+ specs.each{
193
+ |spec|
194
+ spec = self.run( cmd, "-o", spec[key] ).shift
195
+ yield spec
196
+ }
197
+ return specs
198
+
199
+ # That's all folks!
200
+ else
201
+ raise NameError, "No such method #{m.to_s} in class P4", caller
202
+ end
203
+ end
204
+
205
+ #
206
+ # Simple interface for submitting. If any argument is a Hash, (or subclass
207
+ # thereof - like P4::Spec), then it will be assumed to contain the change
208
+ # form. All other arguments are passed on to the server unchanged.
209
+ #
210
+ def run_submit( *args )
211
+ form = nil
212
+ nargs = args.flatten.collect do
213
+ |a|
214
+ if( a.kind_of?( Hash ) )
215
+ form = a
216
+ nil
217
+ else
218
+ a
219
+ end
220
+ end.compact
221
+
222
+ if( form )
223
+ self.input = form
224
+ nargs.push( "-i" )
225
+ end
226
+ return self.run( "submit", nargs )
227
+ end
228
+
229
+ #
230
+ # Simple interface for shelving. Same rules as for submit apply
231
+
232
+ def run_shelve( *args )
233
+ form = nil
234
+ nargs = args.flatten.collect do
235
+ |a|
236
+ if( a.kind_of?( Hash ) )
237
+ form = a
238
+ nil
239
+ else
240
+ a
241
+ end
242
+ end.compact
243
+
244
+ if( form )
245
+ self.input = form
246
+ nargs.push( "-i" )
247
+ end
248
+ return self.run( "shelve", nargs )
249
+ end
250
+
251
+ def delete_shelve( *args )
252
+ if( ! args.include?( "-c" ) )
253
+ args.unshift( "-c")
254
+ end
255
+ return self.run( "shelve", "-d", args)
256
+ end
257
+
258
+ #
259
+ # Simple interface for using "p4 login"
260
+ #
261
+ def run_login( *args )
262
+ self.input = self.password
263
+ return self.run( "login", args )
264
+ end
265
+
266
+ def run_resolve( *args )
267
+ if( block_given? )
268
+ self.run( "resolve", args ) do
269
+ |default|
270
+ yield( default )
271
+ end
272
+ else
273
+ self.run( "resolve", args )
274
+ end
275
+ end
276
+
277
+ #
278
+ # Simple interface to 'p4 tickets'
279
+ #
280
+ def run_tickets
281
+ path = self.ticket_file
282
+ # return an empty array if the file doesn't exist
283
+ # or is a directory.
284
+ results = Array.new
285
+ re = Regexp.new( /([^=]*)=(.*):([^:]*)$/ )
286
+ if( File.exist?( path ) and !File.directory?( path ) )
287
+ File.open( path ) do
288
+ |file|
289
+ file.each_line do
290
+ |line|
291
+ res = re.match( line )
292
+ if( res )
293
+ tickets = { 'Host' => res[1], 'User' => res[2], 'Ticket' => res[3] }
294
+ results.push( tickets )
295
+ end
296
+ end
297
+ end
298
+ end
299
+ return results
300
+ end
301
+
302
+ #
303
+ # Interface for changing the user's password. Supply the old password
304
+ # and the new one.
305
+ #
306
+ def run_password( oldpass, newpass )
307
+ if( oldpass && oldpass.length > 0 )
308
+ self.input = [ oldpass, newpass, newpass ]
309
+ else
310
+ self.input = [ newpass, newpass ]
311
+ end
312
+ self.run( "password" )
313
+ end
314
+
315
+ #
316
+ # The following methods convert the standard output of some common
317
+ # Perforce commands into more structured form to make using the
318
+ # data easier.
319
+ #
320
+ # (Currently only run_filelog is defined. More to follow)
321
+
322
+ #
323
+ # run_filelog: convert "p4 filelog" responses into objects with useful
324
+ # methods
325
+ #
326
+ # Requires tagged output to be of any real use. If tagged output it not
327
+ # enabled then you just get the raw data back
328
+ #
329
+ def run_filelog( *args )
330
+ raw = self.run( 'filelog', args.flatten )
331
+ raw.collect do
332
+ |h|
333
+ if ( ! h.kind_of?( Hash ) )
334
+ h
335
+ else
336
+ df = P4::DepotFile.new( h[ "depotFile" ] )
337
+ h[ "rev" ].each_index do
338
+ |n|
339
+
340
+ # If rev is nil, there's nothing here for us
341
+ next unless h[ "rev" ][ n ]
342
+
343
+ # Create a new revision of this file ready for populating
344
+ r = df.new_revision
345
+
346
+ h.each do
347
+ |key,value|
348
+ next unless( value.kind_of?( Array ) )
349
+ next unless value[ n ]
350
+ next if( value[ n ].kind_of?( Array ) )
351
+ # If the field is the revision time, convert it to a Time object
352
+ value[ n ] = Time.at( value[ n ].to_i ) if key == "time"
353
+ r.set_attribute( key, value[ n ] )
354
+ end
355
+
356
+ # Now if there are any integration records for this revision,
357
+ # add them in too
358
+ next unless ( h[ "how" ] )
359
+ next unless ( h[ "how" ][ n ] )
360
+
361
+ h[ "how" ][ n ].each_index do
362
+ |m|
363
+ how = h[ "how" ][ n ][ m ]
364
+ file = h[ "file" ][ n ][ m ]
365
+ srev = h[ "srev" ][ n ][ m ]
366
+ erev = h[ "erev" ][ n ][ m ]
367
+ srev.gsub!( /^#/, "" )
368
+ erev.gsub!( /^#/, "" )
369
+ srev = ( srev == "none" ? 0 : srev.to_i )
370
+ erev = ( erev == "none" ? 0 : erev.to_i )
371
+
372
+ r.integration( how, file, srev, erev )
373
+ end
374
+ end
375
+ df
376
+ end
377
+ end
378
+ end
379
+
380
+ #
381
+ # Allow the user to run commands at a temporarily altered exception level.
382
+ # Pass the new exception level desired, and a block to be executed at that
383
+ # level.
384
+ #
385
+ def at_exception_level( level )
386
+ return self unless block_given?
387
+ old_level = self.exception_level
388
+ self.exception_level = level
389
+ begin
390
+ yield( self )
391
+ ensure
392
+ self.exception_level = old_level
393
+ end
394
+ self
395
+ end
396
+
397
+ #
398
+ # Allow users to run commands using a specified handler.
399
+ # Pass a handler and the block that will be executed using this handler
400
+ # The handler will be reset to its previous value at the end of this block
401
+ #
402
+ def with_handler( handler )
403
+ return self unless block_given?
404
+ old_handler = self.handler
405
+ self.handler = handler
406
+ begin
407
+ yield( self )
408
+ ensure
409
+ self.handler = old_handler
410
+ end
411
+ self
412
+ end
413
+
414
+ #
415
+ # Show some handy information when using irb
416
+ #
417
+ def inspect
418
+ sprintf( 'P4: [%s] %s@%s (%s)',
419
+ self.port, self.user, self.client,
420
+ self.connected? ? 'connected' : 'not connected' )
421
+ end
422
+
423
+ #*****************************************************************************
424
+ # The P4::Spec class holds the fields in a Perforce spec
425
+ #*****************************************************************************
426
+ class Spec < Hash
427
+ def initialize( fieldmap = nil )
428
+ @fields = fieldmap
429
+ end
430
+
431
+ #
432
+ # Override the default assignment method. This implementation
433
+ # ensures that any fields defined are valid ones for this type of
434
+ # spec.
435
+ #
436
+ def []=( key, value )
437
+ if( self.has_key?( key ) || @fields == nil )
438
+ super( key, value )
439
+ elsif( @fields.has_key?( key.downcase ) )
440
+ super( @fields[ key.downcase ], value )
441
+ else
442
+ raise( P4Exception, "Invalid field: #{key}" )
443
+ end
444
+ end
445
+
446
+ #
447
+ # Return the list of the fields that are permitted in this spec
448
+ #
449
+ def permitted_fields
450
+ @fields.values
451
+ end
452
+
453
+ #
454
+ # Implement accessor methods for the fields in the spec. The accessor
455
+ # methods are all prefixed with '_' to avoid conflicts with the Hash
456
+ # class' namespace. This is a little ugly, but we gain a lot by
457
+ # subclassing Hash so it's worth it.
458
+ #
459
+ def method_missing( m, *a )
460
+ k = m.to_s.downcase
461
+
462
+ # Check if we're being asked for 'to_ary'. If so, raise 'NoMethodError'.
463
+ raise NoMethodError if( k == "to_ary" )
464
+
465
+ if( k[0..0] != "_" )
466
+ raise( RuntimeError,
467
+ "undefined method `#{m.to_s}' for object of " +
468
+ "class #{self.class.to_s}" )
469
+ end
470
+ k = k[ 1..-1 ]
471
+
472
+ if( k =~ /(.*)=$/ )
473
+ if( a.length() == 0 )
474
+ raise( P4Exception, "Method P4##{m} requires an argument" );
475
+ end
476
+
477
+ k = $1
478
+ if( @fields == nil || @fields.has_key?( k ) )
479
+ return self[ @fields[ k ] ] = a.shift
480
+ end
481
+ elsif( self.has_key?( m.to_s ) )
482
+ return self[ m.to_s ]
483
+ elsif( @fields.has_key?( k ) )
484
+ return self[ @fields[ k ] ]
485
+ end
486
+ raise( P4Exception, "Invalid field: #{$1}" )
487
+ end
488
+ end
489
+
490
+ #*****************************************************************************
491
+ #* P4::MergeInfo class
492
+ #*****************************************************************************
493
+
494
+ class MergeInfo
495
+ def initialize( base, yours, theirs, merged, hint )
496
+ @base = base
497
+ @yours = yours
498
+ @theirs = theirs
499
+ @merged = merged
500
+ @hint = hint
501
+ end
502
+
503
+ attr_reader :base, :yours, :theirs, :merged, :hint
504
+ end
505
+
506
+ #*****************************************************************************
507
+ # P4::Integration class
508
+ # P4::Integration objects hold details about the integrations that have
509
+ # been performed on a particular revision. Used primarily with the
510
+ # P4::Revision class
511
+ #*****************************************************************************
512
+ class Integration
513
+ def initialize( how, file, srev, erev )
514
+ @how = how
515
+ @file = file
516
+ @srev = srev
517
+ @erev = erev
518
+ end
519
+
520
+ attr_reader :how, :file, :srev, :erev
521
+ end
522
+
523
+ #*****************************************************************************
524
+ # P4::Revision class
525
+ # Each P4::Revision object holds details about a particular revision
526
+ # of a file. It may also contain the history of any integrations
527
+ # to/from the file
528
+ #*****************************************************************************
529
+
530
+ class Revision
531
+ def initialize( depotFile )
532
+ @depot_file = depotFile
533
+ @integrations = Array.new
534
+ @attributes = Hash.new
535
+ end
536
+
537
+ attr_reader :depot_file
538
+ attr_accessor :integrations
539
+
540
+ def integration( how, file, srev, erev )
541
+ rec = P4::Integration.new( how, file, srev, erev )
542
+ @integrations.push( rec )
543
+ return rec
544
+ end
545
+
546
+ def each_integration
547
+ @integrations.each { |i| yield( i ) }
548
+ end
549
+
550
+ def set_attribute( name, value )
551
+ name = name.downcase
552
+ if( value =~ /^\d+$/ )
553
+ @attributes[ name ] = value.to_i
554
+ else
555
+ @attributes[ name ] = value
556
+ end
557
+ end
558
+
559
+ # Define #type and #type= explicitly as they clash with the
560
+ # deprecated Object#type. As it is deprecated, this clash should
561
+ # disappear in time.
562
+ def type
563
+ @attributes[ 'type' ]
564
+ end
565
+
566
+ def type=( t )
567
+ @attributes[ 'type' ] = t
568
+ end
569
+
570
+ #
571
+ # Generic getters and setters for revision attributes.
572
+ #
573
+ def method_missing( m, *a )
574
+ k = m.to_s.downcase
575
+ if( k =~ /(.*)=$/ )
576
+ if( a.length() == 0 )
577
+ raise( P4Exception, "Method P4##{m} requires an argument" );
578
+ end
579
+ k = $1
580
+ @attributes[ k ] = a.shift
581
+ else
582
+ @attributes[ k ]
583
+ end
584
+ end
585
+ end
586
+
587
+ #*****************************************************************************
588
+ # P4::DepotFile class.
589
+ # Each DepotFile entry contains details about one depot file.
590
+ #*****************************************************************************
591
+ class DepotFile
592
+ def initialize( name )
593
+ @depot_file = name
594
+ @revisions = Array.new
595
+ @headAction = @head_type = @head_time = @head_rev = @head_change = nil
596
+ end
597
+
598
+ attr_reader :depot_file, :revisions
599
+ attr_accessor :head_action, :head_type, :head_time, :head_rev, :head_change
600
+
601
+ def new_revision
602
+ r = P4::Revision.new( @depot_file )
603
+ @revisions.push( r )
604
+ return r
605
+ end
606
+
607
+ def each_revision
608
+ @revisions.each { |r| yield( r ) }
609
+ end
610
+ end
611
+
612
+ #*****************************************************************************
613
+ # P4::OutputHandler class.
614
+ # Base class for all Handler classes that can be passed to P4::handler.
615
+ #*****************************************************************************
616
+ class OutputHandler
617
+ def outputStat(stat)
618
+ REPORT
619
+ end
620
+
621
+ def outputInfo(info)
622
+ REPORT
623
+ end
624
+
625
+ def outputText(text)
626
+ REPORT
627
+ end
628
+
629
+ def outputBinary(binary)
630
+ REPORT
631
+ end
632
+
633
+ def outputMessage(message)
634
+ REPORT
635
+ end
636
+ end
637
+
638
+ class ReportHandler < OutputHandler
639
+ def outputStat(stat)
640
+ p "stat:", stat
641
+ HANDLED
642
+ end
643
+
644
+ def outputInfo(info)
645
+ p "info:", info
646
+ HANDLED
647
+ end
648
+
649
+ def outputText(text)
650
+ p "text:", text
651
+ HANDLED
652
+ end
653
+
654
+ def outputBinary(binary)
655
+ p "binary:", binary
656
+ HANDLED
657
+ end
658
+
659
+ def outputMessage(message)
660
+ p "message:", message
661
+ HANDLED
662
+ end
663
+ end
664
+ end # class P4