rally_rest_api 0.5.9 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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: []