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 +9 -0
- data/README.md +4 -0
- data/lib/ruby_odata/helpers.rb +6 -0
- data/lib/ruby_odata/property_metadata.rb +3 -2
- data/lib/ruby_odata/query_builder.rb +2 -0
- data/lib/ruby_odata/service.rb +9 -7
- data/lib/ruby_odata/version.rb +1 -1
- data/ruby_odata.gemspec +1 -1
- data/spec/property_metadata_spec.rb +2 -2
- data/spec/query_builder_spec.rb +17 -0
- data/spec/service_spec.rb +33 -1
- metadata +6 -6
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
|
data/lib/ruby_odata/helpers.rb
CHANGED
@@ -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
|
28
|
-
|
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
|
data/lib/ruby_odata/service.rb
CHANGED
@@ -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 =
|
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(
|
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 =
|
667
|
-
property_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
|
data/lib/ruby_odata/version.rb
CHANGED
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", "
|
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
|
data/spec/query_builder_spec.rb
CHANGED
@@ -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.
|
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:
|
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:
|
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:
|
484
|
+
hash: 502212128112312954
|
485
485
|
requirements: []
|
486
486
|
rubyforge_project: ruby-odata
|
487
487
|
rubygems_version: 1.8.24
|