p4ruby 2014.1 → 2014.2.0.pre1

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/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