rally_rest_api 0.6.3 → 0.6.6

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/Manifest.txt CHANGED
@@ -18,6 +18,6 @@ test/query_result_spec.rb
18
18
  test/spec_typedef.rb
19
19
  test/tc_query_result.rb
20
20
  test/tc_rest_api.rb
21
- test/tc_rest_object.rb
21
+ test/rest_object_spec.rb
22
22
  test/tc_rest_query.rb
23
23
  test/test_helper.rb
@@ -24,6 +24,14 @@ class NilClass
24
24
  end
25
25
  end
26
26
 
27
+ class TrueClass
28
+ alias :to_q :to_s
29
+ end
30
+
31
+ class FalseClass
32
+ alias :to_q :to_s
33
+ end
34
+
27
35
  # == Generate a query string for Rally's webservice query interface.
28
36
  # Arguments are:
29
37
  # type - the type to query for
@@ -81,7 +89,7 @@ class RestQuery
81
89
  @query_string = "query=" << URI.escape(QueryBuilder.new("and", &block).to_q) if block_given?
82
90
  @query_string.gsub!("&", "%26")
83
91
  @args_for_paging = {}
84
- [:workspace, :project, :project_scope_down, :project_scope_up].each { |k| @args_for_paging[k] = args[k] if args.key?(k) }
92
+ [:workspace, :project, :project_scope_down, :project_scope_up, :order, :fetch].each { |k| @args_for_paging[k] = args[k] if args.key?(k) }
85
93
  @query_params = process_args(args)
86
94
  end
87
95
 
@@ -17,6 +17,8 @@ require File.dirname(__FILE__) + '/rest_object'
17
17
  #
18
18
  #
19
19
  class QueryResult < RestObject
20
+ include Enumerable
21
+
20
22
  attr_reader :total_result_count
21
23
  attr_reader :page_size
22
24
  attr_reader :start_index
@@ -78,4 +80,13 @@ class QueryResult < RestObject
78
80
  false
79
81
  end
80
82
 
83
+ def ref?(element) # :nodoc:
84
+ !element.nil? &&
85
+ !element.attributes["ref"].nil?
86
+ end
87
+
88
+ def terminal?(node)
89
+ !node.has_elements? || ref?(node)
90
+ end
91
+
81
92
  end
@@ -7,9 +7,8 @@ require 'ostruct'
7
7
  # RallyRestAPI - A Ruby-ized interface to Rally's REST webservice API
8
8
  #
9
9
  class RallyRestAPI
10
- include RestBuilder
11
10
 
12
- attr_reader :username, :password, :base_url, :raise_on_warning, :logger
11
+ attr_reader :username, :password, :base_url, :raise_on_warning, :logger, :builder
13
12
  attr_accessor :parse_collections_as_hash
14
13
 
15
14
  ALLOWED_TYPES = %w[subscription workspace project iteration release defect defect_suite test_case
@@ -25,29 +24,36 @@ class RallyRestAPI
25
24
  # * raise_on_warn - true|false|Exception Class. If you want exceptions raised on warnings. Default is false. if 'true' RuntimeException will be raised. If ExceptionClass, then an instance of that will be raised.
26
25
  # * logger - a Logger to log to.
27
26
  #
28
- def initialize(options = {:base_url => "https://rally1.rallydev.com/slm"})
27
+ def initialize(options = {})
29
28
  parse_options options
30
29
  end
31
30
 
32
31
  def parse_options(options)
33
32
  @username = options[:username]
34
33
  @password = options[:password]
35
- @base_url = options[:base_url]
34
+ @base_url = options[:base_url] || "https://rally1.rallydev.com/slm"
36
35
  @raise_on_warning = options[:raise_on_warning]
37
36
  @logger = options[:logger]
38
37
  @parse_collections_as_hash = options[:parse_collections_as_hash] || false
38
+
39
+ if options[:builder]
40
+ builder = options[:builder]
41
+ else
42
+ builder = RestBuilder.new(@base_url, @username, @password)
43
+ end
44
+ builder.logger = @logger if @logger
45
+ @builder = builder
39
46
  end
40
47
 
41
48
  # return an instance of User, for the currently logged in user.
42
49
  def user
43
- RestObject.new(self, read_rest("#{@base_url}/webservice/1.0/user", @username, @password))
50
+ RestObject.new(self, builder.read_rest("#{@base_url}/webservice/1.0/user", @username, @password))
44
51
  end
45
52
  alias :start :user # :nodoc:
46
53
 
47
54
  # This is deprecated, use create instead
48
55
  def method_missing(type, args, &block) # :nodoc:
49
- # raise "'#{type}' is not a supported type. Supported types are: #{ALLOWED_TYPES.inspect}" unless ALLOWED_TYPES.include?(type.to_s)
50
- create_rest(type, args, @username, @password)
56
+ raise "'#{type}' is not a supported method. Use create() instead"
51
57
  end
52
58
 
53
59
  # Create an object.
@@ -59,7 +65,7 @@ class RallyRestAPI
59
65
  # returns the created object as a RestObject.
60
66
  def create(type, values) # :yields: new_object
61
67
  # raise "'#{type}' is not a supported type. Supported types are: #{ALLOWED_TYPES.inspect}" unless ALLOWED_TYPES.include?(type.to_s)
62
- object = create_rest(type, values, @username, @password)
68
+ object = builder.create_rest(type, values, @username, @password)
63
69
  yield object if block_given?
64
70
  object
65
71
  end
@@ -68,10 +74,10 @@ class RallyRestAPI
68
74
  # Example :
69
75
  # rally.find(:artifact, :page_size => 20, :start_index => 20) { equal :name, "name" }
70
76
  # See RestQuery for more info.
71
- def find(type, args = {}, &query)
77
+ def find(type, args = {}, &query_block)
72
78
  # pass the args to RestQuery, make it generate the string and handle generating the query for the
73
79
  # next page etc.
74
- query = RestQuery.new(type, args, &query)
80
+ query = RestQuery.new(type, args, &query_block)
75
81
  query(query)
76
82
  end
77
83
 
@@ -92,7 +98,7 @@ class RallyRestAPI
92
98
 
93
99
  def query(query) # :nodoc:
94
100
  query_url = "#{@base_url}/webservice/1.0/#{query.type.to_s.to_camel}?" << query.to_q
95
- xml = read_rest(query_url, @username, @password)
101
+ xml = builder.read_rest(query_url, @username, @password)
96
102
  QueryResult.new(query, self, xml)
97
103
  end
98
104
 
@@ -4,10 +4,15 @@ require 'rexml/document'
4
4
  require 'rubygems'
5
5
  require 'builder'
6
6
 
7
- module RestBuilder # :nodoc:
7
+ class RestBuilder # :nodoc:
8
+
9
+ attr_reader :base_url, :username, :password
10
+ attr_accessor :logger
11
+
12
+ def initialize(base_url, username, password)
13
+ @base_url, @username, @password = base_url, username, password
14
+ end
8
15
 
9
- DEBUG = false
10
-
11
16
  # create_rest - convert slm builder style:
12
17
  # slm.feature(:name => "feature name")
13
18
  #
@@ -72,6 +77,7 @@ module RestBuilder # :nodoc:
72
77
  req.content_type = 'text/xml'
73
78
  http = Net::HTTP.new(url.host, url.port)
74
79
  http.use_ssl = true if url.scheme == "https"
80
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
75
81
  body = http.start { |http| http.request(req) }.body
76
82
  debug "RestBuilder#send_request result = #{body}"
77
83
  check_for_errors(body)
@@ -175,5 +181,9 @@ module RestBuilder # :nodoc:
175
181
  COLLECTION_TYPES.include?(type)
176
182
  end
177
183
 
184
+ def debug(message)
185
+ @logger.debug message if @logger
186
+ end
187
+
178
188
 
179
189
  end
@@ -1,5 +1,6 @@
1
1
  require 'logger'
2
2
  require File.dirname(__FILE__) + '/rest_builder'
3
+ require 'builder/blankslate'
3
4
 
4
5
  # == An interface to a Rally resource
5
6
  # RestObject is an adapter that is initialized with a resource ref (URL) that represents a Rally object. The adapter
@@ -18,10 +19,8 @@ require File.dirname(__FILE__) + '/rest_builder'
18
19
  #
19
20
  #
20
21
  class RestObject
21
- include RestBuilder
22
22
 
23
23
  attr_reader :username, :password, :rally_rest
24
- alias :builder :rally_rest
25
24
 
26
25
  def initialize(rally_rest, document_content)
27
26
  @rally_rest = rally_rest
@@ -36,24 +35,32 @@ class RestObject
36
35
  end
37
36
 
38
37
  private
38
+ def terminal?(node)
39
+ !node.has_elements?
40
+ end
41
+
42
+ def terminal_object(node)
43
+ if ref?(node)
44
+ low_debug "Returning RestObject for #{ node.to_s}"
45
+ return RestObject.new(rally_rest, node.to_s)
46
+ else
47
+ low_debug "Returning text #{ node.text }"
48
+ return node.text
49
+ end
50
+ end
51
+
39
52
  def parse(node)
40
- debug "parse self = #{self.object_id} node = #{ node.to_s }"
53
+ low_debug "parse self = #{self.object_id} node = #{ node.to_s }"
41
54
 
42
- if !node.has_elements?
43
- if ref?(node)
44
- debug "Returning RestObject for #{ node.to_s}"
45
- return RestObject.new(@rally_rest, node.to_s)
46
- else
47
- debug "Returning text #{ node.text }"
48
- return node.text
49
- end
55
+ if terminal? node
56
+ return terminal_object(node)
50
57
  end
58
+
51
59
  # Convert nodes with children into Hashes
52
60
  elements = {}
53
61
  class << elements
54
62
  def method_missing(sym, *args)
55
63
  value = self[sym]
56
- # raise "No matching element named #{sym}. Know values are #{self.keys.join(' ')}." if value.nil?
57
64
  value
58
65
  end
59
66
  end
@@ -61,15 +68,15 @@ class RestObject
61
68
  #Add all the element's children to the hash.
62
69
  node.each_element do |e|
63
70
  name = underscore(e.name).to_sym
64
- debug "Looking at name = #{ name }"
71
+ low_debug "Looking at name = #{ name }"
65
72
  case elements[name]
66
73
  # We've not seen this element before
67
74
  when NilClass
68
- debug "NilClass name = #{ name }"
75
+ low_debug "NilClass name = #{ name }"
69
76
  # if we have a collection of things with refObjectName attributes, collect them into a hash
70
77
  # with the refObjectName as the key, unless we're told not to make hashs out of collections.
71
78
  if named_collection?(e) && parse_collections_as_hash?
72
- debug "Named collection #{ name }"
79
+ low_debug "Named collection #{ name }"
73
80
  elements[name] = {}
74
81
  e.elements.each do |child|
75
82
  ref_name = child.attributes["refObjectName"]
@@ -81,20 +88,20 @@ class RestObject
81
88
  end
82
89
  end
83
90
  elsif collection?(e)
84
- debug "Collection #{ name }"
91
+ low_debug "Collection #{ name }"
85
92
  elements[name] = []
86
93
  e.elements.each do |child|
87
94
  elements[name] << parse(child)
88
95
  end
89
96
  else # a fully dressed object, without a name
90
- debug "Fully Dressed object, without a name #{ name }"
97
+ low_debug "Fully Dressed object, without a name #{ name }"
91
98
  elements[name] = parse(e)
92
99
  end
93
100
 
94
101
  #Non-unique child elements become arrays: We've already
95
102
  #created the array: just add the element.
96
103
  when Array
97
- debug "Array name = #{ name }"
104
+ low_debug "Array name = #{ name }"
98
105
  elements[name] << parse(e)
99
106
 
100
107
  #We haven't created the array yet: do so,
@@ -103,7 +110,7 @@ class RestObject
103
110
  # when Hash
104
111
  # raise "Don't know how to deail with a named collection we've already seen! element = #{e}"
105
112
  else
106
- debug "creating and wrapping #{elements[name]}with an array name = #{ name }"
113
+ low_debug "creating and wrapping #{elements[name]}with an array name = #{ name }"
107
114
  elements[name] = [elements[name]]
108
115
  elements[name] << parse(e)
109
116
  end
@@ -118,7 +125,7 @@ class RestObject
118
125
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
119
126
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
120
127
  tr("-", "_").
121
- downcase
128
+ downcase.gsub(/_id$/,'_i_d')
122
129
  end
123
130
 
124
131
 
@@ -134,9 +141,9 @@ class RestObject
134
141
  alias :to_q :ref
135
142
 
136
143
  # The name of the object, without having to read the entire body
137
- def name
138
- @document.root.attributes["refObjectName"]
139
- end
144
+ def name
145
+ @document.root.attributes["refObjectName"]
146
+ end
140
147
 
141
148
  # The type of the underlying resource
142
149
  def type
@@ -169,7 +176,7 @@ class RestObject
169
176
 
170
177
  def elements(read = @elements.nil?) #:nodoc:
171
178
  if read
172
- @document_content = read_rest(self.ref, @username, @password)
179
+ @document_content = builder.read_rest(self.ref, @username, @password)
173
180
  @document = REXML::Document.new @document_content
174
181
  @elements = parse(@document.root)
175
182
  end
@@ -186,14 +193,14 @@ class RestObject
186
193
 
187
194
  # update the resource. This will re-read the resource after the update
188
195
  def update(args)
189
- update_rest(self.type, self.ref, args, @username, @password)
196
+ builder.update_rest(self.type, self.ref, args, @username, @password)
190
197
  # need to deal with the block, setting up contexts etc
191
198
  self.elements(true)
192
199
  end
193
200
 
194
201
  # delete the resource
195
202
  def delete
196
- delete_rest(self.ref, @username, @password)
203
+ builder.delete_rest(self.ref, @username, @password)
197
204
  end
198
205
 
199
206
  def method_missing(sym, *args) # :nodoc:
@@ -226,8 +233,15 @@ class RestObject
226
233
  self.rally_rest.parse_collections_as_hash?
227
234
  end
228
235
 
236
+ def low_debug(message)
237
+ # debug message
238
+ end
239
+
229
240
  def debug(message)
230
- # @rally_rest.logger.debug(message) if @rally_rest.logger
241
+ @rally_rest.logger.debug(message) if @rally_rest.logger
231
242
  end
232
243
 
244
+ def builder
245
+ rally_rest.builder
246
+ end
233
247
  end
@@ -15,7 +15,13 @@ class TypeDefinition < RestObject # :nodoc:
15
15
  end
16
16
 
17
17
  def self.get_type_definition(workspace, type)
18
- typedef = workspace.type_definitions.find { |td| td.element_name == type }
18
+ # This is a hack
19
+ typedefs = case workspace.type_definitions
20
+ when Hash : workspace.type_definitions.values.flatten
21
+ when Array : workspace.type_definitions
22
+ end
23
+ typedef = typedefs.find { |td| td.element_name == type }
24
+ # end hack
19
25
  TypeDefinition.new(typedef.rally_rest, typedef.body)
20
26
  end
21
27
 
@@ -2,7 +2,7 @@ module RallyRestVersion #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 6
5
- TINY = 3
5
+ TINY = 6
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,8 +1,4 @@
1
- require 'rubygems'
2
- require 'spec'
3
- require 'builder'
4
-
5
- require File.dirname(__FILE__) + '/../lib/rally_rest_api/query_result'
1
+ require 'test_helper'
6
2
 
7
3
  context "A QueryResult" do
8
4
 
@@ -14,7 +10,7 @@ context "A QueryResult" do
14
10
  @api.stub!(:parse_collections_as_hash?).and_return true
15
11
  end
16
12
 
17
- def make_result(total, page_size, start_index)
13
+ def make_result(total = 20, page_size = 20, start_index = 1)
18
14
  result_count = total > page_size ? page_size : total
19
15
 
20
16
  b = Builder::XmlMarkup.new(:indent => 2)
@@ -79,8 +75,104 @@ context "A QueryResult" do
79
75
  result.results[19].name.should == "name19"
80
76
  end
81
77
 
78
+ specify "should return RestObjects for results" do
79
+ make_result.each { |o| o.should_be_instance_of RestObject }
80
+ end
82
81
  end
83
82
 
84
- context "A QueryResult with 2 pages" do
85
-
83
+
84
+ context "A QueryResult with full objects" do
85
+
86
+ setup do
87
+ @api = mock("RallyRestAPI")
88
+ @api.stub! :username
89
+ @api.stub! :password
90
+ @api.stub!(:logger).and_return(nil)
91
+ @api.stub!(:parse_collections_as_hash?).and_return true
92
+ end
93
+
94
+ def make_result(total = 20, page_size = 20, start_index = 1)
95
+ result_count = total > page_size ? page_size : total
96
+
97
+ b = Builder::XmlMarkup.new(:indent => 2)
98
+ xml = b.QueryResult {
99
+ b.TotalResultCount total
100
+ b.PageSize page_size
101
+ b.StartIndex start_index
102
+
103
+ if (result_count > 0)
104
+ b.Results {
105
+ result_count.times do |i|
106
+ b.RefElement(:ref => "http", :refObjectName => "name#{i}") {
107
+ b.Name("This is the name for #{i}")
108
+ }
109
+ end
110
+ }
111
+ else
112
+ b.Results
113
+ end
114
+ }
115
+
116
+ QueryResult.new(nil, @api, xml)
117
+ end
118
+
119
+ def query_result_xml(&block)
120
+ QueryResult.new(nil, @api, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>#{xml &block}")
121
+ end
122
+
123
+ def xml
124
+ yield Builder::XmlMarkup.new(:indent => 2)
125
+ end
126
+
127
+ specify "should return RestObjects for results" do
128
+ make_result.each { |o| o.should_be_instance_of RestObject }
129
+ end
130
+
131
+ specify "full object collections should lazy load only once" do
132
+ rest_builder = mock("RestBuilder")
133
+ rest_builder.
134
+ should_receive(:read_rest).
135
+ twice.
136
+ with(:any_args).
137
+ and_return( xml do |b|
138
+ b.iteration {
139
+ b.Name("name1")
140
+ b.StartDate("12/12/01")
141
+ }
142
+ end )
143
+
144
+ @api = RallyRestAPI.new(:builder => rest_builder)
145
+
146
+ object = query_result_xml do |b|
147
+ b.QueryResult {
148
+ b.TotalResultCount 2
149
+ b.PageSize 20
150
+ b.StartIndex 1
151
+
152
+ b.Results {
153
+ b.Card(:ref => "http", :refObjectName => "Card1") {
154
+ b.Name("Card1")
155
+ b.Description("Description1")
156
+ b.Iteration(:ref => "http", :refObjectName => "name1")
157
+ }
158
+ b.Card(:ref => "http", :refObjectName => "Card2") {
159
+ b.Name("Card2")
160
+ b.Description("Description2")
161
+ b.Iteration(:ref => "http", :refObjectName => "name1")
162
+ }
163
+ }
164
+ }
165
+ end
166
+
167
+ object.total_result_count.should_be 2
168
+ object.results.should_be_instance_of Array
169
+ object.each_with_index do |c, i|
170
+ c.name.should == "Card#{i + 1}"
171
+ c.description.should == "Description#{i + 1}"
172
+ c.iteration.start_date.should == "12/12/01"
173
+ c.iteration.start_date.should == "12/12/01"
174
+ end
175
+
176
+ end
177
+
86
178
  end
@@ -0,0 +1,288 @@
1
+
2
+ require 'test_helper'
3
+
4
+ context "a RestObject" do
5
+
6
+ setup do
7
+ @api = RallyRestAPI.new
8
+ end
9
+
10
+ def rest_object(xml)
11
+ RestObject.new(@api, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>#{xml}")
12
+ end
13
+
14
+ def rest_object_xml(&block)
15
+ RestObject.new(@api, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>#{xml &block}")
16
+ end
17
+
18
+ def b
19
+ @b ||= Builder::XmlMarkup.new(:indent => 2)
20
+ end
21
+
22
+ def xml
23
+ yield Builder::XmlMarkup.new(:indent => 2)
24
+ end
25
+
26
+ specify "should return the type of the resource from a ref" do
27
+ o = rest_object_xml do |b|
28
+ b.Object(:refObjectName => "name", :ref => "ref", :type => "Defect")
29
+ end
30
+ o.type.should == "Defect"
31
+ end
32
+
33
+ specify "should return the type of the resource from full object" do
34
+ o = rest_object_xml do |b|
35
+ b.Defect(:refObjectName => "name", :ref => "ref") {
36
+ b.Name("name")
37
+ }
38
+ end
39
+ o.type.should == "Defect"
40
+ end
41
+
42
+ specify "should return the ref of the resource " do
43
+ o = rest_object_xml do |b|
44
+ b.Defect(:refObjectName => "name", :ref => "ref") {
45
+ b.Name("name")
46
+ }
47
+ end
48
+ o.ref.should == "ref"
49
+ end
50
+
51
+ specify "should underscore element names" do
52
+ o = rest_object(%Q(<Object ref="bla">
53
+ <TextNode>text</TextNode>
54
+ </Object>))
55
+ o.text_node.should_not_be nil
56
+ o.TextNode.should_be nil
57
+ o.Text_Node.should_be nil
58
+ o.textnode.should_be nil
59
+ end
60
+
61
+ specify "should underscore elements ending in 'ID' correctly" do
62
+ o = rest_object(%Q(<Object ref="bla">
63
+ <SalesforceCaseID>12345</SalesforceCaseID>
64
+ </Object>))
65
+ o.salesforce_case_i_d.should == "12345"
66
+ end
67
+
68
+ specify "should return text nodes" do
69
+ xml = %Q(<Object ref="bla">
70
+ <TextNode>text</TextNode>
71
+ </Object>)
72
+ rest_object(xml).text_node.should == "text"
73
+ end
74
+
75
+ specify "should return nested text nodes" do
76
+ xml = %Q(<Object ref="bla">
77
+ <Nested>
78
+ <TextNode>text</TextNode>
79
+ </Nested>
80
+ </Object>)
81
+ rest_object(xml).nested.text_node.should == "text"
82
+ end
83
+
84
+ specify "ref elements should lazy read" do
85
+ rest_builder = mock("RestBuilder")
86
+ rest_builder.
87
+ should_receive(:read_rest).
88
+ with(:any_args).
89
+ and_return( xml do |b|
90
+ b.TestCase {
91
+ b.Name("name1")
92
+ b.Description("Description")
93
+ }
94
+ end )
95
+
96
+ @api = RallyRestAPI.new(:builder => rest_builder)
97
+
98
+ object = rest_object_xml do |b|
99
+ b.TestCase(:ref => "http", :name => "name1")
100
+ end
101
+ object.description.should == "Description"
102
+ end
103
+
104
+ specify "nested ref elements should lazy read" do
105
+ rest_builder = mock("RestBuilder")
106
+ rest_builder.
107
+ should_receive(:read_rest).
108
+ with(:any_args).
109
+ and_return( xml do |b|
110
+ b.TestCase {
111
+ b.Name("name1")
112
+ b.Description("Description")
113
+ }
114
+ end )
115
+
116
+ @api = RallyRestAPI.new(:builder => rest_builder)
117
+
118
+ object = rest_object_xml do |b|
119
+ b.Defect(:ref => "http") {
120
+ b.TestCase(:ref => "http", :refObjectName => "name1")
121
+ }
122
+ end
123
+ object.test_case.should_be_instance_of RestObject
124
+ object.test_case.type.should == "TestCase"
125
+ object.test_case.description.should == "Description"
126
+ end
127
+
128
+ specify "nested ref elements should lazy read only once" do
129
+ rest_builder = mock("RestBuilder")
130
+ rest_builder.
131
+ should_receive(:read_rest).
132
+ once.
133
+ with(:any_args).
134
+ and_return( xml do |b|
135
+ b.TestCase {
136
+ b.Name("name1")
137
+ b.Description("Description")
138
+ }
139
+ end )
140
+
141
+ @api = RallyRestAPI.new(:builder => rest_builder)
142
+
143
+ object = rest_object_xml do |b|
144
+ b.Defect(:ref => "http") {
145
+ b.TestCase(:ref => "http", :refObjectName => "name1")
146
+ }
147
+ end
148
+ object.test_case.description.should == "Description"
149
+ object.test_case.description.should == "Description"
150
+ end
151
+
152
+ specify "full object collections should lazy load only once" do
153
+ rest_builder = mock("RestBuilder")
154
+ rest_builder.
155
+ should_receive(:read_rest).
156
+ once.
157
+ with(:any_args).
158
+ and_return( xml do |b|
159
+ b.iteration {
160
+ b.Name("name1")
161
+ b.StartDate("12/12/01")
162
+ }
163
+ end )
164
+
165
+ @api = RallyRestAPI.new(:builder => rest_builder)
166
+
167
+ object = rest_object_xml do |b|
168
+ b.QueryResult {
169
+ b.Results {
170
+ b.Card(:ref => "http", :refObjectName => "Card1") {
171
+ b.Name("Card1")
172
+ b.Description("Description1")
173
+ b.Iteration(:ref => "http", :refObjectName => "name1")
174
+ }
175
+ b.Card(:ref => "http", :refObjectName => "Card2") {
176
+ b.Name("Card2")
177
+ b.Description("Description2")
178
+ b.Iteration(:ref => "http", :refObjectName => "name1")
179
+ }
180
+ }
181
+ }
182
+ end
183
+
184
+ object.results.length.should_be 2
185
+ object.results.should_be_instance_of Array
186
+ object.results.first.description.should == "Description1"
187
+ object.results.first.iteration.start_date.should == "12/12/01"
188
+ object.results.first.iteration.start_date.should == "12/12/01"
189
+ end
190
+
191
+ specify "collections are parsed as arrays " do
192
+ @api = RallyRestAPI.new(:parse_collections_as_hash => false)
193
+
194
+ object = rest_object_xml do |b|
195
+ b.Story(:refObjectName => "story", :ref => "http") {
196
+ b.Tasks {
197
+ b.Task(:refObjectName => "task1", :ref => "ref")
198
+ b.Task(:refObjectName => "task2", :ref => "ref")
199
+ b.Task(:refObjectName => "task3", :ref => "ref")
200
+ }
201
+ }
202
+ end
203
+ object.tasks.should_be_instance_of Array
204
+ object.tasks.length.should_be 3
205
+ end
206
+
207
+ specify "collections are parsed as hashes " do
208
+ @api = RallyRestAPI.new(:parse_collections_as_hash => true)
209
+
210
+ object = rest_object_xml do |b|
211
+ b.Story(:refObjectName => "story", :ref => "http") {
212
+ b.Tasks {
213
+ b.Task(:refObjectName => "task1", :ref => "ref")
214
+ b.Task(:refObjectName => "task2", :ref => "ref")
215
+ b.Task(:refObjectName => "task3", :ref => "ref")
216
+ }
217
+ }
218
+ end
219
+ object.tasks.should_be_instance_of Hash
220
+ object.tasks.length.should_be 3
221
+ end
222
+
223
+ specify "when collections are parsed as hashes, dup names should be in arrays " do
224
+ @api = RallyRestAPI.new(:parse_collections_as_hash => true)
225
+
226
+ object = rest_object_xml do |b|
227
+ b.Story(:refObjectName => "story", :ref => "http") {
228
+ b.Tasks {
229
+ b.Task(:refObjectName => "task1", :ref => "ref")
230
+ b.Task(:refObjectName => "task2", :ref => "ref")
231
+ b.Task(:refObjectName => "task2", :ref => "ref")
232
+ }
233
+ }
234
+ end
235
+ object.tasks.should_be_instance_of Hash
236
+ object.tasks.length.should_be 2
237
+ object.tasks["task2"].should_be_instance_of Array
238
+ object.tasks["task2"].length.should_be 2
239
+ end
240
+
241
+ specify "when collections are parsed as hashes, unnamed elements should be in arrays " do
242
+ @api = RallyRestAPI.new(:parse_collections_as_hash => true)
243
+
244
+ object = rest_object_xml do |b|
245
+ b.Story(:refObjectName => "story", :ref => "http") {
246
+ b.Tasks {
247
+ b.Task(:ref => "ref")
248
+ b.Task(:ref => "ref")
249
+ b.Task(:ref => "ref")
250
+ }
251
+ }
252
+ end
253
+ object.tasks.should_be_instance_of Array
254
+ object.tasks.length.should_be 3
255
+ end
256
+
257
+
258
+ specify "should lazy read from collections" do
259
+ rest_builder = mock("RestBuilder")
260
+ rest_builder.
261
+ should_receive(:read_rest).
262
+ twice.
263
+ with(:any_args).
264
+ and_return( xml do |b|
265
+ b.Task {
266
+ b.Name("name1")
267
+ b.Description("Description")
268
+ }
269
+ end )
270
+
271
+ @api = RallyRestAPI.new(:builder => rest_builder, :parse_collections_as_hash => false)
272
+
273
+ object = rest_object_xml do |b|
274
+ b.Story(:refObjectName => "story", :ref => "http") {
275
+ b.Tasks {
276
+ b.Task(:refObjectName => "task1", :ref => "ref")
277
+ b.Task(:refObjectName => "task2", :ref => "ref")
278
+ b.Task(:refObjectName => "task3", :ref => "ref")
279
+ }
280
+ }
281
+ end
282
+
283
+ object.tasks.first.description.should == "Description"
284
+ object.tasks.first.description.should == "Description"
285
+ object.tasks.last.description.should == "Description"
286
+ end
287
+
288
+ end
data/test/tc_rest_api.rb CHANGED
@@ -9,9 +9,9 @@ class RestApiTestCase < Test::Unit::TestCase
9
9
  # We need to override post_xml from RestBuilder, So redefine it here
10
10
  # We want this post_xml to set the xml that was received onto the testcase
11
11
  # So create an xml= on the test, and pass the test into the RallyRestAPI
12
- module LocalBuilder
13
- def test=(test)
14
- @test = test
12
+ class LocalBuilder < RestBuilder
13
+ def initialize(test)
14
+ @test = test
15
15
  end
16
16
  def post_xml(url, xml, username, password)
17
17
  @test.xml = xml
@@ -25,11 +25,7 @@ class RestApiTestCase < Test::Unit::TestCase
25
25
 
26
26
  def setup
27
27
  super
28
- @api = RallyRestAPI.new
29
- class << @api
30
- include LocalBuilder
31
- end
32
- @api.test = self
28
+ @api = RallyRestAPI.new(:builder => LocalBuilder.new(self))
33
29
  end
34
30
 
35
31
  def preamble
@@ -41,7 +37,7 @@ class RestApiTestCase < Test::Unit::TestCase
41
37
  # - String values
42
38
  def test_basic_create
43
39
  xml = "#{preamble}<Feature><Name>name</Name></Feature>"
44
- @api.feature(:name => "name")
40
+ @api.create(:feature, :name => "name")
45
41
  assert_equal(xml, @xml)
46
42
  end
47
43
 
@@ -95,5 +91,9 @@ class RestApiTestCase < Test::Unit::TestCase
95
91
  @api.create(:feature, :dependents => [uc1, uc2])
96
92
  assert_equal(xml, @xml)
97
93
  end
94
+
95
+ def test_default_base_url_should_be_rally_proudction
96
+ assert_equal("https://rally1.rallydev.com/slm", @api.base_url)
97
+ end
98
98
 
99
99
  end
@@ -175,7 +175,19 @@ class TestRestQuery < Test::Unit::TestCase
175
175
  :order => [:package, :owner, :desc]) { equal :name, "name"}.to_q
176
176
  assert_equal("query=(Name = name)&order=Package, Owner desc", URI.unescape(query_string) )
177
177
  end
178
-
178
+
179
+ def test_new_with_true_fetch_arg
180
+ query_string = RestQuery.new(:artifact,
181
+ :fetch => true) { equal :name, "name"}.to_q
182
+ assert_equal("query=(Name = name)&fetch=true", URI.unescape(query_string) )
183
+ end
184
+
185
+ def test_new_with_false_fetch_arg
186
+ query_string = RestQuery.new(:artifact,
187
+ :fetch => false) { equal :name, "name"}.to_q
188
+ assert_equal("query=(Name = name)&fetch=false", URI.unescape(query_string) )
189
+ end
190
+
179
191
  def test_next_page
180
192
  q = RestQuery.new(:artifact,
181
193
  :pagesize => 10,
data/test/test_helper.rb CHANGED
@@ -1,2 +1,4 @@
1
1
  require 'test/unit'
2
+ require 'rubygems'
3
+ require 'spec'
2
4
  require File.dirname(__FILE__) + '/../lib/rally_rest_api'
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: rally_rest_api
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.6.3
7
- date: 2007-01-03 00:00:00 -07:00
6
+ version: 0.6.6
7
+ date: 2007-02-06 00:00:00 -07:00
8
8
  summary: A ruby-ized interface to Rally's REST webservices API
9
9
  require_paths:
10
10
  - lib
@@ -49,18 +49,18 @@ files:
49
49
  - test/spec_typedef.rb
50
50
  - test/tc_query_result.rb
51
51
  - test/tc_rest_api.rb
52
- - test/tc_rest_object.rb
52
+ - test/rest_object_spec.rb
53
53
  - test/tc_rest_query.rb
54
54
  - test/test_helper.rb
55
55
  test_files:
56
56
  - test/tc_rest_api.rb
57
- - test/tc_rest_object.rb
58
57
  - test/query_result_spec.rb
59
58
  - test/tc_rest_query.rb
60
59
  - test/test_helper.rb
61
60
  - test/tc_query_result.rb
62
61
  - test/spec_typedef.rb
63
62
  - test/attribute_definition_spec.rb
63
+ - test/rest_object_spec.rb
64
64
  rdoc_options: []
65
65
 
66
66
  extra_rdoc_files: []
@@ -1,166 +0,0 @@
1
-
2
- require 'test/unit'
3
- require 'test/unit/testcase'
4
- require 'logger'
5
-
6
- require 'rubygems'
7
- require 'rally_rest_api'
8
-
9
-
10
- # We need to override read_rest from RestBuilder, So redefine it here
11
- # We need a way to inject the xml that will be read by a RestObject when
12
- # it is converting itself from a ref.
13
- # So change the RestBuilder, which RestObject includes, and add a test=
14
- # so we can send in this instance of the test case
15
- module MyRestBuilder
16
- def read_rest(url, username, password)
17
- @@test.url = url
18
- @@test.read_xml
19
- end
20
-
21
- def MyRestBuilder.test=(test)
22
- @@test = test
23
- end
24
- end
25
-
26
- $GLOBAL_BINDING = binding
27
-
28
- class RestObjectTestCase < Test::Unit::TestCase
29
- def url=(url)
30
- @url = url
31
- end
32
-
33
- def setup
34
- super
35
- @api = RallyRestAPI.new
36
-
37
- s = <<-"EOF"
38
- class Class
39
- alias :old_new :new
40
- def new(*args)
41
- result = old_new(*args)
42
- if result.kind_of?(RestObject)
43
- class << result
44
- include MyRestBuilder
45
- end
46
- end
47
- return result
48
- end
49
- end
50
- EOF
51
- eval(s, $GLOBAL_BINDING)
52
-
53
- end
54
-
55
- def teardown
56
- super
57
- s = <<-"EOF"
58
- class Class
59
- alias :new :old_new
60
- end
61
- EOF
62
- eval(s, $GLOBAL_BINDING)
63
- end
64
-
65
- def rest_object(xml)
66
- o = RestObject.new(@api, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>#{xml}")
67
- MyRestBuilder.test = self
68
- o
69
- end
70
-
71
- def test_equal_same_instance
72
- xml = %Q(<Object ref="http"><TextNode>text</TextNode></Object>)
73
- r1 = r2 = rest_object(xml)
74
- assert(r1 == r2)
75
- end
76
-
77
- def test_equal_with_nil
78
- xml = %Q(<Object ref="http"><TextNode>text</TextNode></Object>)
79
- r1 = rest_object(xml)
80
- assert(r1 != nil)
81
- end
82
-
83
- def test_equal_refs
84
- r1 = rest_object(%Q(<Object ref="bla"><TextNode>text</TextNode></Object>))
85
- r2 = rest_object(%Q(<Object ref="bla"><TextNode>not text</TextNode></Object>))
86
- assert_equal(r1, r2)
87
- end
88
-
89
-
90
- def test_basic_text_node
91
- xml = %Q(<Object ref="bla"><TextNode>text</TextNode></Object>)
92
- assert_equal("text", rest_object(xml).text_node)
93
- end
94
-
95
- def test_nested_text_node
96
- xml = %Q(<Object ref="bla"><Nested ref="bla/nested"><TextNode>text</TextNode></Nested></Object>)
97
- assert_equal("text", rest_object(xml).nested.text_node)
98
- end
99
-
100
- # make sure RestObject are being created
101
- def test_ref_node
102
- xml = %Q(<Object ref="http"><RefElement ref="http" refObjectName="name1"/></Object>)
103
- assert_equal(RestObject, rest_object(xml).class)
104
- end
105
-
106
- def test_ref_node_reading
107
- xml = %Q(<Object ref="bla"><Top ref="blabla"><RefElement ref="http" refObjectName="name1"/></Top></Object>)
108
- # This will be the xml returned from read_rest
109
- class << self
110
- def read_xml
111
- %Q(<RefElement><Foo>foo</Foo></RefElement>)
112
- end
113
- end
114
-
115
- assert_equal("foo", rest_object(xml).top.ref_element.foo)
116
- end
117
-
118
- def test_ref_node_collection_reading
119
- xml = %Q(<Object ref="bla"><Top ref="bla"><RefElements><RefElement ref="http1" refObjectName="name1"/><RefElement ref="http2" refObjectName="name2"/></RefElements></Top></Object>)
120
- # This will be the xml returned from read_rest
121
- class << self
122
- def read_xml
123
- %Q(<RefElement ref="http3" refObjectName="name1"><Name>name</Name><Description>Desc</Description></RefElement>)
124
- end
125
- end
126
-
127
- assert_equal(2, rest_object(xml).top.ref_elements.length)
128
- ref_element = rest_object(xml).top.ref_elements[0]
129
-
130
- assert_equal("http1", ref_element.ref)
131
- assert_equal("Desc", ref_element.description) # Force a read
132
- assert_equal("http3", ref_element.ref) # Post read
133
- end
134
-
135
- def test_ref_collection_with_dups_is_array
136
- xml = %Q(<Object ref="bla"><Top ref="bla"><RefElements><RefElement ref="http1" refObjectName="name1"/><RefElement ref="http2" refObjectName="name2"/><RefElement ref="http3" refObjectName="name2"/><RefElement ref="http3" refObjectName="name2"/></RefElements></Top></Object>)
137
- # This will be the xml returned from read_rest
138
- class << self
139
- def read_xml
140
- %Q(<RefElement ref="http3" refObjectName="name1"><Name>name</Name><Description>Desc</Description></RefElement>)
141
- end
142
- end
143
-
144
- assert_equal(4, rest_object(xml).top.ref_elements.length)
145
- end
146
-
147
- def test_named_collection_reading
148
- xml = %Q(<Object ref="bla"><Top ref="bla"><NamedElements><NamedElement ref="http1" refObjectName="name1"><Name>name1</Name></NamedElement><NamedElement ref="http2" refObjectName="name2"><Name>name2</Name></NamedElement></NamedElements></Top></Object>)
149
-
150
- assert_equal(2, rest_object(xml).top.named_elements.length)
151
- named_element1 = rest_object(xml).top.named_elements[0]
152
- assert_equal("name1", named_element1.name)
153
-
154
- named_element2 = rest_object(xml).top.named_elements[1]
155
- assert_equal("name2", named_element2.name)
156
- end
157
-
158
- def test_unnamed_collection_reading
159
- xml = %Q(<Object ref="bla"><Top ref="bla"><NamedElements><NamedElement ref="http1"><Name>name1</Name></NamedElement><NamedElement ref="http2"><Name>name2</Name></NamedElement></NamedElements></Top></Object>)
160
-
161
- assert_equal(2, rest_object(xml).top.named_elements.length)
162
- assert_equal("name1", rest_object(xml).top.named_elements[0].name)
163
- assert_equal("name2", rest_object(xml).top.named_elements[1].name)
164
- end
165
-
166
- end