ruby_odata 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  matrix:
2
2
  include:
3
- - rvm: 1.8.7
4
- gemfile: gemfiles/Gemfile.ruby187
3
+ # - rvm: 1.8.7
4
+ # gemfile: gemfiles/Gemfile.ruby187
5
5
  - rvm: 1.9.3
6
6
  gemfile: Gemfile
7
7
  - rvm: 2.0.0
data/CHANGELOG.md CHANGED
@@ -136,3 +136,11 @@
136
136
  * Updated the [VCR](https://github.com/myronmarston/vcr) and [WebMock](https://github.com/bblimke/webmock) gems to the latest versions (used for testing)
137
137
  * Specified activesupport ~> 3.0 (in gemfiles/ruby187) for Ruby 1.8.7 as activesupport 4 doesn't support Ruby < 1.9.3
138
138
 
139
+ ## 0.1.5
140
+ * **BREAKING CHANGES**
141
+ * Previously if the OData service threw an exception, ruby_odata threw a generic error with the message that would start with "HTTP Error XXX: ". Instead of the message, the Error that is thrown is an `OData::ServiceError`. It has an `http_code` property on it, thus, the message is now just the text from the OData error without the "HTTP Error XXX: " prefix. This could potentially cause you problems if you were sniffing error messages for the HTTP error code.
142
+
143
+ * New Features
144
+ * Added the ability to query the OData service using the [$select system query option](http://www.odata.org/documentation/odata-v2-documentation/uri-conventions/#48_Select_System_Query_Option_select)
145
+ * Support for Int64 keys ([issue 39](https://github.com/visoft/ruby_odata/issues/39) and [issue 40](https://github.com/visoft/ruby_odata/pull/40), thanks [@nasali](https://github.com/nasali))
146
+ * New property `is_key` added to `PropertyMetadata` in order to determine the key properties for the class (found in the service's `class_metadata` collection)
@@ -10,5 +10,4 @@ Background:
10
10
 
11
11
  Scenario: Violate a data type conversion (empty string to decimal)
12
12
  Given I call "AddToProducts" on the service with a new "Product" object with Price: ""
13
- When I save changes it should throw an exception with message containing "HTTP Error 400"
14
-
13
+ When I save changes it should throw an exception with message containing "Error encountered in converting the value"
@@ -76,7 +76,7 @@ module OData
76
76
  end
77
77
  end
78
78
  end
79
-
79
+
80
80
  def add_methods(klass)
81
81
  # Add metadata methods
82
82
  klass.send :define_method, :__metadata do
@@ -158,20 +158,20 @@ module OData
158
158
  instance_variable_set("@#{method_name}", value)
159
159
  end
160
160
  end
161
-
161
+
162
162
  # Add an id method pulling out the id from the uri (mainly for Pickle support)
163
163
  klass.send :define_method, :id do
164
164
  metadata = self.__metadata
165
165
  id = nil
166
- if metadata && metadata[:uri] =~ /\((\d+)\)$/
166
+ if metadata && metadata[:uri] =~ /\((\d+)L?\)$/
167
167
  id = $~[1]
168
168
  end
169
169
  return (true if Integer(id) rescue false) ? id.to_i : id
170
170
  end
171
-
171
+
172
172
  # Override equals
173
173
  klass.send :define_method, :== do |other|
174
- self.class == other.class &&
174
+ self.class == other.class &&
175
175
  self.id == other.id &&
176
176
  self.__metadata == other.__metadata
177
177
  end
@@ -195,14 +195,18 @@ module OData
195
195
  klass.send :define_singleton_method, 'properties' do
196
196
  context.class_metadata[klass.to_s] || {}
197
197
  end
198
-
198
+
199
199
  # Finds a single model by ID
200
200
  klass.send :define_singleton_method, 'first' do |id|
201
201
  return nil if id.nil?
202
202
  # TODO: Instead of just pluralizing the klass name, use the actual collection name
203
203
  collection = klass.to_s.pluralize
204
204
  context.send "#{collection}", id
205
- results = context.execute
205
+ begin
206
+ results = context.execute
207
+ rescue OData::ServiceError => e
208
+ return nil
209
+ end
206
210
  results.count == 0 ? nil : results.first
207
211
  end
208
212
  end
@@ -1,4 +1,11 @@
1
1
  module OData
2
2
  # Raised when a user attempts to do something that is not supported
3
3
  class NotSupportedError < StandardError; end
4
+ # Raised when the service returns an error
5
+ class ServiceError < StandardError
6
+ attr_reader :http_code
7
+ def initialize(code)
8
+ @http_code = code
9
+ end
10
+ end
4
11
  end
@@ -15,6 +15,8 @@ module OData
15
15
  attr_reader :nav_prop
16
16
  # Applies only to navigation properties; the association corresponding to the property
17
17
  attr_accessor :association
18
+ # Applies to the primary key(s)
19
+ attr_accessor :is_key
18
20
 
19
21
  # Creates a new instance of the Class Property class
20
22
  #
@@ -18,6 +18,7 @@ class QueryBuilder
18
18
  @filters = []
19
19
  @order_bys = []
20
20
  @navigation_paths = []
21
+ @select = []
21
22
  @skip = nil
22
23
  @top = nil
23
24
  @count = nil
@@ -102,6 +103,7 @@ class QueryBuilder
102
103
  # product_links = svc.execute # => returns URIs for the products under the Category with an ID of 1
103
104
  def links(navigation_property)
104
105
  raise OData::NotSupportedError.new("You cannot call both the `links` method and the `count` method in the same query.") if @count
106
+ raise OData::NotSupportedError.new("You cannot call both the `links` method and the `select` method in the same query.") unless @select.empty?
105
107
  @links_navigation_property = navigation_property
106
108
  self
107
109
  end
@@ -116,6 +118,8 @@ class QueryBuilder
116
118
  # product_count = svc.execute
117
119
  def count
118
120
  raise OData::NotSupportedError.new("You cannot call both the `links` method and the `count` method in the same query.") if @links_navigation_property
121
+ raise OData::NotSupportedError.new("You cannot call both the `select` method and the `count` method in the same query.") unless @select.empty?
122
+
119
123
  @count = true
120
124
  self
121
125
  end
@@ -131,6 +135,28 @@ class QueryBuilder
131
135
  self
132
136
  end
133
137
 
138
+ # Used to customize the properties that are returned for "ad-hoc" queries
139
+ #
140
+ # @param [Array<String>] properties to return
141
+ #
142
+ # @example
143
+ # svc.Products.select('Price', 'Rating')
144
+ def select(*fields)
145
+ raise OData::NotSupportedError.new("You cannot call both the `links` method and the `select` method in the same query.") if @links_navigation_property
146
+ raise OData::NotSupportedError.new("You cannot call both the `count` method and the `select` method in the same query.") if @count
147
+
148
+ @select |= fields
149
+
150
+ expands = fields.find_all { |f| /\// =~ f }
151
+ expands.each do |e|
152
+ parts = e.split '/'
153
+ @expands |= [parts[0...-1].join('/')]
154
+ end
155
+
156
+
157
+ self
158
+ end
159
+
134
160
  # Builds the query URI (path, not including root) incorporating expands, filters, etc.
135
161
  # This is used internally when the execute method is called on the service
136
162
  def query
@@ -150,6 +176,7 @@ class QueryBuilder
150
176
  end
151
177
 
152
178
  query_options = []
179
+ query_options << "$select=#{@select.join(',')}" unless @select.empty?
153
180
  query_options << "$expand=#{@expands.join(',')}" unless @expands.empty?
154
181
  query_options << "$filter=#{@filters.join('+and+')}" unless @filters.empty?
155
182
  query_options << "$orderby=#{@order_bys.join(',')}" unless @order_bys.empty?
@@ -24,9 +24,7 @@ class Service
24
24
  def method_missing(name, *args)
25
25
  # Queries
26
26
  if @collections.include?(name.to_s)
27
- root = "/#{name.to_s}"
28
- root << "(#{args.join(',')})" unless args.empty?
29
- @query = QueryBuilder.new(root, @additional_params)
27
+ @query = build_collection_query_object(name,@additional_params, *args)
30
28
  return @query
31
29
  # Adds
32
30
  elsif name.to_s =~ /^AddTo(.*)/
@@ -96,8 +94,13 @@ class Service
96
94
 
97
95
  # Performs query operations (Read) against the server.
98
96
  # Typically this returns an array of record instances, except in the case of count queries
97
+ # @raise [ServiceError] if there is an error when talking to the service
99
98
  def execute
100
- @response = RestClient::Resource.new(build_query_uri, @rest_options).get
99
+ begin
100
+ @response = RestClient::Resource.new(build_query_uri, @rest_options).get
101
+ rescue Exception => e
102
+ handle_exception(e)
103
+ end
101
104
  return Integer(@response) if @response =~ /^\d+$/
102
105
  handle_collection_result(@response)
103
106
  end
@@ -167,6 +170,51 @@ class Service
167
170
 
168
171
  private
169
172
 
173
+ # Constructs a QueryBuilder instance for a collection using the arguments provided.
174
+ #
175
+ # @param [String] name the name of the collection
176
+ # @param [Hash] additional_parameters the additional parameters
177
+ # @param [Array] args the arguments to use for query
178
+ def build_collection_query_object(name, additional_parameters, *args)
179
+ root = "/#{name.to_s}"
180
+ if args.empty?
181
+ #nothing to add
182
+ elsif args.size == 1
183
+ if args.first.to_s =~ /\d+/
184
+ id_metadata = find_id_metadata(name.to_s)
185
+ root << build_id_path(args.first, id_metadata)
186
+ else
187
+ root << "(#{args.first})"
188
+ end
189
+ else
190
+ root << "(#{args.join(',')})"
191
+ end
192
+ QueryBuilder.new(root, additional_parameters)
193
+ end
194
+
195
+ # Finds the metadata associated with the given collection's first id property
196
+ # Remarks: This is used for single item lookup queries using the ID, e.g. Products(1), not complex primary keys
197
+ #
198
+ # @param [String] collection_name the name of the collection
199
+ def find_id_metadata(collection_name)
200
+ collection_data = @collections.fetch(collection_name)
201
+ class_metadata = @class_metadata.fetch(collection_data[:type].to_s)
202
+ key = class_metadata.select{|k,h| h.is_key }.collect{|k,h| h.name }[0]
203
+ class_metadata[key]
204
+ end
205
+
206
+ # Builds the ID expression of a given id for query
207
+ #
208
+ # @param [Object] id_value the actual value to be used
209
+ # @param [PropertyMetadata] id_metadata the property metadata object for the id
210
+ def build_id_path(id_value, id_metadata)
211
+ if id_metadata.type == "Edm.Int64"
212
+ "(#{id_value}L)"
213
+ else
214
+ "(#{id_value})"
215
+ end
216
+ end
217
+
170
218
  def set_options!(options)
171
219
  @options = options
172
220
  if @options[:eager_partial].nil?
@@ -291,10 +339,12 @@ class Service
291
339
  end
292
340
 
293
341
  # Builds the metadata need for each property for things like feed customizations and navigation properties
294
- def build_property_metadata(props)
342
+ def build_property_metadata(props, keys=[])
295
343
  metadata = {}
296
344
  props.each do |property_element|
297
345
  prop_meta = PropertyMetadata.new(property_element)
346
+ prop_meta.is_key = keys.include?(prop_meta.name)
347
+
298
348
  # If this is a navigation property, we need to add the association to the property metadata
299
349
  prop_meta.association = Association.new(property_element, @edmx) if prop_meta.nav_prop
300
350
  metadata[prop_meta.name] = prop_meta
@@ -319,13 +369,15 @@ class Service
319
369
  error = Nokogiri::XML(e.response)
320
370
 
321
371
  message = error.xpath("m:error/m:message", @ds_namespaces).first.content
322
- raise "HTTP Error #{code}: #{message}"
372
+ raise ServiceError.new(code), message
323
373
  end
324
374
 
325
375
  # Loops through the standard properties (non-navigation) for a given class and returns the appropriate list of methods
326
376
  def collect_properties(klass_name, element, doc)
327
377
  props = element.xpath(".//edm:Property", @ds_namespaces)
328
- @class_metadata[klass_name] = build_property_metadata(props)
378
+ key_elemnts = element.xpath(".//edm:Key//edm:PropertyRef", @ds_namespaces)
379
+ keys = key_elemnts.collect { |k| k['Name'] }
380
+ @class_metadata[klass_name] = build_property_metadata(props, keys)
329
381
  methods = props.collect { |p| p['Name'] }
330
382
  unless element["BaseType"].nil?
331
383
  base = element["BaseType"].split(".").last()
@@ -1,5 +1,5 @@
1
1
  # The ruby_odata namespace
2
2
  module OData
3
3
  # The current version of ruby_odata
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
3
+ <edmx:DataServices m:DataServiceVersion="1.0" m:MaxDataServiceVersion="2.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
4
+ <Schema Namespace="Model" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
5
+ <EntityType Name="Boat">
6
+ <Key>
7
+ <PropertyRef Name="KeyId" />
8
+ </Key>
9
+ <Property Name="KeyId" Type="Edm.Int64" Nullable="false" p6:StoreGeneratedPattern="Identity" xmlns:p6="http://schemas.microsoft.com/ado/2009/02/edm/annotation" />
10
+ <Property Name="color" Type="Edm.String" Nullable="false" MaxLength="50" FixedLength="false" Unicode="false" />
11
+ </EntityType>
12
+ </Schema>
13
+ <Schema Namespace="WebApp" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
14
+ <EntityContainer Name="adoEntities" m:IsDefaultEntityContainer="true" p6:LazyLoadingEnabled="true" xmlns:p6="http://schemas.microsoft.com/ado/2009/02/edm/annotation">
15
+ <EntitySet Name="Boats" EntityType="Model.Boat" />
16
+ </EntityContainer>
17
+ </Schema>
18
+ </edmx:DataServices>
19
+ </edmx:Edmx>
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
3
+ <edmx:DataServices m:DataServiceVersion="1.0" m:MaxDataServiceVersion="2.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
4
+ <Schema Namespace="Model" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
5
+ <EntityType Name="Car">
6
+ <Key>
7
+ <PropertyRef Name="id" />
8
+ </Key>
9
+ <Property Name="id" Type="Edm.Int64" Nullable="false" p6:StoreGeneratedPattern="Identity" xmlns:p6="http://schemas.microsoft.com/ado/2009/02/edm/annotation" />
10
+ <Property Name="color" Type="Edm.String" Nullable="false" MaxLength="50" FixedLength="false" Unicode="false" />
11
+ <Property Name="num_spots" Type="Edm.Int32" />
12
+ <Property Name="striped" Type="Edm.Byte" />
13
+ </EntityType>
14
+ </Schema>
15
+ <Schema Namespace="WebApp" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
16
+ <EntityContainer Name="adoEntities" m:IsDefaultEntityContainer="true" p6:LazyLoadingEnabled="true" xmlns:p6="http://schemas.microsoft.com/ado/2009/02/edm/annotation">
17
+ <EntitySet Name="Cars" EntityType="Model.Car" />
18
+ </EntityContainer>
19
+ </Schema>
20
+ </edmx:DataServices>
21
+ </edmx:Edmx>
@@ -0,0 +1,26 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <feed xml:base="http://test.com/test.svc/"
3
+ xmlns="http://www.w3.org/2005/Atom"
4
+ xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
5
+ xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
6
+ <id>http://test.com/test.svc/Boats</id>
7
+ <title type="text">Boats</title>
8
+ <updated>2013-10-28T19:59:15Z</updated>
9
+ <link rel="self" title="Boats" href="Boats" />
10
+ <entry>
11
+ <id>http://test.com/test.svc/Boats(213L)</id>
12
+ <category term="Model.Boat" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
13
+ <link rel="edit" title="Boat" href="Boats(213L)" />
14
+ <title />
15
+ <updated>2013-10-28T19:59:15Z</updated>
16
+ <author>
17
+ <name />
18
+ </author>
19
+ <content type="application/xml">
20
+ <m:properties>
21
+ <d:KeyId m:type="Edm.Int64">213</d:KeyId>
22
+ <d:color>blue</d:color>
23
+ </m:properties>
24
+ </content>
25
+ </entry>
26
+ </feed>
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <feed xml:base="http://test.com/test.svc/"
3
+ xmlns="http://www.w3.org/2005/Atom"
4
+ xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
5
+ xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
6
+ <id>http://test.com/test.svc/Cars</id>
7
+ <title type="text">Cars</title>
8
+ <updated>2013-10-28T19:59:15Z</updated>
9
+ <link rel="self" title="Cars" href="Cars" />
10
+ <entry>
11
+ <id>http://test.com/test.svc/Cars(213L)</id>
12
+ <category term="Model.Car" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
13
+ <link rel="edit" title="Car" href="Cars(213L)" />
14
+ <title />
15
+ <updated>2013-10-28T19:59:15Z</updated>
16
+ <author>
17
+ <name />
18
+ </author>
19
+ <content type="application/xml">
20
+ <m:properties>
21
+ <d:id m:type="Edm.Int64">213</d:id>
22
+ <d:color>peach</d:color>
23
+ <d:num_spots m:type="Edm.Int32" m:null="true" />
24
+ <d:striped m:type="Edm.Byte" m:null="true" />
25
+ </m:properties>
26
+ </content>
27
+ </entry>
28
+ </feed>
@@ -0,0 +1,268 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <feed xml:base="http://win7dev:8989/SampleService/RubyOData.svc/"
3
+ xmlns="http://www.w3.org/2005/Atom"
4
+ xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
5
+ xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
6
+ xmlns:georss="http://www.georss.org/georss"
7
+ xmlns:gml="http://www.opengis.net/gml">
8
+ <id>http://win7dev:8989/SampleService/RubyOdata.svc/Categories</id>
9
+ <title type="text">Categories</title>
10
+ <updated>2013-07-22T14:54:47Z</updated>
11
+ <link rel="self" title="Categories" href="Categories" />
12
+ <entry>
13
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(1)</id>
14
+ <category term="RubyODataService.Category" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
15
+ <link rel="edit" title="Category" href="Categories(1)" />
16
+ <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Products" type="application/atom+xml;type=feed" title="Products" href="Categories(1)/Products">
17
+ <m:inline>
18
+ <feed>
19
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(1)/Products</id>
20
+ <title type="text">Products</title>
21
+ <updated>2013-07-22T14:54:47Z</updated>
22
+ <link rel="self" title="Products" href="Categories(1)/Products" />
23
+ <entry>
24
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Products(1)</id>
25
+ <category term="RubyODataService.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
26
+ <link rel="edit" title="Product" href="Products(1)" />
27
+ <title />
28
+ <updated>2013-07-22T14:54:47Z</updated>
29
+ <author>
30
+ <name />
31
+ </author>
32
+ <content type="application/xml">
33
+ <m:properties>
34
+ <d:Name>Widget 0001</d:Name>
35
+ </m:properties>
36
+ </content>
37
+ </entry>
38
+ </feed>
39
+ </m:inline>
40
+ </link>
41
+ <title />
42
+ <updated>2013-07-22T14:54:47Z</updated>
43
+ <author>
44
+ <name />
45
+ </author>
46
+ <content type="application/xml">
47
+ <m:properties>
48
+ <d:Name>Category 0001</d:Name>
49
+ </m:properties>
50
+ </content>
51
+ </entry>
52
+ <entry>
53
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(2)</id>
54
+ <category term="RubyODataService.Category" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
55
+ <link rel="edit" title="Category" href="Categories(2)" />
56
+ <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Products" type="application/atom+xml;type=feed" title="Products" href="Categories(2)/Products">
57
+ <m:inline>
58
+ <feed>
59
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(2)/Products</id>
60
+ <title type="text">Products</title>
61
+ <updated>2013-07-22T14:54:47Z</updated>
62
+ <link rel="self" title="Products" href="Categories(2)/Products" />
63
+ <entry>
64
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Products(2)</id>
65
+ <category term="RubyODataService.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
66
+ <link rel="edit" title="Product" href="Products(2)" />
67
+ <title />
68
+ <updated>2013-07-22T14:54:47Z</updated>
69
+ <author>
70
+ <name />
71
+ </author>
72
+ <content type="application/xml">
73
+ <m:properties>
74
+ <d:Name>Widget 0002</d:Name>
75
+ </m:properties>
76
+ </content>
77
+ </entry>
78
+ </feed>
79
+ </m:inline>
80
+ </link>
81
+ <title />
82
+ <updated>2013-07-22T14:54:47Z</updated>
83
+ <author>
84
+ <name />
85
+ </author>
86
+ <content type="application/xml">
87
+ <m:properties>
88
+ <d:Name>Category 0001</d:Name>
89
+ </m:properties>
90
+ </content>
91
+ </entry>
92
+ <entry>
93
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(3)</id>
94
+ <category term="RubyODataService.Category" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
95
+ <link rel="edit" title="Category" href="Categories(3)" />
96
+ <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Products" type="application/atom+xml;type=feed" title="Products" href="Categories(3)/Products">
97
+ <m:inline>
98
+ <feed>
99
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(3)/Products</id>
100
+ <title type="text">Products</title>
101
+ <updated>2013-07-22T14:54:47Z</updated>
102
+ <link rel="self" title="Products" href="Categories(3)/Products" />
103
+ <entry>
104
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Products(3)</id>
105
+ <category term="RubyODataService.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
106
+ <link rel="edit" title="Product" href="Products(3)" />
107
+ <title />
108
+ <updated>2013-07-22T14:54:47Z</updated>
109
+ <author>
110
+ <name />
111
+ </author>
112
+ <content type="application/xml">
113
+ <m:properties>
114
+ <d:Name>Widget 0003</d:Name>
115
+ </m:properties>
116
+ </content>
117
+ </entry>
118
+ </feed>
119
+ </m:inline>
120
+ </link>
121
+ <title />
122
+ <updated>2013-07-22T14:54:47Z</updated>
123
+ <author>
124
+ <name />
125
+ </author>
126
+ <content type="application/xml">
127
+ <m:properties>
128
+ <d:Name>Category 0002</d:Name>
129
+ </m:properties>
130
+ </content>
131
+ </entry>
132
+ <entry>
133
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(4)</id>
134
+ <category term="RubyODataService.Category" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
135
+ <link rel="edit" title="Category" href="Categories(4)" />
136
+ <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Products" type="application/atom+xml;type=feed" title="Products" href="Categories(4)/Products">
137
+ <m:inline>
138
+ <feed>
139
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(4)/Products</id>
140
+ <title type="text">Products</title>
141
+ <updated>2013-07-22T14:54:47Z</updated>
142
+ <link rel="self" title="Products" href="Categories(4)/Products" />
143
+ <entry>
144
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Products(4)</id>
145
+ <category term="RubyODataService.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
146
+ <link rel="edit" title="Product" href="Products(4)" />
147
+ <title />
148
+ <updated>2013-07-22T14:54:47Z</updated>
149
+ <author>
150
+ <name />
151
+ </author>
152
+ <content type="application/xml">
153
+ <m:properties>
154
+ <d:Name>Widget 0004</d:Name>
155
+ </m:properties>
156
+ </content>
157
+ </entry>
158
+ </feed>
159
+ </m:inline>
160
+ </link>
161
+ <title />
162
+ <updated>2013-07-22T14:54:47Z</updated>
163
+ <author>
164
+ <name />
165
+ </author>
166
+ <content type="application/xml">
167
+ <m:properties>
168
+ <d:Name>Category 0002</d:Name>
169
+ </m:properties>
170
+ </content>
171
+ </entry>
172
+ <entry>
173
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(5)</id>
174
+ <category term="RubyODataService.Category" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
175
+ <link rel="edit" title="Category" href="Categories(5)" />
176
+ <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Products" type="application/atom+xml;type=feed" title="Products" href="Categories(5)/Products">
177
+ <m:inline>
178
+ <feed>
179
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(5)/Products</id>
180
+ <title type="text">Products</title>
181
+ <updated>2013-07-22T14:54:47Z</updated>
182
+ <link rel="self" title="Products" href="Categories(5)/Products" />
183
+ <entry>
184
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Products(5)</id>
185
+ <category term="RubyODataService.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
186
+ <link rel="edit" title="Product" href="Products(5)" />
187
+ <title />
188
+ <updated>2013-07-22T14:54:47Z</updated>
189
+ <author>
190
+ <name />
191
+ </author>
192
+ <content type="application/xml">
193
+ <m:properties>
194
+ <d:Name>Widget 0005</d:Name>
195
+ </m:properties>
196
+ </content>
197
+ </entry>
198
+ </feed>
199
+ </m:inline>
200
+ </link>
201
+ <title />
202
+ <updated>2013-07-22T14:54:47Z</updated>
203
+ <author>
204
+ <name />
205
+ </author>
206
+ <content type="application/xml">
207
+ <m:properties>
208
+ <d:Name>Category 0002</d:Name>
209
+ </m:properties>
210
+ </content>
211
+ </entry>
212
+ <entry>
213
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(6)</id>
214
+ <category term="RubyODataService.Category" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
215
+ <link rel="edit" title="Category" href="Categories(6)" />
216
+ <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Products" type="application/atom+xml;type=feed" title="Products" href="Categories(6)/Products">
217
+ <m:inline>
218
+ <feed>
219
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(6)/Products</id>
220
+ <title type="text">Products</title>
221
+ <updated>2013-07-22T14:54:47Z</updated>
222
+ <link rel="self" title="Products" href="Categories(6)/Products" />
223
+ <author>
224
+ <name />
225
+ </author>
226
+ </feed>
227
+ </m:inline>
228
+ </link>
229
+ <title />
230
+ <updated>2013-07-22T14:54:47Z</updated>
231
+ <author>
232
+ <name />
233
+ </author>
234
+ <content type="application/xml">
235
+ <m:properties>
236
+ <d:Name>Category 0001</d:Name>
237
+ </m:properties>
238
+ </content>
239
+ </entry>
240
+ <entry>
241
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(7)</id>
242
+ <category term="RubyODataService.Category" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
243
+ <link rel="edit" title="Category" href="Categories(7)" />
244
+ <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Products" type="application/atom+xml;type=feed" title="Products" href="Categories(7)/Products">
245
+ <m:inline>
246
+ <feed>
247
+ <id>http://win7dev:8989/SampleService/RubyOData.svc/Categories(7)/Products</id>
248
+ <title type="text">Products</title>
249
+ <updated>2013-07-22T14:54:47Z</updated>
250
+ <link rel="self" title="Products" href="Categories(7)/Products" />
251
+ <author>
252
+ <name />
253
+ </author>
254
+ </feed>
255
+ </m:inline>
256
+ </link>
257
+ <title />
258
+ <updated>2013-07-22T14:54:47Z</updated>
259
+ <author>
260
+ <name />
261
+ </author>
262
+ <content type="application/xml">
263
+ <m:properties>
264
+ <d:Name>Category 0002</d:Name>
265
+ </m:properties>
266
+ </content>
267
+ </entry>
268
+ </feed>