rscm-accurev 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/TODO CHANGED
@@ -27,3 +27,4 @@ api - checkout(Time.infinite) just does update
27
27
  checkout(x) only works if ws not exist
28
28
  unless it implies nuke-and-re-checkout (slow)
29
29
 
30
+ api - need ac_update which returns an acresponse
data/lib/rscm/accurev.rb CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env ruby
1
2
  require 'rubygems'
2
3
  require 'rscm/scm/accurev/api'
3
4
  require 'rscm/scm/accurev/command'
@@ -13,10 +13,15 @@ end
13
13
 
14
14
  module RSCM::Accurev
15
15
 
16
- class AccurevException < Exception ; end
16
+ class AccurevException < Exception
17
+ attr_reader :error_msg
18
+ def initialize( msg, error_msg=nil )
19
+ super( "#{msg}: #{error_msg}" )
20
+ @error_msg = error_msg
21
+ end
22
+ end
17
23
 
18
- # Indicates a failure of the update command due to unkept local modifications
19
- class StaleWorkspaceError < AccurevException; end
24
+ class StaleWorkspaceException < AccurevException; end
20
25
 
21
26
  # RSCM implementation for Accurev (http://www.accurev.com/).
22
27
  #
@@ -52,9 +57,9 @@ module RSCM::Accurev
52
57
 
53
58
  def initialize( checkout_dir=nil, depot=nil, workspace_stream=nil )
54
59
  @depot = depot
55
- @checkout_dir = checkout_dir
56
60
  @workspace_stream = workspace_stream
57
61
  @backing_stream = nil # will be pulled from files cmd output
62
+ @checkout_dir = checkout_dir
58
63
  end
59
64
 
60
65
  def name()
@@ -99,13 +104,13 @@ module RSCM::Accurev
99
104
 
100
105
  def ac_files( relative_path )
101
106
  cmd = Command.instance
102
- # there must be a better way to do this yield->yield loopage
103
- obj = []
104
- cmd.accurev_elements( StatData, "files", relative_path ) do |fd|
107
+ ret = []
108
+ acresponse = cmd.accurev( "files", relative_path )
109
+ acresponse['element'].each do |fd|
105
110
  yield fd if block_given?
106
- obj << fd
111
+ ret << fd
107
112
  end
108
- return obj
113
+ return ret
109
114
  end
110
115
 
111
116
  def ac_keep( files=[], message="" )
@@ -187,7 +192,7 @@ module RSCM::Accurev
187
192
  "-l", co )
188
193
  # sucks:
189
194
  if ( mkws_out =~ /already exists/ )
190
- raise AccurevException.new( mkws_out )
195
+ raise AccurevException.new( "Failed to checkout", mkws_out )
191
196
  end
192
197
  end
193
198
  puts "> Updating workspace.."
@@ -195,21 +200,33 @@ module RSCM::Accurev
195
200
  end
196
201
 
197
202
  def update( to_identifier=Time.infinite )
198
- co = PathConverter.nativepath_to_filepath( @checkout_dir )
203
+ co = RSCM::PathConverter.nativepath_to_filepath( @checkout_dir )
199
204
  unless File.exists?( co )
200
205
  raise AccurevException.new( "Workspace does not exist!" )
201
206
  end
202
207
  updated = []
203
- with_working_dir( co ) do
204
- accurev_elements( UpdateData, "update" ) do |up|
205
- yield up.location
206
- updated << up.location
208
+ Command.instance do |cmd|
209
+ cmd.working_dir = co
210
+ acresponse = cmd.accurev( "update" )
211
+ if acresponse.error
212
+ if acresponse.error =~ /workspace have been modified/
213
+ raise StaleWorkspaceException.new( "Workspace stale",
214
+ acresponse.error )
215
+ else
216
+ # some other update problem
217
+ raise AccurevException.new( "Error on update", acresponse.error )
218
+ end
219
+ end
220
+ if acresponse['element']
221
+ acresponse['element'].each do |up|
222
+ yield up.location if block_given?
223
+ updated << up.location
224
+ end
207
225
  end
208
226
  end
209
227
  return updated
210
228
  end
211
229
 
212
-
213
230
  # --- internals
214
231
 
215
232
  private
@@ -10,27 +10,36 @@ module RSCM
10
10
  #
11
11
  class Command
12
12
 
13
- attr_accessor :debug, :debug_to, :accurev, :working_dir
13
+ attr_accessor :debug, :debug_to, :accurev_bin, :working_dir
14
14
 
15
- # map of xml element types to classes
16
- # used by accurev_element to return a list of objects
17
- attr_accessor :classmap
15
+ # If you need to swap out the mapper class
16
+ attr_accessor :xmlmapper
18
17
 
19
18
  def initialize()
19
+ @xmlmapper = XMLMapper
20
20
  @debug = false
21
21
  @debug_to = STDOUT
22
- @accurev = "accurev"
22
+ @accurev_bin = "accurev"
23
23
  @working_dir = "."
24
24
  end
25
25
 
26
+ #
27
+ # Returns a command line string for executing the given
28
+ # accurev subcomment +cmd+ with arguments +opts+.
29
+ #
26
30
  def accurev_cmdline( cmd, *opts )
27
- return "#{@accurev} #{cmd} #{opts.join(' ')}";
31
+ return "#{@accurev_bin} #{cmd} #{opts.join(' ')}";
28
32
  end
29
-
30
- # Execute the given accurev subcommand, and return its
31
- # output as a plain uninterpreted string.
32
- # Not all accurev subcommands (eg, `accurev info`) support
33
- # `-fx` for xml output.
33
+
34
+ #
35
+ # Executes the given accurev subcommand +cmd+ with the
36
+ # given arguments +opts+. The command will be executed with
37
+ # standard (non-xml) output format. This method returns
38
+ # the stdout from the command execution.
39
+ #
40
+ # (Not all accurev subcommands (eg, `accurev info`) support
41
+ # `-fx` for xml output.)
42
+ #
34
43
  def accurev_nofx( cmd, *opts )
35
44
  # nativepath_to_filepath is actually generic to native
36
45
  dir = PathConverter.nativepath_to_filepath( @working_dir )
@@ -45,13 +54,19 @@ module RSCM
45
54
  end
46
55
  end
47
56
  end
48
-
49
- # Execute the given accurev subcommand, and return its
50
- # output as an REXML document. The options list to the command
51
- # will automatically have `-fx` prepended, to specify xml output.
57
+
58
+ #
59
+ # Executes the given accurev subcommand +cmd+ with the
60
+ # given arguments +opts+ in XML mode. The output of the command
61
+ # will be converted to an REXML document and returned.
62
+ # The command's options list will have `-fx` appended,
63
+ # to specify xml output format.
64
+ #
52
65
  # Certain quirks in <AcResponse>-type documents will be
53
- # corrected (see Accurev::AcXMLScrubIO).
54
- def accurev( cmd, *opts )
66
+ # corrected (see Accurev::AcXMLScrubIO). Note that not all
67
+ # accurev subcommands support the "-fx" option.
68
+ #
69
+ def accurev_xml( cmd, *opts )
55
70
  # nativepath_to_filepath is actually generic to native
56
71
  dir = PathConverter.nativepath_to_filepath( @working_dir )
57
72
  dir = File.expand_path( dir )
@@ -64,7 +79,8 @@ module RSCM
64
79
  Better.popen( cmdline ) do |stdout|
65
80
  output = stdout.read()
66
81
  if @debug
67
- @debug_to.puts( "raw>\n#{output}" )
82
+ @debug_to.puts( "raw>" )
83
+ @debug_to.puts( output )
68
84
  end
69
85
  begin
70
86
  return REXML::Document.new( AcXMLScrubIO.new( output ) )
@@ -75,27 +91,28 @@ module RSCM
75
91
  end
76
92
  end
77
93
 
78
- # Execute the given accurev subcommand using +accurev+,
79
- # and return the <element> REXML elements from the
80
- # resulting document.
81
- # For ac commands which emit the common <elements> format.
82
- def accurev_elements( mapclass, cmd, *opts )
83
- doc = self.accurev( cmd, opts )
94
+ #
95
+ # Execute the given accurev subcommand using +accurev_xml+,
96
+ # and return an object tree mirroring the structure of the
97
+ # XML output.
98
+ #
99
+ # This uses +XMLMapper+ to build the object tree.
100
+ #
101
+ def accurev( cmd, *opts )
102
+ doc = self.accurev_xml( cmd, opts )
84
103
  if @debug
104
+ @debug_to.puts( "doc>" )
85
105
  @debug_to.puts( doc )
86
106
  end
87
107
  if doc.elements.size==0
88
108
  raise "Unexpected output from #{cmd}: #{doc}"
89
109
  end
90
- objs = []
91
- doc.elements.each( "/AcResponse/element" ) do |e|
92
- o = mapclass.new( e )
93
- yield o if block_given?
94
- objs << o
95
- end
96
- return objs
110
+ mapper = @xmlmapper.new()
111
+ return mapper.map( doc.root )
97
112
  end
98
113
 
114
+ # static methods
115
+
99
116
  # Retrieve this thread's +Command+ instance.
100
117
  #
101
118
  # eg:
@@ -120,8 +137,9 @@ module RSCM
120
137
  Thread.current[ :RSCM_ACCUREV_COMMAND ] = nil
121
138
  end
122
139
 
140
+ private
141
+
123
142
  # new() is marked private, use Command.instance().
124
- private
125
143
  def new( *args )
126
144
  super(args)
127
145
  end
@@ -1,36 +1,152 @@
1
1
  module RSCM; module Accurev; end; end
2
2
 
3
+ require 'rexml/element'
4
+
3
5
  module RSCM::Accurev
4
6
 
7
+ #
8
+ # XMLMapper creates an object tree from an XML element and its children.
9
+ #
10
+ # For each XML node, it locates a class associated with that node's name,
11
+ # and builds a new object of that type from the element.
12
+ #
13
+ # The nodename-class associations are determined by finding all
14
+ # classes derived from +RSCM::Accurev::ElementBackedClass+ and checking
15
+ # each of those classes +element_name+ methods.
16
+ #
17
+ class XMLMapper
18
+
19
+ # Map of element (tag) names to (ruby) class.
20
+ attr_accessor :classmap
21
+
22
+ def initialize()
23
+ @classmap = {}
24
+ ObjectSpace.each_object( Class ) do |k|
25
+ if k.ancestors.delete( ElementBackedClass ) and k != ElementBackedClass
26
+ if k.respond_to?( :element_name )
27
+ @classmap[ k.element_name ] = k
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ #
34
+ # Build an object tree from the given REXML element and its children.
35
+ # The returned object will be a subclass of +ElementBackedClass+.
36
+ #
37
+ def map( e )
38
+ unless @classmap.has_key?( e.name )
39
+ return nil
40
+ end
41
+ o = @classmap[e.name].new( e )
42
+ e.children.each do |child|
43
+ if child.kind_of?( REXML::Element )
44
+ a = self.map( child )
45
+ unless a.nil?
46
+ if o.children[ a.class.element_name ].nil?
47
+ o.children[ a.class.element_name ] = []
48
+ end
49
+ o.children[ a.class.element_name ] << a
50
+ end
51
+ end
52
+ end
53
+ return o
54
+ end
55
+ end
56
+
57
+ #
58
+ # Generic base class for defining typed mirror classes for XML elements.
59
+ #
60
+ # A subclass of ElementBackedClass has:
61
+ #
62
+ # * A class static +element_name+ method, identifying what
63
+ # type of XML element it shadows
64
+ #
65
+ # * A set of attributes shadowing the XML attributes of its target element
66
+ #
67
+ # * A map (+children+) of child elements, keyed by element name
68
+ #
69
+ # * A (optional) +text_content+ attribute, populated when the
70
+ # target element has text subnodes.
71
+ #
5
72
  class ElementBackedClass
73
+
74
+ attr_accessor :children
75
+
6
76
  def self.element_name( name )
7
- @@name = name # this doesn't do what i think it does
77
+ class << self
78
+ self
79
+ end.send( :define_method, "element_name" ) {name}
8
80
  end
81
+
9
82
  def initialize( element=nil )
83
+ @children = {}
10
84
  unless element.nil?
11
85
  self.set_from_element(element)
12
86
  end
13
87
  end
88
+
89
+ # ebc['foo'] is a synonym for ebc.children['foo']
90
+ def []( key )
91
+ return self.children[key]
92
+ end
93
+
94
+ protected
95
+ # Does the work of calling object setters for each XML attribute.
14
96
  def set_from_element( e )
15
97
  e.attributes.each do |attr, value|
16
- if self.respond_to?( "#{attr}=" )
17
- self.send( "#{attr}=", value )
98
+ # downcase the attr name for method-happiness
99
+ # (also hacks around some inconsistent accurev attr names...)
100
+ mattr = attr.downcase
101
+ if self.respond_to?( "#{mattr}=" )
102
+ self.send( "#{mattr}=", value )
18
103
  end
19
104
  end
105
+ if self.respond_to?( :text_content= ) and ! e.text.nil?
106
+ self.text_content = e.text
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ #
113
+ # Data from the typical AcResponse root node.
114
+ #
115
+ class AcResponseData < ElementBackedClass
116
+ element_name 'AcResponse'
117
+ attr_accessor :command, :directory
118
+
119
+ # Returns this response's error message, or nil if no error detected.
120
+ def error
121
+ if self.children['message']
122
+ zmsg = self.children['message'][0]
123
+ if zmsg.error and zmsg.error == 'true'
124
+ return zmsg.text_content
125
+ end
126
+ else
127
+ return nil
128
+ end
20
129
  end
21
130
  end
22
131
 
23
- # Data from "accurev file" command:
24
- class StatData < ElementBackedClass
132
+ #
133
+ # Data from "accurev file", "accure update", "accurev stat" commands.
134
+ # Identifies workspace objects (files, directories, etc).
135
+ #
136
+ class ElementData < ElementBackedClass
25
137
  element_name 'element'
26
138
  attr_accessor :location, :status, :dir, :id
27
- attr_accessor :elemType, :namedVersion, :Virtual, :Real
139
+ attr_accessor :elemType, :namedVersion, :virtual, :real
28
140
  end
29
-
30
- # Data from "accurev update" command:
31
- class UpdateData < ElementBackedClass
32
- element_name 'element'
33
- attr_accessor :location
141
+
142
+ #
143
+ # Data from "accurev {file,update,stat}" commands.
144
+ # Streamed status/info messages, usually interleaved with ElementData
145
+ # nodes.
146
+ #
147
+ class MessageData < ElementBackedClass
148
+ element_name 'message'
149
+ attr_accessor :error, :text_content
34
150
  end
35
-
151
+
36
152
  end
@@ -0,0 +1,8 @@
1
+ <acResponse
2
+ command="update"Scanning entire workspace for files touched since last scan - ok
3
+ >
4
+ <progress
5
+ phase="Calculating changes"
6
+ increment="100"/>
7
+ <message>Transaction high-water mark would be updated</message>
8
+ </acResponse>
@@ -0,0 +1,125 @@
1
+ <acResponse
2
+ command="update"Scanning entire workspace for files touched since last scan - ok
3
+ >
4
+ <progress
5
+ phase="Calculating changes"
6
+ increment="100"/>
7
+ <progress
8
+ phase="results"
9
+ number="23"/>
10
+ <message>Would remove &quot;src/java/com/orbitz/dc/service/hc/HcItinerary.java&quot; .
11
+ </message>
12
+ <element
13
+ location="src/java/com/orbitz/dc/service/hc/HcItinerary.java"/>
14
+ <checkpoint/>
15
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/Pricing.java&quot; .
16
+ </message>
17
+ <element
18
+ location="src/java/com/orbitz/dc/core/Pricing.java"/>
19
+ <checkpoint/>
20
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcSearchRequest.java&quot; .
21
+ </message>
22
+ <element
23
+ location="src/java/com/orbitz/dc/service/hc/HcSearchRequest.java"/>
24
+ <checkpoint/>
25
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcCreateReservationRequest.java&quot; .
26
+ </message>
27
+ <element
28
+ location="src/java/com/orbitz/dc/service/hc/HcCreateReservationRequest.java"/>
29
+ <checkpoint/>
30
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/HcJourney.java&quot; .
31
+ </message>
32
+ <element
33
+ location="src/java/com/orbitz/dc/core/HcJourney.java"/>
34
+ <checkpoint/>
35
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcCreateReservationResponse.java&quot; .
36
+ </message>
37
+ <element
38
+ location="src/java/com/orbitz/dc/service/hc/HcCreateReservationResponse.java"/>
39
+ <checkpoint/>
40
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcCancelReservationRequest.java&quot; .
41
+ </message>
42
+ <element
43
+ location="src/java/com/orbitz/dc/service/hc/HcCancelReservationRequest.java"/>
44
+ <checkpoint/>
45
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcCancelReservationResponse.java&quot; .
46
+ </message>
47
+ <element
48
+ location="src/java/com/orbitz/dc/service/hc/HcCancelReservationResponse.java"/>
49
+ <checkpoint/>
50
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcModifyReservationResponse.java&quot; .
51
+ </message>
52
+ <element
53
+ location="src/java/com/orbitz/dc/service/hc/HcModifyReservationResponse.java"/>
54
+ <checkpoint/>
55
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcModifyReservationRequest.java&quot; .
56
+ </message>
57
+ <element
58
+ location="src/java/com/orbitz/dc/service/hc/HcModifyReservationRequest.java"/>
59
+ <checkpoint/>
60
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/RateInfo.java&quot; .
61
+ </message>
62
+ <element
63
+ location="src/java/com/orbitz/dc/core/RateInfo.java"/>
64
+ <checkpoint/>
65
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/VariableRateInfo.java&quot; .
66
+ </message>
67
+ <element
68
+ location="src/java/com/orbitz/dc/core/VariableRateInfo.java"/>
69
+ <checkpoint/>
70
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/RATE_CATEGORY.java&quot; .
71
+ </message>
72
+ <element
73
+ location="src/java/com/orbitz/dc/core/RATE_CATEGORY.java"/>
74
+ <checkpoint/>
75
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/RATE_COVERAGE.java&quot; .
76
+ </message>
77
+ <element
78
+ location="src/java/com/orbitz/dc/core/RATE_COVERAGE.java"/>
79
+ <checkpoint/>
80
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/FlatRateInfo.java&quot; .
81
+ </message>
82
+ <element
83
+ location="src/java/com/orbitz/dc/core/FlatRateInfo.java"/>
84
+ <checkpoint/>
85
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/core/HcItinerary.java&quot; .
86
+ </message>
87
+ <element
88
+ location="src/java/com/orbitz/dc/core/HcItinerary.java"/>
89
+ <checkpoint/>
90
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcSearch.java&quot; .
91
+ </message>
92
+ <element
93
+ location="src/java/com/orbitz/dc/service/hc/HcSearch.java"/>
94
+ <checkpoint/>
95
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcCreateReservation.java&quot; .
96
+ </message>
97
+ <element
98
+ location="src/java/com/orbitz/dc/service/hc/HcCreateReservation.java"/>
99
+ <checkpoint/>
100
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcModifyReservation.java&quot; .
101
+ </message>
102
+ <element
103
+ location="src/java/com/orbitz/dc/service/hc/HcModifyReservation.java"/>
104
+ <checkpoint/>
105
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcCancelReservation.java&quot; .
106
+ </message>
107
+ <element
108
+ location="src/java/com/orbitz/dc/service/hc/HcCancelReservation.java"/>
109
+ <checkpoint/>
110
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcRetrieveItinerary.java&quot; .
111
+ </message>
112
+ <element
113
+ location="src/java/com/orbitz/dc/service/hc/HcRetrieveItinerary.java"/>
114
+ <checkpoint/>
115
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryRequest.java&quot; .
116
+ </message>
117
+ <element
118
+ location="src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryRequest.java"/>
119
+ <checkpoint/>
120
+ <message>Would replace content of &quot;src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryResponse.java&quot; .
121
+ </message>
122
+ <element
123
+ location="src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryResponse.java"/>
124
+ <checkpoint/>
125
+ </acResponse>
@@ -0,0 +1,7 @@
1
+ <acResponse
2
+ command="update"Scanning entire workspace for files touched since last scan - ok
3
+ >
4
+ <progress
5
+ phase="Calculating changes"
6
+ increment="100"/>
7
+ </acResponse>
@@ -0,0 +1,147 @@
1
+ <acResponse
2
+ command="update"Scanning entire workspace for files touched since last scan - ok
3
+ >
4
+ <progress
5
+ phase="Calculating changes"
6
+ increment="100"/>
7
+ <progress
8
+ phase="results"
9
+ number="23"/>
10
+ <message>Removing &quot;src/java/com/orbitz/dc/service/hc/HcItinerary.java&quot; .
11
+ </message>
12
+ <element
13
+ location="src/java/com/orbitz/dc/service/hc/HcItinerary.java"/>
14
+ <checkpoint/>
15
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/core/Pricing.java&quot; - ok
16
+ </message>
17
+ <element
18
+ location="src/java/com/orbitz/dc/core/Pricing.java"
19
+ size="1398"/>
20
+ <checkpoint/>
21
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcSearchRequest.java&quot; - ok
22
+ </message>
23
+ <element
24
+ location="src/java/com/orbitz/dc/service/hc/HcSearchRequest.java"
25
+ size="793"/>
26
+ <checkpoint/>
27
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcCreateReservationRequest.java&quot; - ok
28
+ </message>
29
+ <element
30
+ location="src/java/com/orbitz/dc/service/hc/HcCreateReservationRequest.java"
31
+ size="1712"/>
32
+ <checkpoint/>
33
+ <message>Content (3 K) of &quot;src/java/com/orbitz/dc/core/HcJourney.java&quot; - ok
34
+ </message>
35
+ <element
36
+ location="src/java/com/orbitz/dc/core/HcJourney.java"
37
+ size="3846"/>
38
+ <checkpoint/>
39
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcCreateReservationResponse.java&quot; - ok
40
+ </message>
41
+ <element
42
+ location="src/java/com/orbitz/dc/service/hc/HcCreateReservationResponse.java"
43
+ size="849"/>
44
+ <checkpoint/>
45
+ <message>Content (3 K) of &quot;src/java/com/orbitz/dc/service/hc/HcCancelReservationRequest.java&quot; - ok
46
+ </message>
47
+ <element
48
+ location="src/java/com/orbitz/dc/service/hc/HcCancelReservationRequest.java"
49
+ size="3046"/>
50
+ <checkpoint/>
51
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcCancelReservationResponse.java&quot; - ok
52
+ </message>
53
+ <element
54
+ location="src/java/com/orbitz/dc/service/hc/HcCancelReservationResponse.java"
55
+ size="756"/>
56
+ <checkpoint/>
57
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcModifyReservationResponse.java&quot; - ok
58
+ </message>
59
+ <element
60
+ location="src/java/com/orbitz/dc/service/hc/HcModifyReservationResponse.java"
61
+ size="849"/>
62
+ <checkpoint/>
63
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcModifyReservationRequest.java&quot; - ok
64
+ </message>
65
+ <element
66
+ location="src/java/com/orbitz/dc/service/hc/HcModifyReservationRequest.java"
67
+ size="1806"/>
68
+ <checkpoint/>
69
+ <message>Content (3 K) of &quot;src/java/com/orbitz/dc/core/RateInfo.java&quot; - ok
70
+ </message>
71
+ <element
72
+ location="src/java/com/orbitz/dc/core/RateInfo.java"
73
+ size="3495"/>
74
+ <checkpoint/>
75
+ <message>Content (2 K) of &quot;src/java/com/orbitz/dc/core/VariableRateInfo.java&quot; - ok
76
+ </message>
77
+ <element
78
+ location="src/java/com/orbitz/dc/core/VariableRateInfo.java"
79
+ size="2523"/>
80
+ <checkpoint/>
81
+ <message>Content (2 K) of &quot;src/java/com/orbitz/dc/core/RATE_CATEGORY.java&quot; - ok
82
+ </message>
83
+ <element
84
+ location="src/java/com/orbitz/dc/core/RATE_CATEGORY.java"
85
+ size="2513"/>
86
+ <checkpoint/>
87
+ <message>Content (2 K) of &quot;src/java/com/orbitz/dc/core/RATE_COVERAGE.java&quot; - ok
88
+ </message>
89
+ <element
90
+ location="src/java/com/orbitz/dc/core/RATE_COVERAGE.java"
91
+ size="2537"/>
92
+ <checkpoint/>
93
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/core/FlatRateInfo.java&quot; - ok
94
+ </message>
95
+ <element
96
+ location="src/java/com/orbitz/dc/core/FlatRateInfo.java"
97
+ size="975"/>
98
+ <checkpoint/>
99
+ <message>Content (2 K) of &quot;src/java/com/orbitz/dc/core/HcItinerary.java&quot; - ok
100
+ </message>
101
+ <element
102
+ location="src/java/com/orbitz/dc/core/HcItinerary.java"
103
+ size="2707"/>
104
+ <checkpoint/>
105
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcSearch.java&quot; - ok
106
+ </message>
107
+ <element
108
+ location="src/java/com/orbitz/dc/service/hc/HcSearch.java"
109
+ size="1123"/>
110
+ <checkpoint/>
111
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcCreateReservation.java&quot; - ok
112
+ </message>
113
+ <element
114
+ location="src/java/com/orbitz/dc/service/hc/HcCreateReservation.java"
115
+ size="1146"/>
116
+ <checkpoint/>
117
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcModifyReservation.java&quot; - ok
118
+ </message>
119
+ <element
120
+ location="src/java/com/orbitz/dc/service/hc/HcModifyReservation.java"
121
+ size="1156"/>
122
+ <checkpoint/>
123
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcCancelReservation.java&quot; - ok
124
+ </message>
125
+ <element
126
+ location="src/java/com/orbitz/dc/service/hc/HcCancelReservation.java"
127
+ size="1150"/>
128
+ <checkpoint/>
129
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcRetrieveItinerary.java&quot; - ok
130
+ </message>
131
+ <element
132
+ location="src/java/com/orbitz/dc/service/hc/HcRetrieveItinerary.java"
133
+ size="1150"/>
134
+ <checkpoint/>
135
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryRequest.java&quot; - ok
136
+ </message>
137
+ <element
138
+ location="src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryRequest.java"
139
+ size="1477"/>
140
+ <checkpoint/>
141
+ <message>Content (1 K) of &quot;src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryResponse.java&quot; - ok
142
+ </message>
143
+ <element
144
+ location="src/java/com/orbitz/dc/service/hc/HcRetrieveItineraryResponse.java"
145
+ size="756"/>
146
+ <checkpoint/>
147
+ </acResponse>
data/test/t_api.rb CHANGED
@@ -9,7 +9,7 @@ class AccurevAPITest < Test::Unit::TestCase
9
9
  def test_ac_files()
10
10
  Command.instance do |cmd|
11
11
  cmd.working_dir = "test"
12
- cmd.accurev = "./acreplay.rb eg/ac-files.xml"
12
+ cmd.accurev_bin = "./acreplay.rb eg/ac-files.xml"
13
13
  end
14
14
  api = API.new( ".", "test-bogus-depot", "test-bogus-depot-0-impl" )
15
15
  # test array returns
@@ -28,11 +28,49 @@ class AccurevAPITest < Test::Unit::TestCase
28
28
  assert_equal( count, 23 )
29
29
  end
30
30
 
31
- def test_update()
31
+ def test_update_stale()
32
32
  Command.instance do |cmd|
33
- cmd.working_dir = "test"
34
- cmd.accurev = "./acreplay.rb eg/update-stale.xml"
33
+ cmd.accurev_bin = "./acreplay.rb eg/update-stale.xml"
34
+ end
35
+ api = API.new( "test", "test-bogus-depot", "test-bogus-depot-0-impl" )
36
+ begin
37
+ u_elements = api.update()
38
+ flunk( "Simulated update with stale data should throw exception" )
39
+ rescue StaleWorkspaceException => e
40
+ assert( true, "Caught expected exception" )
41
+ assert( e.error_msg =~ /workspace have been modified/ )
42
+ end
43
+ end
44
+
45
+ def test_update_nochanges()
46
+ Command.instance do |cmd|
47
+ cmd.accurev_bin = "./acreplay.rb eg/update-nochanges.xml"
48
+ end
49
+ api = API.new( "test", "test-bogus-depot", "test-bogus-depot-0-impl" )
50
+ u_elements = api.update()
51
+ assert_not_nil( u_elements )
52
+ assert( u_elements.size == 0 )
53
+ end
54
+
55
+ def test_update_newwksp()
56
+ Command.instance do |cmd|
57
+ cmd.accurev_bin = "./acreplay.rb eg/update-newwksp.xml"
58
+ end
59
+ api = API.new( "test", "test-bogus-depot", "test-bogus-depot-0-impl" )
60
+ u_elements = api.update()
61
+ assert_not_nil( u_elements )
62
+ assert_equal( 44, u_elements.length )
63
+ u_elements.each do |location|
64
+ # api.update returns a list of relative filenames
65
+ assert( location =~ /^\// )
66
+ end
67
+ # test yield and array return XXX
68
+ updated = 0
69
+ api.update().each do |location|
70
+ updated += 1
71
+ assert( location =~ /^\// )
35
72
  end
73
+ assert_equal( 44, updated )
36
74
  end
37
75
 
38
76
  end
data/test/t_command.rb CHANGED
@@ -13,7 +13,7 @@ class CommandTest < Test::Unit::TestCase
13
13
  s = cmd.accurev_cmdline( "foo", "bar" )
14
14
  assert_equal( "accurev foo bar", s )
15
15
 
16
- cmd.accurev = "xyzzy"
16
+ cmd.accurev_bin = "xyzzy"
17
17
 
18
18
  s = cmd.accurev_cmdline( "foo", "bar" )
19
19
  assert_equal( "xyzzy foo bar", s )
@@ -24,22 +24,18 @@ class CommandTest < Test::Unit::TestCase
24
24
  def test_a_e
25
25
  cmd = Command.instance
26
26
  cmd.working_dir = "test"
27
- #cmd.debug = true
28
- #cmd.debug_to = File.open( "testing.log", "w" )
29
- cmd.accurev = "./acreplay.rb eg/update-newwksp.xml"
27
+ cmd.debug = true
28
+ cmd.debug_to = File.open( "/tmp/testing.log", "w" )
29
+ cmd.accurev_bin = "./acreplay.rb eg/update-newwksp.xml"
30
30
  # test listy output
31
- list = cmd.accurev_elements( RSCM::Accurev::StatData, "files" )
32
- assert( list.size > 0 )
33
- list.each do |fd|
34
- assert( fd.respond_to?( :location ) )
31
+ acresponse = cmd.accurev( "files" )
32
+ assert_not_nil( acresponse )
33
+ assert( acresponse.kind_of?( AcResponseData ) )
34
+ assert( acresponse['element'].size == 44 )
35
+ assert( acresponse['message'].size == 44 )
36
+ acresponse['element'].each do |e|
37
+ assert( e.kind_of?( ElementData ) )
35
38
  end
36
- # test yieldy output
37
- got_any = false
38
- cmd.accurev_elements( RSCM::Accurev::StatData, "files" ) do |fd|
39
- got_any = true
40
- assert( fd.respond_to?( :location ) )
41
- end
42
- assert( got_any )
43
39
  Command.discard
44
40
  end
45
41
 
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require 'stringio'
4
+ require 'rexml/document'
5
+ require 'rscm/scm/accurev/xml'
6
+ include REXML
7
+ include RSCM::Accurev
8
+
9
+ class ReadQueue < ElementBackedClass
10
+ element_name 'readqueue'
11
+ attr_accessor :owner
12
+ end
13
+
14
+ class Book < ElementBackedClass
15
+ element_name 'book'
16
+ attr_accessor :title, :author
17
+ end
18
+
19
+ class Paper < ElementBackedClass
20
+ element_name 'paper'
21
+ attr_accessor :uri
22
+ end
23
+
24
+ class Note < ElementBackedClass
25
+ element_name 'note'
26
+ attr_accessor :text, :text_content
27
+ end
28
+
29
+ INPUT = <<-EOI
30
+ <?xml version="1.0"?>
31
+ <readqueue owner="gdf">
32
+ <book title="Iron Council" author="China Mieville" />
33
+ <paper uri="http://svk.elixus.org?SVKTutorial">
34
+ <note text="re-read" />
35
+ </paper>
36
+ <book title="Someone Comes to Town, Someone Leaves Town"
37
+ author="Cory Doctorow" />
38
+ <book title="Concurrent Programming in Java" author="Doug Lea">
39
+ <note>Need Brains</note>
40
+ </book>
41
+ </readqueue>
42
+ EOI
43
+
44
+ class XMLMapperTest < Test::Unit::TestCase
45
+ def testmap
46
+ d = Document.new( StringIO.new( INPUT ) )
47
+ assert_equal( "readqueue", d.root.name )
48
+ mapper = XMLMapper.new
49
+ o = mapper.map( d.root )
50
+ assert( o.kind_of?( ElementBackedClass ) )
51
+ assert( o.kind_of?( ReadQueue ) )
52
+ assert_equal( 3, o.children['book'].length )
53
+ assert_equal( 1, o.children['paper'].length )
54
+ paper = o.children['paper'][0]
55
+ assert( paper.kind_of?( Paper ) )
56
+ assert_equal( "paper", paper.class.element_name )
57
+ assert_equal( "http://svk.elixus.org?SVKTutorial", paper.uri )
58
+ assert_equal( 1, paper.children['note'].length )
59
+ note = paper.children['note'][0]
60
+ assert_equal( "re-read", note.text )
61
+ b = nil
62
+ o.children['book'].each do |book|
63
+ b = book if book.author == "Doug Lea"
64
+ end
65
+ assert_equal( "Concurrent Programming in Java", b.title )
66
+ note = b.children['note'][0]
67
+ assert( note.text.nil? )
68
+ assert_equal( "Need Brains", note.text_content )
69
+
70
+ # test []
71
+ assert_equal( 3, o['book'].length )
72
+ end
73
+
74
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rscm-accurev
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.2
7
- date: 2005-08-10 00:00:00 -05:00
6
+ version: 0.0.3
7
+ date: 2005-08-15 00:00:00 -05:00
8
8
  summary: "RSCM::Accurev - RSCM API for Accurev"
9
9
  require_paths:
10
10
  - lib
@@ -33,6 +33,7 @@ files:
33
33
  - bumprelease.sh
34
34
  - lib
35
35
  - LICENSE
36
+ - pkg
36
37
  - Rakefile
37
38
  - README
38
39
  - test
@@ -42,7 +43,6 @@ files:
42
43
  - lib/rscm/accurev.rb
43
44
  - lib/rscm/scm
44
45
  - lib/rscm/scm/accurev
45
- - "lib/rscm/scm/accurev/#api.rb#"
46
46
  - lib/rscm/scm/accurev/api.rb
47
47
  - lib/rscm/scm/accurev/command.rb
48
48
  - lib/rscm/scm/accurev/filterio.rb
@@ -54,9 +54,14 @@ files:
54
54
  - test/t_filterio.rb
55
55
  - test/t_load.rb
56
56
  - test/t_scrubio.rb
57
+ - test/t_xmlmapper.rb
57
58
  - test/eg/ac-files.xml
59
+ - test/eg/update-i-nochanges.xml
60
+ - test/eg/update-i-updates.xml
58
61
  - test/eg/update-newwksp.xml
62
+ - test/eg/update-nochanges.xml
59
63
  - test/eg/update-stale.xml
64
+ - test/eg/update-updates.xml
60
65
  test_files: []
61
66
  rdoc_options:
62
67
  - "--line-numbers"
@@ -1,226 +0,0 @@
1
- # -*- ruby -*-
2
- require 'rubygems'
3
- require 'rscm/base'
4
- require 'rscm/path_converter'
5
- require 'rexml/document'
6
- require 'rscm/scm/accurev/xml'
7
- require 'rscm/scm/accurev/filterio'
8
- require 'rscm/scm/accurev/command'
9
-
10
- module RSCM
11
- module Accurev; end
12
- end
13
-
14
- module RSCM::Accurev
15
-
16
- class AccurevException < Exception ; end
17
-
18
- # Indicates a failure of the update command due to unkept local modifications
19
- class StaleWorkspaceError < AccurevException; end
20
-
21
- # RSCM implementation for Accurev (http://www.accurev.com/).
22
- #
23
- # Requires an accurev cli executable on the path, and the correct
24
- # environment for ac server/principal/password.
25
- #
26
- class API < RSCM::Base
27
- register self
28
-
29
- ann :description => "Accurev Depot"
30
- attr_accessor :depot
31
-
32
- ann :description => "Backing Stream (autodetected)"
33
- attr_accessor :backing_stream
34
-
35
- ann :description => "Workspace Stream"
36
- attr_accessor :workspace_stream
37
-
38
- # defined in Base
39
- # ann :description => "Filesystem Path to Workspace"
40
- # attr_accessor :checkout_dir
41
-
42
- STATUSES = {
43
- '(backed)' => :backed,
44
- '(external)' => :external,
45
- '(modified)' => :modified,
46
- '(kept)' => :kept,
47
- '(defunct)' => :defunct,
48
- '(missing)' => :missing,
49
- '(stranded)' => :stranded,
50
- '(overlap)' => :overlap
51
- }
52
-
53
- def initialize( checkout_dir=nil, depot=nil, workspace_stream=nil )
54
- @depot = depot
55
- @checkout_dir = checkout_dir
56
- @workspace_stream = workspace_stream
57
- @backing_stream = nil # will be pulled from files cmd output
58
- end
59
-
60
- def name()
61
- return "Accurev"
62
- end
63
-
64
- # Accurev operations are atomic:
65
- def transactional?
66
- return true
67
- end
68
-
69
- # "Adds +relative_filename+ to the working copy."
70
- def add( relative_filename )
71
- raise "XXX implement me"
72
- end
73
-
74
- def edit( relative_filename )
75
- # NOP - not required for ac
76
- end
77
-
78
- # "Returns a Revisions object for the period specified ... (inclusive)."
79
- def revisions( from_identifier, to_identifier=Time.infinity )
80
- raise "XXX implement me"
81
- end
82
-
83
- # default impl depends on a correct impl of revisions()
84
- # def uptodate?( identifier=nil )
85
- #
86
- # end
87
-
88
- # def checked_out? - base
89
-
90
- # accurev actually does have triggers, but I haven't implemented that yet
91
- def supports_trigger?
92
- false
93
- end
94
-
95
- # def diff() XXX
96
-
97
-
98
- # --- ac specific
99
-
100
- def ac_files( relative_path )
101
- cmd = Command.instance
102
- # there must be a better way to do this yield->yield loopage
103
- obj = []
104
- cmd.accurev_elements( StatData, "files", relative_path ) do |fd|
105
- yield fd if block_given?
106
- obj << fd
107
- end
108
- return obj
109
- end
110
-
111
- def ac_keep( files=[], message="" )
112
- raise "XXX implement me"
113
- end
114
-
115
- def ac_promote( files=[], message="" )
116
- raise "XXX implement me"
117
- end
118
-
119
- def ac_update( relative_path="." )
120
- d = accurev( "update", relative_path )
121
- if xxx_error_stale
122
- raise StaleWorkspaceError.new( "#{relative_path} is stale -- keep/anchor all changes and re-update" )
123
- end
124
- end
125
-
126
- def ac_purge( files=[] )
127
- raise "XXX implement me"
128
- end
129
-
130
- def ac_revert( files=[] )
131
- raise "XXX implement me"
132
- end
133
-
134
- def ac_move( current_file, new_file )
135
- raise "XXX implement me"
136
- end
137
-
138
- # ac info: shows eg, backing stream
139
- # but doesn't support -fx!
140
-
141
- # ac mkws
142
- # -b orbitz-host-gt3-0-impl
143
- # -w orbitz-host-gt3-0-impl-test2_gfast
144
- # -l `pwd`
145
- # - does not support fx (argh!@)
146
- # - does not pop
147
- #
148
-
149
- # diff: /usr/local/bin/acdiff (!) --fx
150
- # (after an ac cat on the backed file)
151
- # (eg, no in-process diffing ala cvs diff)
152
- # lists modified lines in xml
153
-
154
- # checkout():
155
- # "Checks out or updates[!] contents from a central SCM
156
- # to +checkout_dir+ - a local working copy."
157
- #
158
- # "The +to_identifier+ parameter may be optionally specified to
159
- # obtain files up to a particular time or label."
160
- #
161
- # For accurev, this:
162
- # * checks to see if @checkout_dir exists and appears checked out.
163
- # If it's already checked out, this calls update().
164
- # * otherwise, this creates a new workspace stream and populates it
165
- # at @checkout_dir.
166
- #
167
- # Unknown: support for +to_identifier+ (esp for update)?
168
- #
169
- # This method yields files in the workspace and doesn't need to be
170
- # overridden.
171
- # The checkout_silent() method does the actual work.
172
- #
173
- # Only yields files, not directories
174
- def checkout( to_identifier=Time.infinite )
175
- co = PathConverter.nativepath_to_filepath( @checkout_dir )
176
- unless File.exists?( co )
177
- puts "> creating new workspace..."
178
- if @backing_stream.nil?
179
- raise "Backing stream must be set"
180
- end
181
- if @workspace_stream.nil?
182
- raise "Workspace stream must be set"
183
- end
184
- mkws_out = self.accurev_nofx( "mkws",
185
- "-b", @backing_stream,
186
- "-w", @workspace_stream,
187
- "-l", co )
188
- # sucks:
189
- if ( mkws_out =~ /already exists/ )
190
- raise AccurevException.new( mkws_out )
191
- end
192
- end
193
- puts "> Updating workspace.."
194
- self.update( to_identifier )
195
- end
196
-
197
- def update( to_identifier=Time.infinite )
198
- co = PathConverter.nativepath_to_filepath( @checkout_dir )
199
- unless File.exists?( co )
200
- raise AccurevException.new( "Workspace does not exist!" )
201
- end
202
- updated = []
203
- with_working_dir( co ) do
204
- # XXX Command...
205
- accurev_elements( UpdateData, "update" ) do |up|
206
- yield up.location
207
- updated << up.location
208
- end
209
- end
210
- return updated
211
- end
212
-
213
-
214
- # --- internals
215
-
216
- private
217
-
218
- # Takes a status flags line (eg, "(modified)(kept)")
219
- # and returns a list of status flags.
220
- def map_status( status_line )
221
- l = status_line.split( " " ).map {|x| x.trim}
222
- end
223
-
224
- end
225
-
226
- end