ruby_odata 0.0.3 → 0.0.4
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/CHANGELOG.rdoc +14 -5
- data/README.rdoc +35 -2
- data/VERSION +1 -1
- data/features/query_builder.feature +58 -1
- data/features/step_definitions/service_steps.rb +7 -0
- data/lib/ruby_odata/query_builder.rb +31 -1
- data/ruby_odata.gemspec +1 -1
- data/test/blueprints.rb +3 -3
- metadata +3 -3
data/CHANGELOG.rdoc
CHANGED
@@ -1,13 +1,22 @@
|
|
1
1
|
= ruby_odata Change Log
|
2
2
|
|
3
3
|
=== 0.0.1
|
4
|
-
*
|
5
|
-
*
|
6
|
-
* Query:
|
4
|
+
* New Features
|
5
|
+
* Basic CRUD Operations
|
6
|
+
* Query Enhancement: Filters
|
7
|
+
* Query Enhancement: Expands
|
7
8
|
|
8
9
|
=== 0.0.2
|
9
|
-
*
|
10
|
+
* New Features
|
11
|
+
* Query Enhancement: Order By (both desc and asc)
|
10
12
|
|
11
13
|
=== 0.0.3
|
12
|
-
*
|
14
|
+
* Bug Fixes
|
15
|
+
* Rearranged code to match the gem name. Things were mismatched between odata_ruby and ruby_odata.
|
13
16
|
|
17
|
+
=== 0.0.4
|
18
|
+
* New Features
|
19
|
+
* Query Enhancement: skip
|
20
|
+
* Query Enhancement: top
|
21
|
+
* Ability to perform paging using skip and top together
|
22
|
+
* Updated README with examples for order_by, skip, and top
|
data/README.rdoc
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
Error: The word "DATABASE SETUP - DO THIS FIRST*" is invalid. The character ' ' (U+20) may not appear in the middle of a word.
|
1
2
|
= ruby_odata
|
2
3
|
|
3
4
|
The <b>Open Data Protocol</b> (OData) is a fantastic way to query and update data over standard Web technologies. The ruby_odata library acts as a consumer of OData services.
|
@@ -60,9 +61,12 @@ Querying is easy, for example to pull all the categories from the SampleService,
|
|
60
61
|
categories = svc.execute
|
61
62
|
puts categories.to_json
|
62
63
|
|
63
|
-
You can also expand
|
64
|
+
You can also expand, add filters, order, skip records, and take only the top X records to the query before executing it. For example:
|
64
65
|
|
65
66
|
=== Expanding
|
67
|
+
Expanding allows you to eagerly load other objects that are children of the root.
|
68
|
+
You can use more than one expand on a query.
|
69
|
+
For expanding grandchild and lower entities, you must pass in the full path from the root, for example +Products.expand('Orders').expand('Orders/LineItems')+
|
66
70
|
|
67
71
|
# Without expanding the query
|
68
72
|
svc.Products(1)
|
@@ -78,6 +82,8 @@ You can also expand and add filters to the query before executing it. For examp
|
|
78
82
|
|
79
83
|
|
80
84
|
=== Filtering
|
85
|
+
The syntax for filtering can be found on the {OData Protocol URI Conventions}[http://www.odata.org/developers/protocols/uri-conventions#FilterSystemQueryOption] page.
|
86
|
+
You can use more than one filter, if you call the filter method multiple times it will before an AND.
|
81
87
|
|
82
88
|
# You can access by ID (but that isn't is a filter)
|
83
89
|
# The syntax is just svc.ENTITYNAME(ID) which is shown in the expanding examples above
|
@@ -88,17 +94,44 @@ You can also expand and add filters to the query before executing it. For examp
|
|
88
94
|
puts "#{prod.to_json}"
|
89
95
|
|
90
96
|
=== Combining Expanding and Filtering
|
97
|
+
The query operations follow a {fluent interface}[http://en.wikipedia.org/wiki/Fluent_interface], although they can be added by themselves as well as chained
|
91
98
|
|
92
99
|
svc.Products.filter("Name eq 'Product 2'").expand("Category")
|
93
100
|
prod = svc.execute
|
94
101
|
puts "Filtering on Name eq 'Product 2' and expanding"
|
95
102
|
puts "#{prod.to_json}"
|
96
103
|
|
104
|
+
=== Order By
|
105
|
+
You can order the results by properties of your choice, either ascending or descending.
|
106
|
+
Order by are similar to +expand+s in that you can use more than one of them on a query.
|
107
|
+
For expanding grandchild and lower entities, you must pass in the full path from the root like would do on an +expand+
|
97
108
|
|
109
|
+
svc.Products.order_by("Name")
|
110
|
+
products = svc.execute
|
111
|
+
|
112
|
+
# Specifically requesting descending
|
113
|
+
svc.Products.order_by("Name desc")
|
114
|
+
products = svc.execute
|
115
|
+
|
116
|
+
# Specifically requesting ascending
|
117
|
+
svc.Products.order_by("Name asc")
|
118
|
+
products = svc.execute
|
119
|
+
|
120
|
+
=== Skip
|
121
|
+
Skip allows you to skip a number of records when querying. This is often used for paging along with +top+.
|
122
|
+
|
123
|
+
svc.Products.skip(5)
|
124
|
+
products = svc.execute # => skips the first 5 items
|
125
|
+
|
126
|
+
=== Top
|
127
|
+
Top allows you only retrieve the top X number of records when querying. This is often used for paging along with +skip+.
|
128
|
+
|
129
|
+
svc.Products.top(5)
|
130
|
+
products = svc.execute # => returns only the first 5 items
|
98
131
|
|
99
132
|
== Tests
|
100
133
|
*DATABASE SETUP - DO THIS FIRST*
|
101
|
-
Within /test/SampleService/App_Data/ rename _TestDB*.* to TestDB*.*. This file is just the
|
134
|
+
Within /test/SampleService/App_Data/ rename _TestDB*.* to TestDB*.*. This file is just the initial database, and needs to be renamed so that unwanted changes to that DB aren't persisted in source control.
|
102
135
|
|
103
136
|
All of the tests are written using Cucumber going against a sample service (Found in /test/SampleService/*). The SampleService is an ASP.NET Web Site running a SQLEXPRESS 2008 R2 Database (TestDB), as well as the ADO.NET Entity Framework and a WCF Data Service. In order to run the tests, you need to spin up the SampleService and have it running on port 8888 (http://localhost:8888/SampleService).
|
104
137
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
@@ -21,8 +21,9 @@ Scenario: Navigation Properties should be able to be eager loaded
|
|
21
21
|
And the method "Id" on the result's method "Category" should equal: "1"
|
22
22
|
|
23
23
|
|
24
|
-
#
|
24
|
+
# Filters
|
25
25
|
Scenario: Filters should be allowed on the root level entity
|
26
|
+
# Filter
|
26
27
|
Given I call "AddToProducts" on the service with a new "Product" object with Name: "Test Product"
|
27
28
|
When I save changes
|
28
29
|
When I call "Products" on the service
|
@@ -90,6 +91,62 @@ Scenario: Order by should access sorting acsending
|
|
90
91
|
| Product 5 |
|
91
92
|
|
92
93
|
|
94
|
+
# Skip
|
95
|
+
Scenario: Skip should be allowed on the root level entity
|
96
|
+
Given the following Products exist:
|
97
|
+
| Name |
|
98
|
+
| Product 1 |
|
99
|
+
| Product 2 |
|
100
|
+
| Product 3 |
|
101
|
+
| Product 4 |
|
102
|
+
| Product 5 |
|
103
|
+
When I call "Products" on the service
|
104
|
+
And I skip 3
|
105
|
+
And I run the query
|
106
|
+
Then the result should be:
|
107
|
+
| Name |
|
108
|
+
| Product 4 |
|
109
|
+
| Product 5 |
|
110
|
+
|
111
|
+
|
112
|
+
# Top
|
113
|
+
Scenario: Top should be allowed on the root level entity
|
114
|
+
Given the following Products exist:
|
115
|
+
| Name |
|
116
|
+
| Product 1 |
|
117
|
+
| Product 2 |
|
118
|
+
| Product 3 |
|
119
|
+
| Product 4 |
|
120
|
+
| Product 5 |
|
121
|
+
When I call "Products" on the service
|
122
|
+
And I ask for the top 3
|
123
|
+
And I run the query
|
124
|
+
Then the result should be:
|
125
|
+
| Name |
|
126
|
+
| Product 1 |
|
127
|
+
| Product 2 |
|
128
|
+
| Product 3 |
|
129
|
+
|
130
|
+
Scenario: Top should be able to be used along with skip for paging
|
131
|
+
Given the following Products exist:
|
132
|
+
| Name |
|
133
|
+
| Product 1 |
|
134
|
+
| Product 2 |
|
135
|
+
| Product 3 |
|
136
|
+
| Product 4 |
|
137
|
+
| Product 5 |
|
138
|
+
| Product 6 |
|
139
|
+
When I call "Products" on the service
|
140
|
+
And I skip 2
|
141
|
+
And I ask for the top 2
|
142
|
+
And I run the query
|
143
|
+
Then the result should be:
|
144
|
+
| Name |
|
145
|
+
| Product 3 |
|
146
|
+
| Product 4 |
|
147
|
+
|
148
|
+
|
149
|
+
|
93
150
|
|
94
151
|
|
95
152
|
|
@@ -62,6 +62,13 @@ When /^I order by: "([^\"]*)"$/ do |order|
|
|
62
62
|
@service_query.order_by(order)
|
63
63
|
end
|
64
64
|
|
65
|
+
When /^I skip (\d+)$/ do |skip|
|
66
|
+
@service_query.skip(skip)
|
67
|
+
end
|
68
|
+
|
69
|
+
When /^I ask for the top (\d+)$/ do |top|
|
70
|
+
@service_query.top(top)
|
71
|
+
end
|
65
72
|
|
66
73
|
Then /^the method "([^\"]*)" on the result should be of type "([^\"]*)"$/ do |method, type|
|
67
74
|
result = @service_result.send(method.to_sym)
|
@@ -15,6 +15,8 @@ class QueryBuilder
|
|
15
15
|
@expands = []
|
16
16
|
@filters = []
|
17
17
|
@order_bys = []
|
18
|
+
@skip = nil
|
19
|
+
@top = nil
|
18
20
|
end
|
19
21
|
|
20
22
|
# Used to eagerly-load data for nested objects, for example, obtaining a Category for a Product within one call to the server
|
@@ -36,7 +38,7 @@ class QueryBuilder
|
|
36
38
|
|
37
39
|
# Used to filter data being returned
|
38
40
|
# ==== Required Attributes
|
39
|
-
# - filter: The
|
41
|
+
# - filter: The conditions to apply to the query
|
40
42
|
#
|
41
43
|
# ==== Example
|
42
44
|
# svc.Products.filter("Name eq 'Product 2'")
|
@@ -58,6 +60,32 @@ class QueryBuilder
|
|
58
60
|
self
|
59
61
|
end
|
60
62
|
|
63
|
+
# Used to skip a number of records
|
64
|
+
# This is typically used for paging, where it would be used along with the +top+ method.
|
65
|
+
# ==== Required Attributes
|
66
|
+
# - num: The number of items to skip
|
67
|
+
#
|
68
|
+
# ==== Example
|
69
|
+
# svc.Products.skip(5)
|
70
|
+
# products = svc.execute # => skips the first 5 items
|
71
|
+
def skip(num)
|
72
|
+
@skip = num
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Used to take only the top X records
|
77
|
+
# This is typically used for paging, where it would be used along with the +skip+ method.
|
78
|
+
# ==== Required Attributes
|
79
|
+
# - num: The number of items to return
|
80
|
+
#
|
81
|
+
# ==== Example
|
82
|
+
# svc.Products.top(5)
|
83
|
+
# products = svc.execute # => returns only the first 5 items
|
84
|
+
def top(num)
|
85
|
+
@top = num
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
61
89
|
# Builds the query URI (path, not including root) incorporating expands, filters, etc.
|
62
90
|
# This is used internally when the execute method is called on the service
|
63
91
|
def query
|
@@ -66,6 +94,8 @@ class QueryBuilder
|
|
66
94
|
query_options << "$expand=#{@expands.join(',')}" unless @expands.empty?
|
67
95
|
query_options << "$filter=#{@filters.join('+and+')}" unless @filters.empty?
|
68
96
|
query_options << "$orderby=#{@order_bys.join(',')}" unless @order_bys.empty?
|
97
|
+
query_options << "$skip=#{@skip}" unless @skip.nil?
|
98
|
+
query_options << "$top=#{@top}" unless @top.nil?
|
69
99
|
if !query_options.empty?
|
70
100
|
q << "?"
|
71
101
|
q << query_options.join('&')
|
data/ruby_odata.gemspec
CHANGED
data/test/blueprints.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Sham.define do
|
2
|
-
category_name
|
3
|
-
product_name
|
4
|
-
price
|
2
|
+
category_name { |i| "Category #{i}" }
|
3
|
+
product_name { |i| "Widget #{i}" }
|
4
|
+
price(:unique => false) { ['5.00', '10.00', '20.00', '15.00' , '25.00', '7.50'].rand }
|
5
5
|
end
|
6
6
|
|
7
7
|
Product.blueprint do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_odata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Damien White
|