ruby_odata 0.1.6 → 0.2.0.beta1

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.
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