Skip to content
This repository
Browse code

Added the ability to perform a basic count (and cleaned up single quo…

…tes/spaces)
  • Loading branch information...
commit 0068bd5a6be22bc48e7945ba407cc05b1cae002b 1 parent 523de8c
Damien White authored August 06, 2012
28  features/query_builder.feature
... ...
@@ -1,7 +1,7 @@
1 1
 Feature: Query Builder
2 2
   In order to query OData services
3 3
   As a user
4  
-  I want to be able to perform valid OData protocol operations 
  4
+  I want to be able to perform valid OData protocol operations
5 5
 
6 6
 Background:
7 7
   Given a HTTP ODataService exists
@@ -12,7 +12,7 @@ Scenario: Navigation Properties should be able to be eager loaded
12 12
   Given I call "AddToCategories" on the service with a new "Category" object with Name: "Test Category"
13 13
   And I save changes
14 14
   And I call "AddToProducts" on the service with a new "Product" object with Category: "@@LastSave.first"
15  
-  And I save changes	
  15
+  And I save changes
16 16
   And I call "Products" on the service with args: "1"
17 17
   And I expand the query to include "Category"
18 18
   When I run the query
@@ -99,14 +99,14 @@ Scenario: Skip should be allowed on the root level entity
99 99
   | Product 2 |
100 100
   | Product 3 |
101 101
   | Product 4 |
102  
-  | Product 5 | 
  102
+  | Product 5 |
103 103
   When I call "Products" on the service
104 104
   And I skip 3
105 105
   And I run the query
106 106
   Then the result should be:
107 107
   | Name      |
108 108
   | Product 4 |
109  
-  | Product 5 |  
  109
+  | Product 5 |
110 110
 
111 111
 
112 112
 # Top
@@ -117,14 +117,14 @@ Scenario: Top should be allowed on the root level entity
117 117
   | Product 2 |
118 118
   | Product 3 |
119 119
   | Product 4 |
120  
-  | Product 5 | 
  120
+  | Product 5 |
121 121
   When I call "Products" on the service
122 122
   And I ask for the top 3
123 123
   And I run the query
124 124
   Then the result should be:
125 125
   | Name      |
126 126
   | Product 1 |
127  
-  | Product 2 | 
  127
+  | Product 2 |
128 128
   | Product 3 |
129 129
 
130 130
 Scenario: Top should be able to be used along with skip for paging
@@ -134,8 +134,8 @@ Scenario: Top should be able to be used along with skip for paging
134 134
   | Product 2 |
135 135
   | Product 3 |
136 136
   | Product 4 |
137  
-  | Product 5 | 
138  
-  | Product 6 |  
  137
+  | Product 5 |
  138
+  | Product 6 |
139 139
   When I call "Products" on the service
140 140
   And I skip 2
141 141
   And I ask for the top 2
@@ -143,7 +143,7 @@ Scenario: Top should be able to be used along with skip for paging
143 143
   Then the result should be:
144 144
   | Name      |
145 145
   | Product 3 |
146  
-  | Product 4 |  
  146
+  | Product 4 |
147 147
 
148 148
 
149 149
 # Links
@@ -160,3 +160,13 @@ Scenario: Navigation Properties should be able to represented as links
160 160
   And I run the query
161 161
   Then the result count should be 3
162 162
   Then the method "path" on the first result should equal: "/SampleService/RubyOData.svc/Products(1)"
  163
+
  164
+# Count
  165
+Scenario: Count should be able to be used on a root collection
  166
+  Given 4 products exist
  167
+  When I call "Products" on the service
  168
+  And I ask for the count
  169
+  And I run the query
  170
+  Then the integer result should be 4
  171
+
  172
+
4  features/step_definitions/service_steps.rb
@@ -122,6 +122,10 @@
122 122
   @service_query.top(top)
123 123
 end
124 124
 
  125
+When /^I ask for the count$/ do
  126
+  @service_query.count
  127
+end
  128
+
125 129
 When /^I ask for the links for "([^\"]*)"$/ do |nav_prop|
126 130
   @service_query.links(nav_prop)
127 131
 end
1  lib/ruby_odata.rb
@@ -13,6 +13,7 @@
13 13
 require "bigdecimal/util"
14 14
 require "backports"
15 15
 
  16
+require lib + "/ruby_odata/exceptions"
16 17
 require lib + "/ruby_odata/association"
17 18
 require lib + "/ruby_odata/property_metadata"
18 19
 require lib + "/ruby_odata/query_builder"
3  lib/ruby_odata/exceptions.rb
... ...
@@ -0,0 +1,3 @@
  1
+module OData
  2
+  class NotSupportedError < StandardError; end
  3
+end
19  lib/ruby_odata/query_builder.rb
@@ -19,7 +19,7 @@ def initialize(root, additional_params = {})
19 19
     @order_bys = []
20 20
     @skip = nil
21 21
     @top = nil
22  
-    @links = nil
  22
+    @count = nil
23 23
     @additional_params = additional_params
24 24
   end
25 25
 
@@ -98,10 +98,22 @@ def top(num)
98 98
   #   svc.Categories(1).links("Products")
99 99
   #   product_links = svc.execute # => returns URIs for the products under the Category with an ID of 1
100 100
   def links(navigation_property)
  101
+    raise OData::NotSupportedError.new("You cannot call both the `links` method and the `count` method in the same query.") if @count
101 102
     @navigation_property = navigation_property
102 103
     self
103 104
   end
104 105
 
  106
+  # Used to return a count of objects instead of the objects themselves
  107
+  # ==== Example
  108
+  #   svc.Products
  109
+  #   svc.count
  110
+  #   product_count = svc.execute
  111
+  def count
  112
+    raise OData::NotSupportedError.new("You cannot call both the `links` method and the `count` method in the same query.") if @navigation_property
  113
+    @count = true
  114
+    self
  115
+  end
  116
+
105 117
   # Builds the query URI (path, not including root) incorporating expands, filters, etc.
106 118
   # This is used internally when the execute method is called on the service
107 119
   def query
@@ -112,6 +124,11 @@ def query
112 124
       q << "/$links/#{@navigation_property}"
113 125
     end
114 126
 
  127
+    # Handle count queries, this isn't just a standard query option
  128
+    if @count
  129
+      q << "/$count"
  130
+    end
  131
+
115 132
     query_options = []
116 133
     query_options << "$expand=#{@expands.join(',')}" unless @expands.empty?
117 134
     query_options << "$filter=#{@filters.join('+and+')}" unless @filters.empty?
4  lib/ruby_odata/service.rb
@@ -96,9 +96,11 @@ def save_changes
96 96
     end
97 97
   end
98 98
 
99  
-  # Performs query operations (Read) against the server, returns an array of record instances.
  99
+  # Performs query operations (Read) against the server.
  100
+  # Typically this returns an array of record instances, except in the case of count queries
100 101
   def execute
101 102
     result = RestClient::Resource.new(build_query_uri, @rest_options).get
  103
+    return Integer(result) if result =~ /^\d+$/
102 104
     handle_collection_result(result)
103 105
   end
104 106
 
43  spec/query_builder_spec.rb
... ...
@@ -1,34 +1,55 @@
1  
-require 'spec_helper'
  1
+require "spec_helper"
2 2
 
3 3
 module OData
4 4
   describe QueryBuilder do
5 5
     describe "#initialize" do
6  
-      it "handles additional parameters" do        
7  
-        builder = QueryBuilder.new 'Products', { :x=>1, :y=>2 }
  6
+      it "handles additional parameters" do
  7
+        builder = QueryBuilder.new "Products", { :x=>1, :y=>2 }
8 8
         builder.query.should eq "Products?x=1&y=2"
9 9
       end
10  
-      it "handles empty additional parameters" do        
11  
-        builder = QueryBuilder.new 'Products'
  10
+      it "handles empty additional parameters" do
  11
+        builder = QueryBuilder.new "Products"
12 12
         builder.query.should eq "Products"
13  
-      end      
  13
+      end
14 14
     end
15 15
     describe "#query" do
16 16
       it "should append additional parameters to the end" do
17  
-        builder = QueryBuilder.new 'Products', { :x=>1, :y=>2 }
  17
+        builder = QueryBuilder.new "Products", { :x=>1, :y=>2 }
18 18
         builder.top(10)
19 19
         builder.query.should eq "Products?$top=10&x=1&y=2"
20 20
       end
21 21
       it "should properly handle queries for links" do
22  
-        builder = QueryBuilder.new 'Categories(1)'
23  
-        builder.links('Products')
  22
+        builder = QueryBuilder.new "Categories(1)"
  23
+        builder.links("Products")
24 24
         builder.query.should eq "Categories(1)/$links/Products"
25 25
       end
26 26
       it "should properly handle queries for links with additional operations" do
27  
-        builder = QueryBuilder.new 'Categories(1)'
28  
-        builder.links('Products')
  27
+        builder = QueryBuilder.new "Categories(1)"
  28
+        builder.links("Products")
29 29
         builder.top(5)
30 30
         builder.query.should eq "Categories(1)/$links/Products?$top=5"
31 31
       end
  32
+      it "should throw an execption if count was already called on the builder" do
  33
+        builder = QueryBuilder.new "Categories(1)"
  34
+        builder.count
  35
+        lambda { builder.links("Products") }.should raise_error(OData::NotSupportedError, "You cannot call both the `links` method and the `count` method in the same query.")
  36
+      end
  37
+      context "count" do
  38
+        it "should accept the count method" do
  39
+          builder = QueryBuilder.new "Products"
  40
+          lambda { builder.count }.should_not raise_error
  41
+        end
  42
+        it "should properly handle the count method" do
  43
+          builder = QueryBuilder.new "Products"
  44
+          builder.count
  45
+          builder.query.should eq "Products/$count"
  46
+        end
  47
+        it "should throw an execption if links was already called on the builder" do
  48
+          builder = QueryBuilder.new "Categories(1)"
  49
+          builder.links("Products")
  50
+          lambda { builder.count }.should raise_error(OData::NotSupportedError, "You cannot call both the `links` method and the `count` method in the same query.")
  51
+        end
  52
+      end
32 53
     end
33 54
   end
34 55
 end

0 notes on commit 0068bd5

Please sign in to comment.
Something went wrong with that request. Please try again.