ruby_odata 0.1.6 → 0.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +2 -0
- data/Guardfile +1 -1
- data/features/basic_auth.feature +13 -13
- data/features/cassettes/basic_auth_protected_resource.yml +22 -20
- data/features/cassettes/batch_request_additions.yml +26 -26
- data/features/cassettes/batch_request_deletes.yml +27 -27
- data/features/cassettes/batch_request_updates.yml +26 -26
- data/features/cassettes/clean_database_for_testing.yml +10 -10
- data/features/cassettes/cucumber_tags/basic_auth.yml +54 -163
- data/features/cassettes/cucumber_tags/batch_request.yml +229 -1118
- data/features/cassettes/cucumber_tags/complex_types.yml +81 -191
- data/features/cassettes/cucumber_tags/error_handling.yml +45 -33
- data/features/cassettes/cucumber_tags/query_builder.yml +555 -1248
- data/features/cassettes/cucumber_tags/service_manage.yml +216 -591
- data/features/cassettes/cucumber_tags/service_methods.yml +137 -412
- data/features/cassettes/cucumber_tags/ssl.yml +121 -117
- data/features/cassettes/cucumber_tags/type_conversion.yml +104 -170
- data/features/cassettes/service_manage_additions.yml +26 -28
- data/features/cassettes/service_manage_deletions.yml +21 -21
- data/features/cassettes/service_manage_deletions_2.yml +21 -21
- data/features/cassettes/unsecured_metadata.yml +14 -14
- data/features/service.feature +37 -37
- data/features/service_methods.feature +38 -38
- data/features/step_definitions/pickle_steps.rb +5 -5
- data/features/step_definitions/service_steps.rb +26 -26
- data/features/support/hooks.rb +3 -2
- data/features/support/vcr.rb +13 -13
- data/lib/ruby_odata.rb +4 -1
- data/lib/ruby_odata/association.rb +15 -8
- data/lib/ruby_odata/class_builder.rb +5 -1
- data/lib/ruby_odata/query_builder.rb +171 -173
- data/lib/ruby_odata/resource.rb +153 -0
- data/lib/ruby_odata/service.rb +30 -30
- data/lib/ruby_odata/version.rb +1 -1
- data/ruby_odata.gemspec +48 -43
- data/spec/association_spec.rb +15 -11
- data/spec/fixtures/decimal/metadata.xml +1 -0
- data/spec/fixtures/v4/edmx_metadata.xml +145 -0
- data/spec/fixtures/v4/result_categories.xml +0 -0
- data/spec/revised_service_spec.rb +16 -11
- data/spec/service_spec.rb +149 -114
- data/spec/service_v4_spec.rb +102 -0
- data/spec/spec_helper.rb +6 -1
- metadata +109 -32
- data/features/cassettes/cucumber_tags/service.yml +0 -234
@@ -22,12 +22,12 @@ end
|
|
22
22
|
|
23
23
|
# not find a model
|
24
24
|
Then(/^#{capture_model} should not exist(?: with #{capture_fields})?$/) do |name, fields|
|
25
|
-
find_model(name, fields).
|
25
|
+
expect(find_model(name, fields)).to be_nil
|
26
26
|
end
|
27
27
|
|
28
28
|
# find models with a table
|
29
29
|
Then(/^the following #{capture_plural_factory} should exists?:?$/) do |plural_factory, table|
|
30
|
-
find_models_from_table(plural_factory, table).
|
30
|
+
expect(find_models_from_table(plural_factory, table)).to_not be_any(&:nil?)
|
31
31
|
end
|
32
32
|
|
33
33
|
# find exactly n models
|
@@ -42,7 +42,7 @@ end
|
|
42
42
|
|
43
43
|
# assert model is in another model's has_many assoc
|
44
44
|
Then(/^#{capture_model} should be (?:in|one of|amongst) #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
|
45
|
-
model_with_associations(owner).send(association).
|
45
|
+
expect(model_with_associations(owner).send(association)).to include(model!(target))
|
46
46
|
end
|
47
47
|
|
48
48
|
# assert model is not in another model's has_many assoc
|
@@ -60,7 +60,7 @@ Then(/^#{capture_model} should not be #{capture_model}(?:'s)? (\w+)$/) do |targe
|
|
60
60
|
model!(owner).send(association).should_not == model!(target)
|
61
61
|
end
|
62
62
|
|
63
|
-
# assert model.predicate?
|
63
|
+
# assert model.predicate?
|
64
64
|
Then(/^#{capture_model} should (?:be|have) (?:an? )?#{capture_predicate}$/) do |name, predicate|
|
65
65
|
if model!(name).respond_to?("has_#{predicate.gsub(' ', '_')}")
|
66
66
|
model!(name).should send("have_#{predicate.gsub(' ', '_')}")
|
@@ -83,7 +83,7 @@ end
|
|
83
83
|
Then(/^#{capture_model}'s (\w+) (should(?: not)?) be #{capture_value}$/) do |name, attribute, expectation, expected|
|
84
84
|
actual_value = model(name).send(attribute)
|
85
85
|
expectation = expectation.gsub(' ', '_')
|
86
|
-
|
86
|
+
|
87
87
|
case expected
|
88
88
|
when 'nil', 'true', 'false'
|
89
89
|
actual_value.send(expectation, send("be_#{expected}"))
|
@@ -25,11 +25,11 @@ Given /^a HTTP BasicAuth ODataService exists using username "([^\"]*)" and passw
|
|
25
25
|
end
|
26
26
|
|
27
27
|
Given /^a HTTP BasicAuth ODataService exists using username "([^\"]*)" and password "([^\"]*)" it should throw an exception with message "([^\"]*)"$/ do |username, password, msg|
|
28
|
-
|
28
|
+
expect { @service = OData::Service.new(BASICAUTH_URL, { :username => username, :password => password }) }.to raise_error(msg)
|
29
29
|
end
|
30
30
|
|
31
31
|
Given /^a HTTP BasicAuth ODataService exists it should throw an exception with message "([^\"]*)"$/ do |msg|
|
32
|
-
|
32
|
+
expect { @service = OData::Service.new(BASICAUTH_URL) }.to raise_error(msg)
|
33
33
|
end
|
34
34
|
|
35
35
|
Given /^a HTTPS BasicAuth ODataService exists using self-signed certificate and username "([^\"]*)" and password "([^\"]*)"$/ do |username, password|
|
@@ -41,19 +41,19 @@ When /^I call "([^\"]*)" on the service$/ do |method|
|
|
41
41
|
end
|
42
42
|
|
43
43
|
Then /^the integer result should be ([^\"]*)$/ do |result|
|
44
|
-
@service_result.
|
44
|
+
expect(@service_result).to eq result.to_i
|
45
45
|
end
|
46
46
|
|
47
47
|
Then /^I should be able to call "([^\"]*)" on the service$/ do |method|
|
48
|
-
|
48
|
+
expect { @service.send(method) }.to_not raise_error
|
49
49
|
end
|
50
50
|
|
51
51
|
Then /^I should not be able to call "([^\"]*)" on the service$/ do |method|
|
52
|
-
|
52
|
+
expect { @service.send(method) }.to raise_error
|
53
53
|
end
|
54
54
|
|
55
55
|
Then /^I should be able to call "([^\"]*)" on the service with args: "([^\"]*)"$/ do |method, args|
|
56
|
-
|
56
|
+
expect { @service.send(method, args) }.to_not raise_error
|
57
57
|
end
|
58
58
|
|
59
59
|
When /^I call "([^\"]*)" on the service with args: "([^\"]*)"$/ do |method, args|
|
@@ -65,19 +65,19 @@ When /^I run the query$/ do
|
|
65
65
|
end
|
66
66
|
|
67
67
|
Then /^the result should be of type "([^\"]*)"$/ do |type|
|
68
|
-
@service_result.class.to_s.
|
68
|
+
expect(@service_result.class.to_s).to eq type
|
69
69
|
end
|
70
70
|
|
71
71
|
Then /^the result should have a method: "([^\"]*)"$/ do |method|
|
72
|
-
@service_result.respond_to?(method.to_sym).
|
72
|
+
expect(@service_result.respond_to?(method.to_sym)).to eq true
|
73
73
|
end
|
74
74
|
|
75
75
|
Then /^the method "([^\"]*)" on the result should equal: "([^\"]*)"$/ do |method, value|
|
76
|
-
@service_result.send(method.to_sym).to_s.
|
76
|
+
expect(@service_result.send(method.to_sym).to_s).to eq value
|
77
77
|
end
|
78
78
|
|
79
79
|
Then /^the method "([^\"]*)" on the result should be nil$/ do |method|
|
80
|
-
@service_result.send(method.to_sym).
|
80
|
+
expect(@service_result.send(method.to_sym)).to eq nil
|
81
81
|
end
|
82
82
|
|
83
83
|
When /^I set "([^\"]*)" on the result to "([^\"]*)"$/ do |property_name, value|
|
@@ -114,7 +114,7 @@ end
|
|
114
114
|
|
115
115
|
Then /^the method "([^\"]*)" on the result should be of type "([^\"]*)"$/ do |method, type|
|
116
116
|
result = @service_result.send(method.to_sym)
|
117
|
-
result.class.to_s.
|
117
|
+
expect(result.class.to_s).to eq type
|
118
118
|
end
|
119
119
|
|
120
120
|
Given /^I call "([^\"]*)" on the service with a new "([^\"]*)" object(?: with (.*))?$/ do |method, object, fields|
|
@@ -129,7 +129,7 @@ When /^I save changes$/ do
|
|
129
129
|
end
|
130
130
|
|
131
131
|
Then /^the save result should be of type "([^\"]*)"$/ do |type|
|
132
|
-
@saved_result.class.to_s.
|
132
|
+
expect(@saved_result.class.to_s).to eq type
|
133
133
|
end
|
134
134
|
|
135
135
|
When /^I call "([^\"]*)" on the service with the last save result$/ do |method|
|
@@ -141,12 +141,12 @@ When /^I call "([^\"]*)" on the service with the last query result$/ do |method|
|
|
141
141
|
end
|
142
142
|
|
143
143
|
Then /^the save result should equal: "([^\"]*)"$/ do |result|
|
144
|
-
@saved_result.to_s.
|
144
|
+
expect(@saved_result.to_s).to eq result
|
145
145
|
end
|
146
146
|
|
147
147
|
Then /^the method "([^\"]*)" on the save result should equal: "([^\"]*)"$/ do |method, value|
|
148
148
|
result = @saved_result.send(method.to_sym)
|
149
|
-
result.
|
149
|
+
expect(result).to eq value
|
150
150
|
end
|
151
151
|
|
152
152
|
When /^blueprints exist for the service$/ do
|
@@ -155,23 +155,23 @@ end
|
|
155
155
|
|
156
156
|
Given /^I call "([^\"]*)" on the service with a new "([^\"]*)" object it should throw an exception with message "([^\"]*)"$/ do |method, object, msg|
|
157
157
|
obj = object.constantize.send :make
|
158
|
-
|
158
|
+
expect { @service.send(method.to_sym, obj) }.to raise_error(msg)
|
159
159
|
end
|
160
160
|
|
161
161
|
When /^I save changes it should throw an exception with message containing "([^"]*)"$/ do |msg|
|
162
|
-
|
162
|
+
expect { @service.save_changes }.to raise_error(/#{msg}.*/)
|
163
163
|
end
|
164
164
|
|
165
165
|
Then /^no "([^\"]*)" should exist$/ do |collection|
|
166
166
|
@service.send(collection)
|
167
167
|
results = @service.execute
|
168
|
-
results.
|
168
|
+
expect(results).to eq []
|
169
169
|
end
|
170
170
|
|
171
171
|
Then /^the primitive results should be:$/ do |table|
|
172
172
|
# table is a Cucumber::Ast::Table
|
173
173
|
values = table.hashes
|
174
|
-
result_table =
|
174
|
+
result_table = values
|
175
175
|
table.diff!(result_table)
|
176
176
|
end
|
177
177
|
|
@@ -191,13 +191,13 @@ Then /^the result should be:$/ do |table|
|
|
191
191
|
results << obj_hash
|
192
192
|
end
|
193
193
|
|
194
|
-
result_table =
|
194
|
+
result_table = results
|
195
195
|
|
196
196
|
table.diff!(result_table)
|
197
197
|
end
|
198
198
|
|
199
199
|
Then /^a class named "([^\"]*)" should exist$/ do |klass_name|
|
200
|
-
(Object.const_defined? klass_name).
|
200
|
+
expect(Object.const_defined? klass_name).to eq true
|
201
201
|
end
|
202
202
|
|
203
203
|
# Operations against a method on the service result
|
@@ -207,12 +207,12 @@ When /^I call "([^\"]*)" for "([^\"]*)" on the result$/ do |method2, method1|
|
|
207
207
|
end
|
208
208
|
|
209
209
|
Then /^the operation should not be null$/ do
|
210
|
-
@operation_result.nil
|
210
|
+
expect(@operation_result.nil?).to eq false
|
211
211
|
end
|
212
212
|
|
213
213
|
Then /^the method "([^\"]*)" on the result's method "([^\"]*)" should equal: "([^\"]*)"$/ do |method, result_method, value|
|
214
214
|
obj = @service_result.send(result_method.to_sym)
|
215
|
-
obj.send(method.to_sym).to_s.
|
215
|
+
expect(obj.send(method.to_sym).to_s).to eq value
|
216
216
|
end
|
217
217
|
|
218
218
|
When /^I set "([^\"]*)" on the result's method "([^\"]*)" to "([^\"]*)"$/ do |property_name, result_method, value|
|
@@ -223,9 +223,9 @@ end
|
|
223
223
|
Then /^the "([^\"]*)" method on the object should return a (.*)/ do |method_name, type|
|
224
224
|
methods = method_name.split '.'
|
225
225
|
if methods.length == 1
|
226
|
-
@service_result.first.send(method_name).class.to_s.
|
226
|
+
expect(@service_result.first.send(method_name).class.to_s).to eq type
|
227
227
|
else
|
228
|
-
@service_result.first.send(methods[0]).send(methods[1]).class.to_s.
|
228
|
+
expect(@service_result.first.send(methods[0]).send(methods[1]).class.to_s).to eq type
|
229
229
|
end
|
230
230
|
end
|
231
231
|
|
@@ -235,12 +235,12 @@ end
|
|
235
235
|
|
236
236
|
Then /^the new query result's time "([^\"]*)" should equal the saved query result$/ do |method_name|
|
237
237
|
methods = method_name.split '.'
|
238
|
-
@service_result.send(methods[0]).send(methods[1]).xmlschema(3).
|
238
|
+
expect(@service_result.send(methods[0]).send(methods[1]).xmlschema(3)).to eq @stored_query_result.send(methods[0]).send(methods[1]).xmlschema(3)
|
239
239
|
end
|
240
240
|
|
241
241
|
|
242
242
|
Then /^the result count should be (\d+)$/ do |expected_count|
|
243
|
-
@service_result.count.
|
243
|
+
expect(@service_result.count).to eq expected_count.to_i
|
244
244
|
end
|
245
245
|
|
246
246
|
When /^I add a link between #{capture_model} and #{capture_model} on "([^"]*)"$/ do |parent, child, property|
|
data/features/support/hooks.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
Before do
|
2
2
|
VCR.use_cassette("clean_database_for_testing") do
|
3
|
-
|
3
|
+
conn = Faraday.new(url: "http://#{WEBSERVER}:#{HTTP_PORT_NUMBER}")
|
4
|
+
conn.post '/SampleService/RubyOData.svc/CleanDatabaseForTesting'
|
4
5
|
end
|
5
|
-
end
|
6
|
+
end
|
data/features/support/vcr.rb
CHANGED
@@ -5,20 +5,20 @@ VCR.configure do |c|
|
|
5
5
|
c.hook_into :webmock
|
6
6
|
c.cassette_library_dir = "features/cassettes"
|
7
7
|
c.default_cassette_options = {
|
8
|
-
|
9
|
-
:
|
8
|
+
record: :none,
|
9
|
+
match_requests_on: [:method, OData::Support::SampleServiceMatcher]
|
10
10
|
}
|
11
11
|
end
|
12
12
|
|
13
13
|
VCR.cucumber_tags do |t|
|
14
|
-
t.tags
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
14
|
+
t.tags "@basic_auth",
|
15
|
+
"@batch_request",
|
16
|
+
"@complex_types",
|
17
|
+
"@error_handling",
|
18
|
+
"@query_builder",
|
19
|
+
"@service",
|
20
|
+
"@service_manage",
|
21
|
+
"@service_methods",
|
22
|
+
"@ssl",
|
23
|
+
"@type_conversion"
|
24
|
+
end
|
data/lib/ruby_odata.rb
CHANGED
@@ -7,7 +7,9 @@ require "active_support" # Used for serializtion to JSON
|
|
7
7
|
require "active_support/inflector"
|
8
8
|
require "active_support/core_ext"
|
9
9
|
require "cgi"
|
10
|
-
require "
|
10
|
+
require "excon"
|
11
|
+
require "faraday_middleware"
|
12
|
+
require "faraday"
|
11
13
|
require "nokogiri"
|
12
14
|
require "bigdecimal"
|
13
15
|
require "bigdecimal/util"
|
@@ -19,5 +21,6 @@ require lib + "/ruby_odata/property_metadata"
|
|
19
21
|
require lib + "/ruby_odata/query_builder"
|
20
22
|
require lib + "/ruby_odata/class_builder"
|
21
23
|
require lib + "/ruby_odata/operation"
|
24
|
+
require lib + "/ruby_odata/resource"
|
22
25
|
require lib + "/ruby_odata/service"
|
23
26
|
require lib + "/ruby_odata/helpers"
|
@@ -8,20 +8,27 @@ module OData
|
|
8
8
|
@edmx = edmx
|
9
9
|
|
10
10
|
# Get the edm namespace because it varies by version
|
11
|
-
|
12
|
-
@
|
11
|
+
root = @edmx.xpath("/edmx:Edmx").first
|
12
|
+
@version = root['Version'].to_i
|
13
|
+
edmxurl = root.namespaces['xmlns:edmx']
|
14
|
+
ds = @edmx.xpath("edmx:Edmx/edmx:DataServices/*", "edmx" => edmxurl).first
|
15
|
+
edm_ns = ds.namespaces['xmlns'].to_s
|
16
|
+
@edmx_namespaces = { "edmx" => edmxurl, "edm" => edm_ns }
|
13
17
|
parse_nav_prop(nav_prop_element)
|
14
18
|
end
|
15
19
|
|
16
20
|
private
|
17
21
|
|
18
22
|
def parse_nav_prop(element)
|
19
|
-
@
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
if @version >= 4
|
24
|
+
else
|
25
|
+
@relationship = element['Relationship']
|
26
|
+
relationship_parts = @relationship.split('.')
|
27
|
+
@name = relationship_parts.pop
|
28
|
+
@namespace = relationship_parts.join('.')
|
29
|
+
@from_role = role_hash(@name, element['FromRole'])
|
30
|
+
@to_role = role_hash(@name, element['ToRole'])
|
31
|
+
end
|
25
32
|
end
|
26
33
|
|
27
34
|
def role_hash(association_name, role_name)
|
@@ -121,7 +121,11 @@ module OData
|
|
121
121
|
# Convert Arrays into proper Collections
|
122
122
|
collections = vars.find_all { |o| o[1].class == Array } || []
|
123
123
|
collections.each do |c|
|
124
|
-
|
124
|
+
if options[:type] == :add
|
125
|
+
vars[c[0]] = c[1]
|
126
|
+
else
|
127
|
+
vars[c[0]] = { '__metadata' => { 'type' => props[c[0]].type }, 'results' => c[1] }
|
128
|
+
end
|
125
129
|
end
|
126
130
|
|
127
131
|
# Convert a BigDecimal to a string for serialization (to match Edm.Decimal)
|
@@ -1,194 +1,192 @@
|
|
1
1
|
module OData
|
2
|
-
# The query builder is used to call query operations against the service. This shouldn't be called directly, but rather it is returned from the dynamic methods created for the specific service that you are calling.
|
3
|
-
#
|
4
|
-
# @example For example, given the following code snippet:
|
5
|
-
# svc = OData::Service.new "http://127.0.0.1:8989/SampleService/RubyOData.svc"
|
6
|
-
# svc.Categories
|
7
|
-
# The *Categories* method would return a QueryBuilder
|
8
|
-
class QueryBuilder
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# Used to eagerly-load data for nested objects, for example, obtaining a Category for a Product within one call to the server
|
30
|
-
#
|
31
|
-
# @param [String] path of the entity to expand relative to the root
|
32
|
-
# @example
|
33
|
-
# # Without expanding the query (no Category will be filled in for the Product)
|
34
|
-
# svc.Products(1)
|
35
|
-
# prod1 = svc.execute
|
36
|
-
#
|
37
|
-
# # With expanding the query (the Category will be filled in)
|
38
|
-
# svc.Products(1).expand('Category')
|
39
|
-
# prod1 = svc.execute
|
40
|
-
def expand(path)
|
41
|
-
@expands << path
|
42
|
-
self
|
43
|
-
end
|
2
|
+
# The query builder is used to call query operations against the service. This shouldn't be called directly, but rather it is returned from the dynamic methods created for the specific service that you are calling.
|
3
|
+
#
|
4
|
+
# @example For example, given the following code snippet:
|
5
|
+
# svc = OData::Service.new "http://127.0.0.1:8989/SampleService/RubyOData.svc"
|
6
|
+
# svc.Categories
|
7
|
+
# The *Categories* method would return a QueryBuilder
|
8
|
+
class QueryBuilder
|
9
|
+
attr_accessor :additional_params
|
10
|
+
|
11
|
+
# Creates a new instance of the QueryBuilder class
|
12
|
+
#
|
13
|
+
# @param [String] root entity collection to query against
|
14
|
+
# @param [Hash, {}] additional_params hash of additional parameters to use for a query
|
15
|
+
def initialize(root, additional_params = {})
|
16
|
+
@root = Helpers.uri_escape(root.to_s)
|
17
|
+
@expands = []
|
18
|
+
@filters = []
|
19
|
+
@order_bys = []
|
20
|
+
@navigation_paths = []
|
21
|
+
@select = []
|
22
|
+
@skip = nil
|
23
|
+
@top = nil
|
24
|
+
@count = nil
|
25
|
+
@links_navigation_property = nil
|
26
|
+
@additional_params = additional_params
|
27
|
+
end
|
44
28
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
29
|
+
# Used to eagerly-load data for nested objects, for example, obtaining a Category for a Product within one call to the server
|
30
|
+
#
|
31
|
+
# @param [String] path of the entity to expand relative to the root
|
32
|
+
# @example
|
33
|
+
# # Without expanding the query (no Category will be filled in for the Product)
|
34
|
+
# svc.Products(1)
|
35
|
+
# prod1 = svc.execute
|
36
|
+
#
|
37
|
+
# # With expanding the query (the Category will be filled in)
|
38
|
+
# svc.Products(1).expand('Category')
|
39
|
+
# prod1 = svc.execute
|
40
|
+
def expand(path)
|
41
|
+
@expands << path
|
42
|
+
self
|
43
|
+
end
|
56
44
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
45
|
+
# Used to filter data being returned
|
46
|
+
#
|
47
|
+
# @param [String] filter conditions to apply to the query
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# svc.Products.filter("Name eq 'Product 2'")
|
51
|
+
# products = svc.execute
|
52
|
+
def filter(filter)
|
53
|
+
@filters << CGI.escape(filter)
|
54
|
+
self
|
55
|
+
end
|
68
56
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
57
|
+
# Used to order the data being returned
|
58
|
+
#
|
59
|
+
# @param [String] order_by the order by statement. Note to specify direction, use "desc" or "asc"; must be lowercase
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# svc.Products.order_by("Name")
|
63
|
+
# products = svc.execute
|
64
|
+
def order_by(order_by)
|
65
|
+
@order_bys << CGI.escape(order_by)
|
66
|
+
self
|
67
|
+
end
|
81
68
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
69
|
+
# Used to skip a number of records
|
70
|
+
# This is typically used for paging, where it would be used along with the `top` method.
|
71
|
+
#
|
72
|
+
# @param [Integer] num the number of items to skip
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# svc.Products.skip(5)
|
76
|
+
# products = svc.execute # => skips the first 5 items
|
77
|
+
def skip(num)
|
78
|
+
@skip = num
|
79
|
+
self
|
80
|
+
end
|
94
81
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@links_navigation_property = navigation_property
|
108
|
-
self
|
109
|
-
end
|
82
|
+
# Used to take only the top X records
|
83
|
+
# This is typically used for paging, where it would be used along with the `skip` method.
|
84
|
+
#
|
85
|
+
# @param [Integer] num the number of items to return
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# svc.Products.top(5)
|
89
|
+
# products = svc.execute # => returns only the first 5 items
|
90
|
+
def top(num)
|
91
|
+
@top = num
|
92
|
+
self
|
93
|
+
end
|
110
94
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
95
|
+
# Used to return links instead of actual objects
|
96
|
+
#
|
97
|
+
# @param [String] navigation_property the NavigationProperty name to retrieve the links for
|
98
|
+
#
|
99
|
+
# @raise [NotSupportedError] if count has already been called on the query
|
100
|
+
#
|
101
|
+
# @example
|
102
|
+
# svc.Categories(1).links("Products")
|
103
|
+
# product_links = svc.execute # => returns URIs for the products under the Category with an ID of 1
|
104
|
+
def links(navigation_property)
|
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?
|
107
|
+
@links_navigation_property = navigation_property
|
108
|
+
self
|
109
|
+
end
|
126
110
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
111
|
+
# Used to return a count of objects instead of the objects themselves
|
112
|
+
#
|
113
|
+
# @raise [NotSupportedError] if links has already been called on the query
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# svc.Products
|
117
|
+
# svc.count
|
118
|
+
# product_count = svc.execute
|
119
|
+
def count
|
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
|
+
|
123
|
+
@count = true
|
124
|
+
self
|
125
|
+
end
|
137
126
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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('/')]
|
127
|
+
# Used to navigate to a child collection, typically used to filter or perform a similar function against the children
|
128
|
+
#
|
129
|
+
# @param [String] navigation_property the NavigationProperty to drill-down into
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# svc.Genres('Horror Movies').navigate("Titles").filter("Name eq 'Halloween'")
|
133
|
+
def navigate(navigation_property)
|
134
|
+
@navigation_paths << Helpers.uri_escape(navigation_property)
|
135
|
+
self
|
154
136
|
end
|
155
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
|
+
self
|
157
|
+
end
|
156
158
|
|
157
|
-
|
158
|
-
|
159
|
+
# Builds the query URI (path, not including root) incorporating expands, filters, etc.
|
160
|
+
# This is used internally when the execute method is called on the service
|
161
|
+
def query
|
162
|
+
q = @root.clone
|
159
163
|
|
160
|
-
|
161
|
-
|
162
|
-
def query
|
163
|
-
q = @root.clone
|
164
|
+
# Navigation paths come first in the query
|
165
|
+
q << "/#{@navigation_paths.join("/")}" unless @navigation_paths.empty?
|
164
166
|
|
165
|
-
|
166
|
-
|
167
|
+
# Handle links queries, this isn't just a standard query option
|
168
|
+
q << "/$links/#{@links_navigation_property}" if @links_navigation_property
|
167
169
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
170
|
+
# Handle count queries, this isn't just a standard query option
|
171
|
+
q << "/$count" if @count
|
172
|
+
query_options = generate_query_options
|
172
173
|
|
173
|
-
|
174
|
-
|
175
|
-
q << "/$count"
|
174
|
+
q << "?#{query_options.join('&')}" if !query_options.empty?
|
175
|
+
q
|
176
176
|
end
|
177
177
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
178
|
+
private
|
179
|
+
|
180
|
+
def generate_query_options
|
181
|
+
query_options = []
|
182
|
+
query_options << "$select=#{@select.join(',')}" unless @select.empty?
|
183
|
+
query_options << "$expand=#{@expands.join(',')}" unless @expands.empty?
|
184
|
+
query_options << "$filter=#{@filters.join('+and+')}" unless @filters.empty?
|
185
|
+
query_options << "$orderby=#{@order_bys.join(',')}" unless @order_bys.empty?
|
186
|
+
query_options << "$skip=#{@skip}" unless @skip.nil?
|
187
|
+
query_options << "$top=#{@top}" unless @top.nil?
|
188
|
+
query_options << @additional_params.to_query unless @additional_params.empty?
|
189
|
+
query_options
|
189
190
|
end
|
190
|
-
return q
|
191
191
|
end
|
192
|
-
end
|
193
|
-
|
194
192
|
end # Module
|