rally_rest_api 0.5.9 → 0.6.0

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/Rakefile CHANGED
@@ -39,7 +39,7 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
39
39
  p.summary = DESCRIPTION
40
40
  p.url = HOMEPATH
41
41
  p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
42
- p.test_globs = ["test/**/tc_*.rb"]
42
+ p.test_globs = ["test/*.rb"]
43
43
  p.clean_globs = CLEAN #An array of file patterns to delete on clean.
44
44
  p.extra_deps = ["builder"]
45
45
 
@@ -18,6 +18,12 @@ class Symbol # :nodoc: all
18
18
  end
19
19
  end
20
20
 
21
+ class NilClass
22
+ def to_q
23
+ "null"
24
+ end
25
+ end
26
+
21
27
  # == Generate a query string for Rally's webservice query interface.
22
28
  # Arguments are:
23
29
  # type - the type to query for
@@ -73,6 +79,8 @@ class RestQuery
73
79
  def initialize(type, args = {}, &block)
74
80
  @type = type
75
81
  @query_string = "query=" << URI.escape(QueryBuilder.new("and", &block).to_q) if block_given?
82
+ @args_for_paging = {}
83
+ [:workspace, :project, :project_scope_down, :project_scope_up].each { |k| @args_for_paging[k] = args[k] if args.key?(k) }
76
84
  @query_params = process_args(args)
77
85
  end
78
86
 
@@ -91,7 +99,7 @@ class RestQuery
91
99
  end
92
100
 
93
101
  def next_page(args)
94
- @query_params = process_args(args)
102
+ @query_params = process_args(args.merge(@args_for_paging))
95
103
  self
96
104
  end
97
105
 
@@ -1,4 +1,4 @@
1
- require 'rally_rest_api/rest_object'
1
+ require File.dirname(__FILE__) + '/rest_object'
2
2
 
3
3
  # == An interface to the paged query result
4
4
  #
@@ -36,7 +36,6 @@ class QueryResult < RestObject
36
36
  @start_index = elements[:start_index].to_i
37
37
  end
38
38
 
39
- require 'active_support/breakpoint'
40
39
  # fetch the next page of results. Uses the original query to generate the query string.
41
40
  def next_page
42
41
  @rally_rest.query(@query.next_page(:start => self.start_index + self.page_size,
@@ -54,7 +53,7 @@ require 'active_support/breakpoint'
54
53
  yield result.dup
55
54
  end
56
55
  current_result = current_result.next_page if current_result.more_pages?
57
- end while last_result != current_result
56
+ end while !last_result.equal? current_result
58
57
  end
59
58
 
60
59
  # return the first element. Useful for queries that return only one result
@@ -3,10 +3,6 @@ require 'uri'
3
3
  require 'rexml/document'
4
4
  require 'ostruct'
5
5
 
6
- require 'rally_rest_api/rest_builder'
7
- require 'rally_rest_api/query'
8
- require 'rally_rest_api/rest_object'
9
- require 'rally_rest_api/query_result'
10
6
 
11
7
  #
12
8
  # RallyRestAPI - A Ruby-ized interface to Rally's REST webservice API
@@ -79,7 +75,7 @@ class RallyRestAPI
79
75
  end
80
76
 
81
77
  # find all object of a given type. Base types work also (.e.g :artifact)
82
- def find_all(type, *args)
78
+ def find_all(type, args = {})
83
79
  find(type, args) { gt :object_i_d, "0" }
84
80
  end
85
81
 
@@ -170,7 +170,7 @@ module RestBuilder # :nodoc:
170
170
  end
171
171
 
172
172
 
173
- COLLECTION_TYPES = [:dependents, :dependencies, :defects]
173
+ COLLECTION_TYPES = [:dependents, :dependencies, :defects, :duplicates]
174
174
  def collection_type?(type)
175
175
  COLLECTION_TYPES.include?(type)
176
176
  end
@@ -1,4 +1,4 @@
1
- require 'rally_rest_api/rest_builder'
1
+ require File.dirname(__FILE__) + '/rest_builder'
2
2
 
3
3
  # == An interface to a Rally resource
4
4
  # RestObject is an adapter that is initialized with a resource ref (URL) that represents a Rally object. The adapter
@@ -145,6 +145,11 @@ class RestObject
145
145
  @document.root.attributes["type"] || @document.root.name
146
146
  end
147
147
 
148
+ # the type as symbol
149
+ def type_as_symbol
150
+ underscore(self.type).intern
151
+ end
152
+
148
153
  # The oid of the underlying resource
149
154
  def oid
150
155
  self.elements[:object_id]
@@ -173,6 +178,10 @@ class RestObject
173
178
  @elements
174
179
  end
175
180
 
181
+ def ==(other)
182
+ other.ref == self.ref
183
+ end
184
+
176
185
  public
177
186
 
178
187
  # update the resource. This will re-read the resource after the update
@@ -1,49 +1,86 @@
1
+ require 'pp'
2
+ class TypeDefinition < RestObject # :nodoc:
1
3
 
2
- require 'rally_rest_api/rest_object'
4
+ def self.cached_type_definitions
5
+ @@cached_type_definitions ||= {}
6
+ end
3
7
 
4
- class TypeDefinition < RestObject # :nodoc:
8
+ def self.cached_type_definition(workspace_domain_object, type = workspace_domain_object.type)
9
+ key = make_key(workspace_domain_object.workspace, type)
5
10
 
6
- def self.cached_type_definition(workspace_domain_object)
7
- # todo, actually cache the value
8
- typedef = workspace_domain_object.workspace.type_definitions.values.find do |typedef|
9
- puts "#{workspace_domain_object.type} == #{typedef.element_name}"
10
- workspace_domain_object.type == typedef.element_name
11
+ unless cached_type_definitions.key? key
12
+ cached_type_definitions[key] = get_type_definition(workspace_domain_object.workspace, type)
11
13
  end
14
+ cached_type_definitions[key]
15
+ end
16
+
17
+ def self.get_type_definition(workspace, type)
18
+ mangle_type_definitions(workspace.type_definitions)
19
+ typedef = workspace.type_definitions[type]
12
20
  TypeDefinition.new(typedef.rally_rest, typedef.body)
13
21
  end
14
22
 
23
+ # This is a hack
24
+ # Strip spaces from the name, and put the values back in to the hash
25
+ def self.mangle_type_definitions(type_definitions)
26
+ type_definitions.each do |key, value|
27
+ type_definitions[key.gsub(" ", "")] = value if key.include? " "
28
+ end
29
+ end
30
+
31
+ def self.make_key(workspace, type)
32
+ # Parent typedefs don't have a workspace, so key them appropriately
33
+ ref = workspace.ref rescue ""
34
+ type + ref
35
+ end
36
+
37
+ def custom_attributes
38
+ collect_attributes { |element_name, attrdef| attrdef.custom == "true" }
39
+ end
40
+
41
+ # custom and constrained, i.e. a custom dropdown
42
+ def custom_constrained_attributes
43
+ collect_attributes { |element_name, attrdef| attrdef.custom == "true" && attrdef.constrained == "true" }
44
+ end
45
+ alias custom_dropdown_attributes custom_constrained_attributes
46
+
47
+ def constrained_attributes
48
+ collect_attributes { |element_name, attrdef| attrdef.constrained == "true" }
49
+ end
50
+
15
51
  def collection_attributes(include_parent = false)
16
- collect_attributes_of_type("COLLECTION", include_parent)
52
+ collect_attributes(include_parent) { |element_name, attrdef| attrdef.attribute_type == "COLLECTION" }
17
53
  end
18
54
 
19
55
  def object_attributes(include_parent = false)
20
- collect_attributes_of_type("OBJECT", include_parent)
56
+ collect_attributes(include_parent) do |element_name, attrdef|
57
+ attrdef.attribute_type == "OBJECT"
58
+ end
21
59
  end
22
60
 
23
61
  def reference_attributes(include_parent = false)
24
62
  collection_attributes(include_parent).merge object_attributes(include_parent)
25
63
  end
26
64
 
27
- def collect_attributes_of_type(type, include_parent)
28
- self.attributes(include_parent).inject({}) do |h, (element_name, attrdef)|
29
- h[element_name] = attrdef if attrdef.attribute_type == type
30
- h
31
- end
65
+ def collect_attributes(include_parent = false)
66
+ values = self.attributes(include_parent).find_all { |element_name, attrdef| yield element_name, attrdef }
67
+ Hash[*values.flatten]
32
68
  end
33
69
 
34
- def symbol_keyed_attributes(attribute_array)
35
- attribute_array.inject({}) do |hash, attrdef|
36
- hash[underscore(attrdef.element_name).intern] = attrdef
70
+ def symbol_keyed_attributes(attribute_hash)
71
+ return {} unless attribute_hash # some typedefs will have no attributes
72
+ attribute_hash.values.inject({}) do |hash, attrdef|
73
+ hash[underscore(attrdef.element_name).intern] = AttributeDefinition.new(self.rally_rest, attrdef)
37
74
  hash
38
75
  end
39
76
  end
40
77
 
41
78
  def attributes(include_parent = false)
42
- return symbol_keyed_attributes(self.elements[:attributes].values) unless include_parent
79
+ return symbol_keyed_attributes(self.elements[:attributes]) unless include_parent
43
80
 
44
81
  typedef = self
45
82
  all_attributes = {}
46
- until typedef.parent.nil?
83
+ until typedef.nil?
47
84
  all_attributes.merge! typedef.attributes(false)
48
85
  typedef = typedef.parent
49
86
  end
@@ -52,7 +89,17 @@ class TypeDefinition < RestObject # :nodoc:
52
89
 
53
90
  def parent
54
91
  return nil if self.elements[:parent].nil?
55
- TypeDefinition.new(self.rally_rest, self.elements[:parent].body)
92
+ cached_parent
93
+ end
94
+
95
+ def cached_parent
96
+ type_no_spaces = self.elements[:parent].name.gsub(" ", "")
97
+ key = TypeDefinition.make_key(self.workspace, type_no_spaces)
98
+ typedef = TypeDefinition.cached_type_definitions[key]
99
+ if typedef.nil?
100
+ typedef = TypeDefinition.cached_type_definitions[key] = TypeDefinition.new(self.rally_rest, self.elements[:parent].body)
101
+ end
102
+ typedef
56
103
  end
57
104
 
58
105
  end
@@ -1,8 +1,8 @@
1
1
  module RallyRestVersion #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 5
5
- TINY = 9
4
+ MINOR = 6
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'builder'
4
+ require 'ostruct'
5
+ require 'rally_rest_api'
6
+
7
+
8
+ context "An attribute definition for a custom dropdown" do
9
+
10
+ setup do
11
+ @b = Builder::XmlMarkup.new(:indent => 2)
12
+ @b.instruct!
13
+
14
+ xml = @b.AttributeDefinition(:ref => "http://bla/bla/bla",
15
+ :refObjectName => "Custom Dropdown") {
16
+ @b.Name("Custom Dropdown")
17
+ @b.AttributeType("STRING")
18
+ @b.ElementName("CustomDropdown")
19
+ @b.Constrained("true")
20
+ @b.Custom("true")
21
+
22
+ @b.AllowedValues {
23
+ @b.AllowedAttributeValue(:ref => "null") {
24
+ @b.StringValue
25
+ }
26
+ @b.AllowedAttributeValue(:ref => "null") {
27
+ @b.StringValue("Actor")
28
+ }
29
+ }
30
+ }
31
+ fake_rally_rest = OpenStruct.new(:username => "", :password => "")
32
+ @attrdef = AttributeDefinition.new(fake_rally_rest, RestObject.new(fake_rally_rest, xml).elements)
33
+ end
34
+
35
+ specify "should have 'Custom Dropdown' as the name" do
36
+ @attrdef.name.should == "Custom Dropdown"
37
+ end
38
+
39
+ specify "should return an arry of allowed values" do
40
+ @attrdef.allowed_values.should_be_instance_of Array
41
+ end
42
+
43
+ specify "should have 2 allowd values" do
44
+ @attrdef.allowed_values.length.should == 2
45
+ end
46
+
47
+ specify "should not return an array of hashes" do
48
+ @attrdef.allowed_values.each { |value| value.should_not_be_instance_of Hash }
49
+ end
50
+
51
+ specify "allowed_values should match" do
52
+ @attrdef.allowed_values.should == [nil, "Actor"]
53
+ end
54
+ end
@@ -0,0 +1,83 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'ostruct'
4
+ require 'rally_rest_api'
5
+
6
+
7
+ context "A Test Case type definition with 2 collection attribues, 1 object attributes, and 1 string attribute" do
8
+
9
+ setup do
10
+ @b = Builder::XmlMarkup.new(:indent => 2)
11
+ @b.instruct!
12
+
13
+ xml = @b.TypeDefinition(:ref => "https://rally1.rallydev.com:443/slm/webservice/1.0/typedefinition/31552422",
14
+ :refObjectName => "Test Case",
15
+ :type => "TypeDefinition") {
16
+ @b.ElementName("TestCase")
17
+ @b.Attributes {
18
+ {"Notes" => {:type => "TEXT", :element_name => "Notes", :constrained => "false", :custom => "false"},
19
+ "Test Case Result" => {:type => "COLLECTION", :element_name => "TestCaseResult", :constrained => "false", :custom => "false"},
20
+ "Foo Bar" => {:type => "COLLECTION", :element_name => "FooBar", :constrained => "false", :custom => "false"},
21
+ "Requirement" => {:type => "OBJECT", :element_name => "Requirement", :constrained => "false", :custom => "false"},
22
+ "Constrained" => {:type => "STRING", :element_name => "Constrained", :constrained => "true", :custom => "false"},
23
+ "Custom" => {:type => "STRING", :element_name => "Custom", :constrained => "false", :custom => "true"},
24
+ "ConstainedCustom" => {:type => "STRING", :element_name => "ConstrainedCustom", :constrained => "true", :custom => "true"}
25
+ }.each do |name, type|
26
+ @b.AttributeDefinition(:ref => "http://bla/bla/bla",
27
+ :refObjectName => name) {
28
+ @b.AttributeType(type[:type])
29
+ @b.ElementName(type[:element_name])
30
+ @b.Constrained(type[:constrained])
31
+ @b.Custom(type[:custom])
32
+ }
33
+ end
34
+ }
35
+ }
36
+ @typedef = TypeDefinition.new(OpenStruct.new(:username => "", :password => ""), xml)
37
+ end
38
+
39
+ specify "all attribute keys are symbols" do
40
+ @typedef.attributes.keys.each { |a| a.should_be_instance_of Symbol }
41
+ end
42
+
43
+ specify "there should be 2 collection attributes" do
44
+ @typedef.collection_attributes.size.should == 2
45
+ end
46
+
47
+ specify "the :test_case_result attribute should exist" do
48
+ @typedef.collection_attributes[:test_case_result].should_not_be_nil
49
+ end
50
+
51
+ specify "All the collection attributes keys should be symbols" do
52
+ @typedef.collection_attributes.keys.each { |a| a.should_be_instance_of Symbol }
53
+ end
54
+
55
+ specify "there should be 1 object attribute" do
56
+ @typedef.object_attributes.size.should == 1
57
+ end
58
+
59
+ specify "the :requirement attribute should exist" do
60
+ @typedef.object_attributes[:requirement].should_not_be_nil
61
+ end
62
+
63
+ specify "should have 2 constrained attributes" do
64
+ @typedef.constrained_attributes.size.should == 2
65
+ end
66
+
67
+ specify "should have 2 cusom attributes" do
68
+ @typedef.custom_attributes.size.should == 2
69
+ end
70
+
71
+ specify "should have 1 custom_constrained_attributes" do
72
+ @typedef.custom_constrained_attributes.size.should == 1
73
+ @typedef.custom_dropdown_attributes.size.should == 1
74
+ end
75
+
76
+ specify "#attributes should return instances of AttributeDefinitions" do
77
+ @typedef.attributes.values.each { |attrdef| attrdef.should_be_instance_of AttributeDefinition }
78
+ end
79
+
80
+ specify "parent should be nil" do
81
+ @typedef.parent.should_be_nil
82
+ end
83
+ end
@@ -1,7 +1,8 @@
1
1
  require 'test/unit'
2
2
  require 'test/unit/testcase'
3
3
 
4
- require 'rally_rest_api/rally_rest'
4
+ require 'rubygems'
5
+ require 'rally_rest_api'
5
6
 
6
7
  class QueryResultTestCase < Test::Unit::TestCase
7
8
 
data/test/tc_rest_api.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require 'test/unit'
2
2
  require 'test/unit/testcase'
3
3
 
4
- require 'rally_rest_api/rally_rest'
4
+ require 'rubygems'
5
+ require 'rally_rest_api'
5
6
 
6
7
  class RestApiTestCase < Test::Unit::TestCase
7
8
 
@@ -2,7 +2,9 @@
2
2
  require 'test/unit'
3
3
  require 'test/unit/testcase'
4
4
 
5
- require 'rally_rest_api/rally_rest'
5
+ require 'rubygems'
6
+ require 'rally_rest_api'
7
+
6
8
 
7
9
  # We need to override read_rest from RestBuilder, So redefine it here
8
10
  # We need a way to inject the xml that will be read by a RestObject when
@@ -1,6 +1,8 @@
1
1
 
2
2
  require "test/unit"
3
- require 'rally_rest_api/query'
3
+
4
+ require 'rubygems'
5
+ require 'rally_rest_api'
4
6
 
5
7
  class TestRestQuery < Test::Unit::TestCase
6
8
  # RestQuery.find(:feature).where { # default is and
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.5.9
7
- date: 2006-11-30 00:00:00 -07:00
6
+ version: 0.6.0
7
+ date: 2006-12-13 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
@@ -52,7 +52,10 @@ test_files:
52
52
  - test/tc_rest_api.rb
53
53
  - test/tc_rest_object.rb
54
54
  - test/tc_rest_query.rb
55
+ - test/test_helper.rb
55
56
  - test/tc_query_result.rb
57
+ - test/spec_typedef.rb
58
+ - test/attribute_definition_spec.rb
56
59
  rdoc_options: []
57
60
 
58
61
  extra_rdoc_files: []