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
@@ -0,0 +1,153 @@
1
+ module OData
2
+ class Resource
3
+ attr_reader :url, :options, :block
4
+
5
+ def initialize(url, options={}, backwards_compatibility=nil, &block)
6
+ @url = url
7
+ @block = block
8
+ @options = options.is_a?(Hash) ? options : { user: options, password: backwards_compatibility }
9
+
10
+ @conn = Faraday.new(url: url, ssl: { verify: verify_ssl }) do |faraday|
11
+ faraday.use :gzip
12
+ faraday.response :raise_error
13
+ faraday.adapter :excon
14
+
15
+ faraday.options.timeout = timeout if timeout
16
+ faraday.options.open_timeout = open_timeout if open_timeout
17
+
18
+ faraday.headers = (faraday.headers || {}).merge(@options[:headers] || {})
19
+ faraday.headers = (faraday.headers).merge({
20
+ :accept => '*/*; q=0.5, application/xml',
21
+ })
22
+
23
+ faraday.basic_auth user, password if user# this adds to headers so must be behind
24
+ end
25
+
26
+ @conn.headers[:user_agent] = 'Ruby'
27
+ end
28
+
29
+ def get(additional_headers={})
30
+ @conn.get do |req|
31
+ req.url url
32
+ req.headers = (headers || {}).merge(additional_headers)
33
+ end
34
+ end
35
+
36
+ def head(additional_headers={})
37
+ @conn.head do |req|
38
+ req.url url
39
+ req.headers = (headers || {}).merge(additional_headers)
40
+ end
41
+ end
42
+
43
+ def post(payload, additional_headers={})
44
+ @conn.post do |req|
45
+ req.url url
46
+ req.headers = (headers || {}).merge(additional_headers)
47
+ req.body = prepare_payload payload
48
+ end
49
+ end
50
+
51
+ def put(payload, additional_headers={})
52
+ @conn.put do |req|
53
+ req.url url
54
+ req.headers = (headers || {}).merge(additional_headers)
55
+ req.body = prepare_payload payload
56
+ end
57
+ end
58
+
59
+ def patch(payload, additional_headers={})
60
+ @conn.patch do |req|
61
+ req.url url
62
+ req.headers = (headers || {}).merge(additional_headers)
63
+ req.body = prepare_payload payload
64
+ end
65
+ end
66
+
67
+ def delete(additional_headers={})
68
+ @conn.delete do |req|
69
+ req.url url
70
+ req.headers = (headers || {}).merge(additional_headers)
71
+ end
72
+ end
73
+
74
+ def to_s
75
+ url
76
+ end
77
+
78
+ def user
79
+ options[:user]
80
+ end
81
+
82
+ def password
83
+ options[:password]
84
+ end
85
+
86
+ def verify_ssl
87
+ options[:verify_ssl]
88
+ end
89
+
90
+ def headers
91
+ @conn.headers || {}
92
+ end
93
+
94
+ def timeout
95
+ options[:timeout]
96
+ end
97
+
98
+ def open_timeout
99
+ options[:open_timeout]
100
+ end
101
+
102
+ # Construct a subresource, preserving authentication.
103
+ #
104
+ # Example:
105
+ #
106
+ # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd')
107
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
108
+ #
109
+ # This is especially useful if you wish to define your site in one place and
110
+ # call it in multiple locations:
111
+ #
112
+ # def orders
113
+ # RestClient::Resource.new('http://example.com/orders', 'admin', 'mypasswd')
114
+ # end
115
+ #
116
+ # orders.get # GET http://example.com/orders
117
+ # orders['1'].get # GET http://example.com/orders/1
118
+ # orders['1/items'].delete # DELETE http://example.com/orders/1/items
119
+ #
120
+ # Nest resources as far as you want:
121
+ #
122
+ # site = RestClient::Resource.new('http://example.com')
123
+ # posts = site['posts']
124
+ # first_post = posts['1']
125
+ # comments = first_post['comments']
126
+ # comments.post 'Hello', :content_type => 'text/plain'
127
+ #
128
+ def [](suburl, &new_block)
129
+ case
130
+ when block_given? then self.class.new(concat_urls(url, suburl), options, &new_block)
131
+ when block then self.class.new(concat_urls(url, suburl), options, &block)
132
+ else
133
+ self.class.new(concat_urls(url, suburl), options)
134
+ end
135
+ end
136
+
137
+ def concat_urls(url, suburl) # :nodoc:
138
+ url = url.to_s
139
+ suburl = suburl.to_s
140
+ if url.slice(-1, 1) == '/' or suburl.slice(0, 1) == '/'
141
+ url + suburl
142
+ else
143
+ "#{url}/#{suburl}"
144
+ end
145
+ end
146
+
147
+ def prepare_payload payload
148
+ JSON.generate(payload)
149
+ rescue JSON::GeneratorError
150
+ payload
151
+ end
152
+ end
153
+ end
@@ -9,7 +9,7 @@ class Service
9
9
  # @option options [String] :username for http basic auth
10
10
  # @option options [String] :password for http basic auth
11
11
  # @option options [Object] :verify_ssl false if no verification, otherwise mode (OpenSSL::SSL::VERIFY_PEER is default)
12
- # @option options [Hash] :rest_options a hash of rest-client options that will be passed to all RestClient::Resource.new calls
12
+ # @option options [Hash] :rest_options a hash of rest-client options that will be passed to all OData::Resource.new calls
13
13
  # @option options [Hash] :additional_params a hash of query string params that will be passed on all calls
14
14
  # @option options [Boolean, true] :eager_partial true if queries should consume partial feeds until the feed is complete, false if explicit calls to next must be performed
15
15
  def initialize(service_uri, options = {})
@@ -97,12 +97,12 @@ class Service
97
97
  # @raise [ServiceError] if there is an error when talking to the service
98
98
  def execute
99
99
  begin
100
- @response = RestClient::Resource.new(build_query_uri, @rest_options).get
100
+ @response = OData::Resource.new(build_query_uri, @rest_options).get
101
101
  rescue Exception => e
102
102
  handle_exception(e)
103
103
  end
104
- return Integer(@response) if @response =~ /^\d+$/
105
- handle_collection_result(@response)
104
+ return Integer(@response.body) if @response.body =~ /^\d+$/
105
+ handle_collection_result(@response.body)
106
106
  end
107
107
 
108
108
  # Overridden to identify methods handled by method_missing
@@ -147,8 +147,8 @@ class Service
147
147
  raise NotSupportedError, "You cannot load a property on an entity that isn't tracked" if obj.send(:__metadata).nil?
148
148
  raise ArgumentError, "'#{nav_prop}' is not a valid navigation property" unless obj.respond_to?(nav_prop.to_sym)
149
149
  raise ArgumentError, "'#{nav_prop}' is not a valid navigation property" unless @class_metadata[obj.class.to_s][nav_prop].nav_prop
150
- results = RestClient::Resource.new(build_load_property_uri(obj, nav_prop), @rest_options).get
151
- prop_results = build_classes_from_result(results)
150
+ results = OData::Resource.new(build_load_property_uri(obj, nav_prop), @rest_options).get
151
+ prop_results = build_classes_from_result(results.body)
152
152
  obj.send "#{nav_prop}=", (singular?(nav_prop) ? prop_results.first : prop_results)
153
153
  end
154
154
 
@@ -224,7 +224,7 @@ class Service
224
224
  @rest_options.merge!(options[:rest_options] || {})
225
225
  @additional_params = options[:additional_params] || {}
226
226
  @namespace = options[:namespace]
227
- @json_type = options[:json_type] || :json
227
+ @json_type = options[:json_type] || 'application/json'
228
228
  end
229
229
 
230
230
  def default_instance_vars!
@@ -236,7 +236,7 @@ class Service
236
236
  end
237
237
 
238
238
  def set_namespaces
239
- @edmx = Nokogiri::XML(RestClient::Resource.new(build_metadata_uri, @rest_options).get)
239
+ @edmx = Nokogiri::XML(OData::Resource.new(build_metadata_uri, @rest_options).get.body)
240
240
  @ds_namespaces = {
241
241
  "m" => "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata",
242
242
  "edmx" => "http://schemas.microsoft.com/ado/2007/06/edmx",
@@ -376,8 +376,8 @@ class Service
376
376
  def handle_exception(e)
377
377
  raise e unless defined? e.response
378
378
 
379
- code = e.http_code
380
- error = Nokogiri::XML(e.response)
379
+ code = e.response[:status]
380
+ error = Nokogiri::XML(e.response[:body])
381
381
 
382
382
  message = if error.xpath("m:error/m:message", @ds_namespaces).first
383
383
  error.xpath("m:error/m:message", @ds_namespaces).first.content
@@ -509,8 +509,8 @@ class Service
509
509
 
510
510
  def handle_partial
511
511
  if @next_uri
512
- result = RestClient::Resource.new(@next_uri, @rest_options).get
513
- results = handle_collection_result(result)
512
+ result = OData::Resource.new(@next_uri, @rest_options).get
513
+ results = handle_collection_result(result.body)
514
514
  end
515
515
  results
516
516
  end
@@ -600,26 +600,26 @@ class Service
600
600
  if operation.kind == "Add"
601
601
  save_uri = build_save_uri(operation)
602
602
  json_klass = operation.klass.to_json(:type => :add)
603
- post_result = RestClient::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type}
604
- return build_classes_from_result(post_result)
603
+ post_result = OData::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type}
604
+ return build_classes_from_result(post_result.body)
605
605
  elsif operation.kind == "Update"
606
606
  update_uri = build_resource_uri(operation)
607
607
  json_klass = operation.klass.to_json
608
- update_result = RestClient::Resource.new(update_uri, @rest_options).put json_klass, {:content_type => @json_type}
609
- return (update_result.code == 204)
608
+ update_result = OData::Resource.new(update_uri, @rest_options).put json_klass, {:content_type => @json_type}
609
+ return (update_result.status == 204)
610
610
  elsif operation.kind == "Delete"
611
611
  delete_uri = build_resource_uri(operation)
612
- delete_result = RestClient::Resource.new(delete_uri, @rest_options).delete
613
- return (delete_result.code == 204)
612
+ delete_result = OData::Resource.new(delete_uri, @rest_options).delete
613
+ return (delete_result.status == 204)
614
614
  elsif operation.kind == "AddLink"
615
615
  save_uri = build_add_link_uri(operation)
616
616
  json_klass = operation.child_klass.to_json(:type => :link)
617
- post_result = RestClient::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type}
617
+ post_result = OData::Resource.new(save_uri, @rest_options).post json_klass, {:content_type => @json_type}
618
618
 
619
619
  # Attach the child to the parent
620
- link_child_to_parent(operation) if (post_result.code == 204)
620
+ link_child_to_parent(operation) if (post_result.status == 204)
621
621
 
622
- return(post_result.code == 204)
622
+ return(post_result.status == 204)
623
623
  end
624
624
  end
625
625
 
@@ -633,11 +633,11 @@ class Service
633
633
  batch_uri = build_batch_uri
634
634
 
635
635
  body = build_batch_body(operations, batch_num, changeset_num)
636
- result = RestClient::Resource.new( batch_uri, @rest_options).post body, {:content_type => "multipart/mixed; boundary=batch_#{batch_num}"}
636
+ result = OData::Resource.new( batch_uri, @rest_options).post body, {:content_type => "multipart/mixed; boundary=batch_#{batch_num}"}
637
637
 
638
638
  # TODO: More result validation needs to be done.
639
639
  # The result returns HTTP 202 even if there is an error in the batch
640
- return (result.code == 202)
640
+ return (result.status == 202)
641
641
  end
642
642
  def build_batch_body(operations, batch_num, changeset_num)
643
643
  # Header
@@ -827,17 +827,17 @@ class Service
827
827
  func[:parameters].keys.each_with_index { |key, i| params[key] = args[0][i] } unless func[:parameters].nil?
828
828
 
829
829
  function_uri = build_function_import_uri(name, params)
830
- result = RestClient::Resource.new(function_uri, @rest_options).send(func[:http_method].downcase, {})
830
+ result = OData::Resource.new(function_uri, @rest_options).send(func[:http_method].downcase, {})
831
831
 
832
832
  # Is this a 204 (No content) result?
833
- return true if result.code == 204
833
+ return true if result.status == 204
834
834
 
835
835
  # No? Then we need to parse the results. There are 4 kinds...
836
836
  if func[:return_type] == Array
837
837
  # a collection of entites
838
- return build_classes_from_result(result) if @classes.include?(func[:inner_return_type].to_s)
838
+ return build_classes_from_result(result.body) if @classes.include?(func[:inner_return_type].to_s)
839
839
  # a collection of native types
840
- elements = Nokogiri::XML(result).xpath("//ds:element", @ds_namespaces)
840
+ elements = Nokogiri::XML(result.body).xpath("//ds:element", @ds_namespaces)
841
841
  results = []
842
842
  elements.each do |e|
843
843
  results << parse_primative_type(e.content, func[:inner_return_type])
@@ -847,18 +847,18 @@ class Service
847
847
 
848
848
  # a single entity
849
849
  if @classes.include?(func[:return_type].to_s)
850
- entry = Nokogiri::XML(result).xpath("atom:entry[not(ancestor::atom:entry)]", @ds_namespaces)
850
+ entry = Nokogiri::XML(result.body).xpath("atom:entry[not(ancestor::atom:entry)]", @ds_namespaces)
851
851
  return entry_to_class(entry)
852
852
  end
853
853
 
854
854
  # or a single native type
855
855
  unless func[:return_type].nil?
856
- e = Nokogiri::XML(result).xpath("/*").first
856
+ e = Nokogiri::XML(result.body).xpath("/*").first
857
857
  return parse_primative_type(e.content, func[:return_type])
858
858
  end
859
859
 
860
860
  # Nothing could be parsed, so just return if we got a 200 or not
861
- return (result.code == 200)
861
+ return (result.status == 200)
862
862
  end
863
863
 
864
864
  # Helpers
@@ -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.6"
4
+ VERSION = "0.2.0.beta1"
5
5
  end
data/ruby_odata.gemspec CHANGED
@@ -1,43 +1,48 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "ruby_odata/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "ruby_odata"
7
- s.version = OData::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ["Damien White"]
10
- s.email = ["damien.white@visoftinc.com"]
11
- s.homepage = %q{http://github.com/visoft/ruby_odata}
12
- s.summary = %q{Ruby consumer of OData services.}
13
- s.description = %q{An OData Client Library for Ruby. Use this to interact with OData services}
14
- s.license = "MIT"
15
-
16
- s.rubyforge_project = "ruby-odata"
17
-
18
- s.required_ruby_version = '>= 1.9.3'
19
-
20
- s.add_dependency("addressable", ">= 2.3.4")
21
- s.add_dependency("i18n", "~> 0.6.0")
22
- s.add_dependency("activesupport", ">= 3.0.0")
23
- s.add_dependency("rest-client", ">= 1.5.1")
24
- s.add_dependency("nokogiri", ">= 1.4.2")
25
-
26
- s.add_development_dependency("rake", "0.9.2")
27
- s.add_development_dependency("rspec", "~> 2.11.0")
28
- s.add_development_dependency("cucumber", "~> 1.2.1")
29
- s.add_development_dependency("pickle", "~> 0.4.11")
30
- s.add_development_dependency("machinist", "~> 2.0")
31
- s.add_development_dependency("webmock", "~> 1.11.0")
32
- s.add_development_dependency("guard", "~> 1.3.0")
33
- s.add_development_dependency("guard-rspec", "~> 1.2.1")
34
- s.add_development_dependency("guard-cucumber", "~> 1.2.0")
35
- s.add_development_dependency("vcr", "~> 2.5.0")
36
- s.add_development_dependency("simplecov", "~> 0.7.1")
37
- s.add_development_dependency("coveralls", "~> 0.6.7")
38
-
39
- s.files = `git ls-files`.split("\n")
40
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
41
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
42
- s.require_paths = ["lib"]
43
- end
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ruby_odata/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ruby_odata"
7
+ s.version = OData::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Damien White"]
10
+ s.email = ["damien.white@visoftinc.com"]
11
+ s.homepage = %q{http://github.com/visoft/ruby_odata}
12
+ s.summary = %q{Ruby consumer of OData services.}
13
+ s.description = %q{An OData Client Library for Ruby. Use this to interact with OData services}
14
+ s.license = "MIT"
15
+
16
+ s.rubyforge_project = "ruby-odata"
17
+
18
+ s.required_ruby_version = '>= 1.9.3'
19
+
20
+ s.add_dependency("addressable", ">= 2.3.4")
21
+ s.add_dependency("i18n", ">= 0.7.0")
22
+ s.add_dependency("activesupport", ">= 3.0.0")
23
+ s.add_dependency("excon", "~> 0.45.3")
24
+ s.add_dependency("faraday_middleware")
25
+ s.add_dependency("faraday", "~> 0.9.1")
26
+ s.add_dependency("nokogiri", ">= 1.4.2")
27
+
28
+ s.add_development_dependency("rake", ">=0.9.2")
29
+ s.add_development_dependency("rspec", "~> 3.2.0")
30
+ s.add_development_dependency("rspec-its", "~> 1.2.0")
31
+ s.add_development_dependency("cucumber", "~> 2.0.0")
32
+ s.add_development_dependency("pickle", "~> 0.5.1")
33
+ s.add_development_dependency("machinist", "~> 2.0")
34
+ s.add_development_dependency("webmock", "~> 1.21.0")
35
+ s.add_development_dependency("guard", "~> 2.12.5")
36
+ s.add_development_dependency("guard-rspec", "~> 4.5.0")
37
+ s.add_development_dependency("guard-cucumber", "~> 1.6.0")
38
+ s.add_development_dependency("vcr", "~> 2.9.3")
39
+ s.add_development_dependency("simplecov", "~> 0.7.1")
40
+ s.add_development_dependency("coveralls", "~> 0.6.7")
41
+ s.add_development_dependency("pry")
42
+ s.add_development_dependency("pry-nav")
43
+
44
+ s.files = `git ls-files`.split("\n")
45
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
46
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
47
+ s.require_paths = ["lib"]
48
+ end
@@ -4,7 +4,7 @@ module OData
4
4
  describe Association do
5
5
  before(:all) do
6
6
  stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
7
- with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
7
+ with(:headers => DEFAULT_HEADERS).
8
8
  to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
9
9
 
10
10
  @svc = OData::Service.new "http://test.com/test.svc/$metadata"
@@ -13,33 +13,37 @@ module OData
13
13
  describe "#initialize singlular navigation property" do
14
14
  before { @association = Association.new @product_category, @svc.edmx }
15
15
  subject { @association }
16
-
17
- it "should set the association name" do
16
+
17
+ it "sets the association name" do
18
18
  subject.name.should eq 'Category_Products'
19
19
  end
20
- it "should set the association namespace" do
20
+
21
+ it "sets the association namespace" do
21
22
  subject.namespace.should eq 'RubyODataService'
22
23
  end
23
- it "should set the relationship name" do
24
+
25
+ it "sets the relationship name" do
24
26
  subject.relationship.should eq 'RubyODataService.Category_Products'
25
27
  end
28
+
26
29
  context "from_role method" do
27
30
  subject { @association.from_role }
28
- it { should have_key 'Category_Products_Target'}
29
- it "should set the edmx type" do
31
+ it { should have_key 'Category_Products_Target'}
32
+ it "sets the edmx type" do
30
33
  subject['Category_Products_Target'][:edmx_type].should eq 'RubyODataService.Product'
31
34
  end
32
- it "should set the multiplicity" do
35
+ it "sets the multiplicity" do
33
36
  subject['Category_Products_Target'][:multiplicity].should eq '*'
34
37
  end
35
38
  end
39
+
36
40
  context "to_role method" do
37
41
  subject { @association.to_role }
38
- it { should have_key 'Category_Products_Source'}
39
- it "should set the edmx type" do
42
+ it { should have_key 'Category_Products_Source'}
43
+ it "sets the edmx type" do
40
44
  subject['Category_Products_Source'][:edmx_type].should eq 'RubyODataService.Category'
41
45
  end
42
- it "should set the multiplicity" do
46
+ it "sets the multiplicity" do
43
47
  subject['Category_Products_Source'][:multiplicity].should eq '1'
44
48
  end
45
49
  end