ruby_odata 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -100,3 +100,12 @@
100
100
  * Refactored exceptions to use proper error classes
101
101
  * Integrated [Guard](https://github.com/guard/guard) into the test suite for continuous testing
102
102
  * Integrated [VCR](https://github.com/myronmarston/vcr) into test suite in order to run Cucumber steps without running the test server.
103
+
104
+ ### 0.1.2
105
+ * New Features
106
+ * Added support for nokogiri >= 1.5.1 while maintaining backwards compatibility for >=1.4.2
107
+ * Backports requirement is now for >= 2.3.0
108
+ * Added the ability to pass in :rest_options to the service constructor within the options hash.
109
+
110
+ * Bug Fixes
111
+ * Prevented `svc.load_property` from mutating the obj's metadata uri (thanks [@sillylogger](https://github.com/sillylogger))
data/README.md CHANGED
@@ -16,6 +16,7 @@ The **Open Data Protocol** (OData) is a fantastic way to query and update data o
16
16
  * [Ruby OData Update v0.0.7](http://bit.ly/ruby_odata007)
17
17
  * [Ruby OData Update v0.0.8](http://bit.ly/ruby_odata008)
18
18
  * [Ruby OData Update v0.0.10](http://bit.ly/ruby_odata0010)
19
+ * [Major Ruby OData Update v0.1.0](http://bit.ly/ruby_odata010)
19
20
 
20
21
  ## Installation
21
22
  You can install ruby_odata as a gem using:
@@ -29,6 +30,7 @@ There are various options that you can pass when creating an instance of the ser
29
30
  * username: username for http basic auth
30
31
  * password: password for http basic auth
31
32
  * verify_ssl: false if no verification, otherwise mode (OpenSSL::SSL::VERIFY_PEER is default)
33
+ * rest_options: a hash of options that will be passed on to the rest-client calls. The passed in rest_options will be merged with the standard options that are set (username, password, and verify_ssl). This will allow you to set additional SSL settings. See [the rest-client docs](http://rubydoc.info/gems/rest-client/1.6.7/file/README.rdoc#SSL_Client_Certificates) for more information. Note, the options that you pass in will take precedence over the previous 3 options, so it is possible to set/override the username, password, and verify_ssl options directly with this hash.
32
34
  * additional_params: a hash of query string params that will be passed on all calls (query, new, update, delete, batch)
33
35
  * namespace: a string based namespace to create your objects in. You can specify the namespace using periods as separators (like .NET, for example `VisoftInc.Sample.Models`) or using double colons as separators (like Ruby `VisoftInc::Sample::Models`). By providing a namespace you can prevent naming collisions in your applications.
34
36
 
@@ -276,6 +278,8 @@ In order to run the tests, you need to spin up IIS running a virtual directory o
276
278
 
277
279
  The SampleService requires IIS or IIS Express. IIS Express is a free download from Microsoft and the preferred approach to running the application. Once installed, there is a batch file found in /test called "iisExpress x64.bat" that will spin up the appropriate instances needed for the Cucumber tests. There is a also an "iisExpress x86.bat" file for those of you running a 32-bit machine. The only difference is the path to the Program Files directory. Once you run the batch file, the web server will spin up. To stop the server, use 'Q' and then enter or close the command window.
278
280
 
281
+ If you are having trouble with IIS Express, you may need to perform the following: Upon running the IIS Express installer copy the config folder from the IIS Express installed folder (e.g. c:\Progam Files (x86)\IIS Express\config) to the IIS folder in your home folder (e.g. c:\Users\Administrator\Documents\IISExpress). Within the newly copied config folder, copy the aspnet.config file from the templates\PersonalWebServer\aspnet.config folder into the config folder as well (e.g. c:\Users\Administrator\Documents\IISExpres\config\aspnet.config).
282
+
279
283
  If you are testing on a Windows machine, you may encounter a problem with using Cucumber and Ruby 1.9.2. You will get a message when you fire up cucumber about missing msvcrt-ruby18.dll. The fix for this is to make sure you have the [RubyInstaller DevKit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit) installed, then do the following:
280
284
 
281
285
  gem uninstall json
@@ -17,5 +17,11 @@ module OData
17
17
  URI.escape(*args)
18
18
  end
19
19
  end
20
+
21
+ # Nokogiri changed how it handles namespaced attributes with v1.5.1, this is for backwards compatibility to >= 1.4.2
22
+ # Nokogiri now >=1.5.1 requires the namespace prefix is used
23
+ def self.get_namespaced_attribute(node, attr_name, prefix)
24
+ return node["#{prefix}:#{attr_name}"] || node[attr_name]
25
+ end
20
26
  end
21
27
  end
@@ -24,8 +24,9 @@ module OData
24
24
  @name = property_element['Name']
25
25
  @type = property_element['Type']
26
26
  @nullable = ((property_element['Nullable'] && property_element['Nullable'] == "true") || property_element.name == 'NavigationProperty') || false
27
- @fc_target_path = property_element['FC_TargetPath']
28
- @fc_keep_in_content = (property_element['FC_KeepInContent']) ? (property_element['FC_KeepInContent'] == "true") : nil
27
+ @fc_target_path = Helpers.get_namespaced_attribute(property_element, 'FC_TargetPath', 'm')
28
+ keep_in_content = Helpers.get_namespaced_attribute(property_element, 'FC_KeepInContent', 'm')
29
+ @fc_keep_in_content = (keep_in_content) ? (keep_in_content == "true") : nil
29
30
  @nav_prop = property_element.name == 'NavigationProperty'
30
31
  end
31
32
  end
@@ -6,6 +6,8 @@ module OData
6
6
  # svc.Categories
7
7
  # The *Categories* method would return a QueryBuilder
8
8
  class QueryBuilder
9
+ attr_accessor :additional_params
10
+
9
11
  # Creates a new instance of the QueryBuilder class
10
12
  #
11
13
  # @param [String] root entity collection to query against
@@ -9,6 +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
13
  # @option options [Hash] :additional_params a hash of query string params that will be passed on all calls
13
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
14
15
  def initialize(service_uri, options = {})
@@ -172,6 +173,7 @@ class Service
172
173
  @options[:eager_partial] = true
173
174
  end
174
175
  @rest_options = { :verify_ssl => get_verify_mode, :user => @options[:username], :password => @options[:password] }
176
+ @rest_options.merge!(options[:rest_options] || {})
175
177
  @additional_params = options[:additional_params] || {}
176
178
  @namespace = options[:namespace]
177
179
  end
@@ -472,13 +474,13 @@ class Service
472
474
  uri
473
475
  end
474
476
  def build_add_link_uri(operation)
475
- uri = "#{operation.klass.send(:__metadata)[:uri]}"
477
+ uri = operation.klass.send(:__metadata)[:uri].dup
476
478
  uri << "/$links/#{operation.klass_name}"
477
479
  uri << "?#{@additional_params.to_query}" unless @additional_params.empty?
478
480
  uri
479
481
  end
480
482
  def build_resource_uri(operation)
481
- uri = operation.klass.send(:__metadata)[:uri]
483
+ uri = operation.klass.send(:__metadata)[:uri].dup
482
484
  uri << "?#{@additional_params.to_query}" unless @additional_params.empty?
483
485
  uri
484
486
  end
@@ -488,7 +490,7 @@ class Service
488
490
  uri
489
491
  end
490
492
  def build_load_property_uri(obj, property)
491
- uri = obj.__metadata[:uri]
493
+ uri = obj.__metadata[:uri].dup
492
494
  uri << "/#{property}"
493
495
  uri
494
496
  end
@@ -630,7 +632,7 @@ class Service
630
632
 
631
633
  # Complex Types
632
634
  def complex_type_to_class(complex_type_xml)
633
- klass_name = qualify_class_name(complex_type_xml.attr('type').split('.')[-1])
635
+ klass_name = qualify_class_name(Helpers.get_namespaced_attribute(complex_type_xml, 'type', 'm').split('.')[-1])
634
636
  klass = @classes[klass_name].new
635
637
 
636
638
  # Fill in the properties
@@ -663,8 +665,8 @@ class Service
663
665
 
664
666
  # Parses a value into the proper type based on an xml property element
665
667
  def parse_value(property_xml)
666
- property_type = property_xml.attr('type')
667
- property_null = property_xml.attr('null')
668
+ property_type = Helpers.get_namespaced_attribute(property_xml, 'type', 'm')
669
+ property_null = Helpers.get_namespaced_attribute(property_xml, 'null', 'm')
668
670
 
669
671
  # Handle a nil property type, this is a string
670
672
  return property_xml.content if property_type.nil?
@@ -764,4 +766,4 @@ class Service
764
766
  end
765
767
  end
766
768
 
767
- end # module OData
769
+ end # module OData
@@ -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.1"
4
+ VERSION = "0.1.2"
5
5
  end
data/ruby_odata.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency("activesupport", ">= 3.0.0")
19
19
  s.add_dependency("rest-client", ">= 1.5.1")
20
20
  s.add_dependency("nokogiri", ">= 1.4.2")
21
- s.add_dependency("backports", "~> 2.3.0")
21
+ s.add_dependency("backports", ">= 2.3.0")
22
22
 
23
23
  s.add_development_dependency("rake", "0.9.2")
24
24
  s.add_development_dependency("rspec", "~> 2.11.0")
@@ -28,13 +28,13 @@ module OData
28
28
  property_metadata.nullable.should eq false
29
29
  end
30
30
  it "parses an EDMX property with the fc_target_path and fc_keep_in_content attribute" do
31
- property_element = RSpecSupport::ElementHelpers.string_to_element('<Property Name="Title" Type="Edm.String" Nullable="true" m:FC_TargetPath="SyndicationTitle" m:FC_ContentKind="text" m:FC_KeepInContent="false" />')
31
+ property_element = RSpecSupport::ElementHelpers.string_to_element('<Property xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" Name="Title" Type="Edm.String" Nullable="true" m:FC_TargetPath="SyndicationTitle" m:FC_ContentKind="text" m:FC_KeepInContent="false" />')
32
32
  property_metadata = PropertyMetadata.new property_element
33
33
  property_metadata.fc_target_path.should eq "SyndicationTitle"
34
34
  property_metadata.fc_keep_in_content.should eq false
35
35
  end
36
36
  it "parses an EDMX property where fc_keep_in_content is true" do
37
- property_element = RSpecSupport::ElementHelpers.string_to_element('<Property Name="Title" Type="Edm.String" Nullable="true" m:FC_TargetPath="SyndicationTitle" m:FC_ContentKind="text" m:FC_KeepInContent="true" />')
37
+ property_element = RSpecSupport::ElementHelpers.string_to_element('<Property xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" Name="Title" Type="Edm.String" Nullable="true" m:FC_TargetPath="SyndicationTitle" m:FC_ContentKind="text" m:FC_KeepInContent="true" />')
38
38
  property_metadata = PropertyMetadata.new property_element
39
39
  property_metadata.fc_keep_in_content.should eq true
40
40
  end
@@ -88,6 +88,23 @@ module OData
88
88
  end
89
89
  end
90
90
 
91
+ describe "additional_parameters" do
92
+ it "should be able to be added at any time" do
93
+ builder = QueryBuilder.new "PollingLocations"
94
+ builder.filter("Address/Zip eq 45693")
95
+ builder.expand("Election")
96
+ builder.additional_params[:foo] = "bar"
97
+ builder.query.should eq "PollingLocations?$expand=Election&$filter=Address%2FZip+eq+45693&foo=bar"
98
+ end
99
+
100
+ it "should not overwrite what is already there" do
101
+ builder = QueryBuilder.new "Products", { :x=>1, :y=>2 }
102
+ builder.top(10)
103
+ builder.additional_params[:foo] = "bar"
104
+ builder.query.should eq "Products?$top=10&foo=bar&x=1&y=2"
105
+ end
106
+ end
107
+
91
108
  describe "#query" do
92
109
  it "should encode spaces in IDs" do
93
110
  builder = QueryBuilder.new "Categories('Cool Stuff')"
data/spec/service_spec.rb CHANGED
@@ -36,6 +36,23 @@ module OData
36
36
  a_request(:get, "http://test.com/test.svc/$metadata?x=1&y=2").should have_been_made
37
37
  end
38
38
  end
39
+
40
+ describe "rest-client options" do
41
+ before(:each) do
42
+ # Required for the build_classes method
43
+ stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
44
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
45
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {})
46
+ end
47
+ it "should accept in options that will be passed to the rest-client lib" do
48
+ svc = OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } }
49
+ svc.options[:rest_options].should eq Hash[:ssl_ca_file => "ca_certificate.pem"]
50
+ end
51
+ it "should merge the rest options with the built in options" do
52
+ svc = OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } }
53
+ svc.instance_variable_get(:@rest_options).should eq Hash[:verify_ssl => 1, :user => nil, :password => nil, :ssl_ca_file => "ca_certificate.pem"]
54
+ end
55
+ end
39
56
  end
40
57
  describe "additional query string parameters" do
41
58
  before(:each) do
@@ -466,6 +483,15 @@ module OData
466
483
  category.Products[0].Id.should eq 1
467
484
  category.Products[1].Id.should eq 2
468
485
  end
486
+
487
+ it "should not mutate the object's metadata" do
488
+ svc = OData::Service.new "http://test.com/test.svc/"
489
+ svc.Products(1)
490
+ product = svc.execute.first
491
+ original_metadata = product.__metadata.to_json
492
+ svc.load_property(product, "Category")
493
+ product.__metadata.to_json.should == original_metadata
494
+ end
469
495
  end
470
496
 
471
497
  describe "find, create, add, update, and delete" do
@@ -704,6 +730,12 @@ module OData
704
730
  end
705
731
  end
706
732
  end
733
+
734
+ describe "restful options" do
735
+ it "should allow " do
736
+
737
+ end
738
+ end
707
739
  end
708
740
 
709
741
  describe_private OData::Service do
@@ -723,4 +755,4 @@ module OData
723
755
  end
724
756
  end
725
757
  end
726
- end
758
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_odata
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-25 00:00:00.000000000 Z
12
+ date: 2013-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: i18n
@@ -80,7 +80,7 @@ dependencies:
80
80
  requirement: !ruby/object:Gem::Requirement
81
81
  none: false
82
82
  requirements:
83
- - - ~>
83
+ - - ! '>='
84
84
  - !ruby/object:Gem::Version
85
85
  version: 2.3.0
86
86
  type: :runtime
@@ -88,7 +88,7 @@ dependencies:
88
88
  version_requirements: !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
91
- - - ~>
91
+ - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: 2.3.0
94
94
  - !ruby/object:Gem::Dependency
@@ -472,7 +472,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
472
472
  version: '0'
473
473
  segments:
474
474
  - 0
475
- hash: 737449873321699372
475
+ hash: 502212128112312954
476
476
  required_rubygems_version: !ruby/object:Gem::Requirement
477
477
  none: false
478
478
  requirements:
@@ -481,7 +481,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
481
481
  version: '0'
482
482
  segments:
483
483
  - 0
484
- hash: 737449873321699372
484
+ hash: 502212128112312954
485
485
  requirements: []
486
486
  rubyforge_project: ruby-odata
487
487
  rubygems_version: 1.8.24