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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +2 -0
  5. data/Guardfile +1 -1
  6. data/features/basic_auth.feature +13 -13
  7. data/features/cassettes/basic_auth_protected_resource.yml +22 -20
  8. data/features/cassettes/batch_request_additions.yml +26 -26
  9. data/features/cassettes/batch_request_deletes.yml +27 -27
  10. data/features/cassettes/batch_request_updates.yml +26 -26
  11. data/features/cassettes/clean_database_for_testing.yml +10 -10
  12. data/features/cassettes/cucumber_tags/basic_auth.yml +54 -163
  13. data/features/cassettes/cucumber_tags/batch_request.yml +229 -1118
  14. data/features/cassettes/cucumber_tags/complex_types.yml +81 -191
  15. data/features/cassettes/cucumber_tags/error_handling.yml +45 -33
  16. data/features/cassettes/cucumber_tags/query_builder.yml +555 -1248
  17. data/features/cassettes/cucumber_tags/service_manage.yml +216 -591
  18. data/features/cassettes/cucumber_tags/service_methods.yml +137 -412
  19. data/features/cassettes/cucumber_tags/ssl.yml +121 -117
  20. data/features/cassettes/cucumber_tags/type_conversion.yml +104 -170
  21. data/features/cassettes/service_manage_additions.yml +26 -28
  22. data/features/cassettes/service_manage_deletions.yml +21 -21
  23. data/features/cassettes/service_manage_deletions_2.yml +21 -21
  24. data/features/cassettes/unsecured_metadata.yml +14 -14
  25. data/features/service.feature +37 -37
  26. data/features/service_methods.feature +38 -38
  27. data/features/step_definitions/pickle_steps.rb +5 -5
  28. data/features/step_definitions/service_steps.rb +26 -26
  29. data/features/support/hooks.rb +3 -2
  30. data/features/support/vcr.rb +13 -13
  31. data/lib/ruby_odata.rb +4 -1
  32. data/lib/ruby_odata/association.rb +15 -8
  33. data/lib/ruby_odata/class_builder.rb +5 -1
  34. data/lib/ruby_odata/query_builder.rb +171 -173
  35. data/lib/ruby_odata/resource.rb +153 -0
  36. data/lib/ruby_odata/service.rb +30 -30
  37. data/lib/ruby_odata/version.rb +1 -1
  38. data/ruby_odata.gemspec +48 -43
  39. data/spec/association_spec.rb +15 -11
  40. data/spec/fixtures/decimal/metadata.xml +1 -0
  41. data/spec/fixtures/v4/edmx_metadata.xml +145 -0
  42. data/spec/fixtures/v4/result_categories.xml +0 -0
  43. data/spec/revised_service_spec.rb +16 -11
  44. data/spec/service_spec.rb +149 -114
  45. data/spec/service_v4_spec.rb +102 -0
  46. data/spec/spec_helper.rb +6 -1
  47. metadata +109 -32
  48. 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).should be_nil
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).should_not be_any(&:nil?)
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).should include(model!(target))
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
- lambda { @service = OData::Service.new(BASICAUTH_URL, { :username => username, :password => password }) }.should raise_error(msg)
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
- lambda { @service = OData::Service.new(BASICAUTH_URL) }.should raise_error(msg)
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.should eq result.to_i
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
- lambda { @service.send(method) }.should_not raise_error
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
- lambda { @service.send(method) }.should raise_error
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
- lambda { @service.send(method, args) }.should_not raise_error
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.should eq type
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).should eq true
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.should eq value
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).should eq nil
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.should eq type
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.should eq type
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.should eq result
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.should eq value
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
- lambda { @service.send(method.to_sym, obj) }.should raise_error(msg)
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
- lambda { @service.save_changes }.should raise_error(/#{msg}.*/)
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.should eq []
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 = Cucumber::Ast::Table.new(values)
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 = Cucumber::Ast::Table.new(results)
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).should eq true
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?.should eq false
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.should eq value
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.should eq type
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.should eq type
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).should eq @stored_query_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.should eq expected_count.to_i
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|
@@ -1,5 +1,6 @@
1
1
  Before do
2
2
  VCR.use_cassette("clean_database_for_testing") do
3
- RestClient.post "http://#{WEBSERVER}:#{HTTP_PORT_NUMBER}/SampleService/RubyOData.svc/CleanDatabaseForTesting", {}
3
+ conn = Faraday.new(url: "http://#{WEBSERVER}:#{HTTP_PORT_NUMBER}")
4
+ conn.post '/SampleService/RubyOData.svc/CleanDatabaseForTesting'
4
5
  end
5
- end
6
+ end
@@ -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
- :record => :none,
9
- :match_requests_on => [:method, OData::Support::SampleServiceMatcher]
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 "@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
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 "rest_client"
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
- edm_ns = @edmx.xpath("edmx:Edmx/edmx:DataServices/*", "edmx" => "http://schemas.microsoft.com/ado/2007/06/edmx").first.namespaces['xmlns'].to_s
12
- @edmx_namespaces = { "edmx" => "http://schemas.microsoft.com/ado/2007/06/edmx", "edm" => edm_ns }
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
- @relationship = element['Relationship']
20
- relationship_parts = @relationship.split('.')
21
- @name = relationship_parts.pop
22
- @namespace = relationship_parts.join('.')
23
- @from_role = role_hash(@name, element['FromRole'])
24
- @to_role = role_hash(@name, element['ToRole'])
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
- vars[c[0]] = { '__metadata' => { 'type' => props[c[0]].type }, 'results' => c[1] }
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
- 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
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
- # 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
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
- # 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
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
- # 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
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
- # 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
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
- # 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
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
- # 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
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
- # 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
136
- end
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
- # 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('/')]
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
- self
158
- end
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
- # Builds the query URI (path, not including root) incorporating expands, filters, etc.
161
- # This is used internally when the execute method is called on the service
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
- # Navigation paths come first in the query
166
- q << "/" + @navigation_paths.join("/") unless @navigation_paths.empty?
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
- # Handle links queries, this isn't just a standard query option
169
- if @links_navigation_property
170
- q << "/$links/#{@links_navigation_property}"
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
- # Handle count queries, this isn't just a standard query option
174
- if @count
175
- q << "/$count"
174
+ q << "?#{query_options.join('&')}" if !query_options.empty?
175
+ q
176
176
  end
177
177
 
178
- query_options = []
179
- query_options << "$select=#{@select.join(',')}" unless @select.empty?
180
- query_options << "$expand=#{@expands.join(',')}" unless @expands.empty?
181
- query_options << "$filter=#{@filters.join('+and+')}" unless @filters.empty?
182
- query_options << "$orderby=#{@order_bys.join(',')}" unless @order_bys.empty?
183
- query_options << "$skip=#{@skip}" unless @skip.nil?
184
- query_options << "$top=#{@top}" unless @top.nil?
185
- query_options << @additional_params.to_query unless @additional_params.empty?
186
- if !query_options.empty?
187
- q << "?"
188
- q << query_options.join('&')
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