ruby_odata 0.0.10 → 0.1.0

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 (173) hide show
  1. data/.gitignore +9 -3
  2. data/.travis.yml +4 -0
  3. data/CHANGELOG.rdoc +18 -1
  4. data/README.rdoc +77 -19
  5. data/config/cucumber.yml +4 -3
  6. data/features/basic_auth.feature +5 -7
  7. data/features/batch_request.feature +11 -11
  8. data/features/complex_types.feature +11 -11
  9. data/features/query_builder.feature +27 -18
  10. data/features/service.feature +8 -8
  11. data/features/service_manage.feature +27 -12
  12. data/features/service_methods.feature +37 -0
  13. data/features/ssl.feature +4 -4
  14. data/features/step_definitions/pickle_steps.rb +100 -0
  15. data/features/step_definitions/service_steps.rb +99 -65
  16. data/features/support/constants.rb +3 -0
  17. data/features/support/custom_helpers.rb +54 -0
  18. data/features/support/env.rb +0 -10
  19. data/features/support/hooks.rb +1 -1
  20. data/features/support/pickle.rb +82 -0
  21. data/features/type_conversion.feature +10 -10
  22. data/lib/ruby_odata.rb +4 -1
  23. data/lib/ruby_odata/association.rb +36 -0
  24. data/lib/ruby_odata/class_builder.rb +96 -16
  25. data/lib/ruby_odata/helpers.rb +10 -0
  26. data/lib/ruby_odata/operation.rb +7 -5
  27. data/lib/ruby_odata/property_metadata.rb +11 -6
  28. data/lib/ruby_odata/query_builder.rb +20 -1
  29. data/lib/ruby_odata/service.rb +407 -94
  30. data/lib/ruby_odata/version.rb +1 -1
  31. data/ruby_odata.gemspec +2 -0
  32. data/spec/association_spec.rb +48 -0
  33. data/spec/class_builder_spec.rb +11 -2
  34. data/spec/fixtures/inheritance/edmx_pluralsight.xml +111 -0
  35. data/spec/fixtures/inheritance/result_pluralsight_courses.xml +229 -0
  36. data/spec/fixtures/links/result_links_query.xml +6 -0
  37. data/spec/fixtures/partial/partial_feed_metadata.xml +25 -0
  38. data/spec/fixtures/partial/partial_feed_part_1.xml +42 -0
  39. data/spec/fixtures/partial/partial_feed_part_2.xml +42 -0
  40. data/spec/fixtures/partial/partial_feed_part_3.xml +40 -0
  41. data/spec/fixtures/sample_service/edmx_categories_products.xml +1 -0
  42. data/spec/fixtures/sample_service/result_category_names.xml +5 -0
  43. data/spec/fixtures/sample_service/result_entity_category_web_get.xml +29 -0
  44. data/spec/fixtures/sample_service/result_entity_single_category_web_get.xml +23 -0
  45. data/spec/fixtures/sample_service/result_first_category_id.xml +2 -0
  46. data/spec/fixtures/sample_service/result_multiple_category_products.xml +57 -0
  47. data/spec/fixtures/sample_service/result_single_category.xml +18 -0
  48. data/spec/fixtures/sample_service/result_single_product.xml +26 -0
  49. data/spec/fixtures/sample_service/result_single_product_not_found.xml +4 -0
  50. data/spec/fixtures/sap/edmx_sap_demo_flight.xml +62 -58
  51. data/spec/property_metadata_spec.rb +9 -2
  52. data/spec/query_builder_spec.rb +11 -0
  53. data/spec/revised_service_spec.rb +197 -0
  54. data/spec/service_spec.rb +463 -4
  55. data/test/RubyODataService/RubyODataService.sln +20 -0
  56. data/test/RubyODataService/RubyODataService/App_Start/EntityFramework.SqlServerCompact.cs +12 -0
  57. data/test/RubyODataService/RubyODataService/BasicAuth/RubyOData.svc +3 -0
  58. data/test/RubyODataService/RubyODataService/BasicAuth/RubyOData.svc.cs +107 -0
  59. data/test/RubyODataService/RubyODataService/Global.asax +1 -0
  60. data/test/RubyODataService/RubyODataService/Global.asax.cs +49 -0
  61. data/test/RubyODataService/RubyODataService/Models/AuditFields.cs +16 -0
  62. data/test/RubyODataService/RubyODataService/Models/Category.cs +16 -0
  63. data/test/RubyODataService/RubyODataService/Models/Product.cs +20 -0
  64. data/test/RubyODataService/RubyODataService/Properties/AssemblyInfo.cs +35 -0
  65. data/test/RubyODataService/RubyODataService/RubyOData.svc +3 -0
  66. data/test/RubyODataService/RubyODataService/RubyOData.svc.cs +57 -0
  67. data/test/RubyODataService/RubyODataService/RubyODataContext.cs +16 -0
  68. data/test/RubyODataService/RubyODataService/RubyODataService.csproj +159 -0
  69. data/test/RubyODataService/RubyODataService/RubyODataService.csproj.user +31 -0
  70. data/test/RubyODataService/RubyODataService/Web.Debug.config +30 -0
  71. data/test/RubyODataService/RubyODataService/Web.Release.config +31 -0
  72. data/test/RubyODataService/RubyODataService/Web.config +27 -0
  73. data/test/RubyODataService/RubyODataService/bin/EntityFramework.dll +0 -0
  74. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Edm.dll +0 -0
  75. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.OData.dll +0 -0
  76. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Services.Client.dll +0 -0
  77. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Services.dll +0 -0
  78. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Spatial.dll +0 -0
  79. data/test/RubyODataService/RubyODataService/bin/RubyODataService.dll +0 -0
  80. data/test/RubyODataService/RubyODataService/bin/System.Data.SqlServerCe.Entity.dll +0 -0
  81. data/test/RubyODataService/RubyODataService/bin/System.Data.SqlServerCe.dll +0 -0
  82. data/test/RubyODataService/RubyODataService/bin/System.Spatial.dll +0 -0
  83. data/test/RubyODataService/RubyODataService/bin/WebActivator.dll +0 -0
  84. data/test/RubyODataService/RubyODataService/bin/amd64/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  85. data/test/RubyODataService/RubyODataService/bin/amd64/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  86. data/test/RubyODataService/RubyODataService/bin/amd64/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  87. data/test/RubyODataService/RubyODataService/bin/amd64/sqlcecompact40.dll +0 -0
  88. data/test/RubyODataService/RubyODataService/bin/amd64/sqlceer40EN.dll +0 -0
  89. data/test/RubyODataService/RubyODataService/bin/amd64/sqlceme40.dll +0 -0
  90. data/test/RubyODataService/RubyODataService/bin/amd64/sqlceqp40.dll +0 -0
  91. data/test/RubyODataService/RubyODataService/bin/amd64/sqlcese40.dll +0 -0
  92. data/test/RubyODataService/RubyODataService/bin/x86/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  93. data/test/RubyODataService/RubyODataService/bin/x86/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  94. data/test/RubyODataService/RubyODataService/bin/x86/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  95. data/test/RubyODataService/RubyODataService/bin/x86/sqlcecompact40.dll +0 -0
  96. data/test/RubyODataService/RubyODataService/bin/x86/sqlceer40EN.dll +0 -0
  97. data/test/RubyODataService/RubyODataService/bin/x86/sqlceme40.dll +0 -0
  98. data/test/RubyODataService/RubyODataService/bin/x86/sqlceqp40.dll +0 -0
  99. data/test/RubyODataService/RubyODataService/bin/x86/sqlcese40.dll +0 -0
  100. data/test/RubyODataService/RubyODataService/packages.config +7 -0
  101. data/test/RubyODataService/packages/EntityFramework.4.2.0.0/EntityFramework.4.2.0.0.nupkg +0 -0
  102. data/test/RubyODataService/packages/EntityFramework.4.2.0.0/lib/net40/EntityFramework.dll +0 -0
  103. data/test/RubyODataService/packages/EntityFramework.4.2.0.0/lib/net40/EntityFramework.xml +13488 -0
  104. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/Content/App_Start/EntityFramework.SqlServerCompact.cs.pp +12 -0
  105. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/EULA_ENU.rtf +969 -0
  106. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/EntityFramework.SqlServerCompact.4.1.8482.2.nupkg +0 -0
  107. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/lib/System.Data.SqlServerCe.Entity.dll +0 -0
  108. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/tools/install.ps1 +3 -0
  109. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Content/web.config.transform +8 -0
  110. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  111. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  112. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  113. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlcecompact40.dll +0 -0
  114. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlceer40EN.dll +0 -0
  115. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlceme40.dll +0 -0
  116. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlceqp40.dll +0 -0
  117. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlcese40.dll +0 -0
  118. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  119. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  120. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  121. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlcecompact40.dll +0 -0
  122. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlceer40EN.dll +0 -0
  123. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlceme40.dll +0 -0
  124. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlceqp40.dll +0 -0
  125. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlcese40.dll +0 -0
  126. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/SQLCE_EULA_ENU.rtf +778 -0
  127. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/SqlServerCompact.4.0.8482.1.nupkg +0 -0
  128. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Tools/GetSqlCEPostBuildCmd.ps1 +12 -0
  129. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Tools/install.ps1 +11 -0
  130. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Tools/uninstall.ps1 +9 -0
  131. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/lib/System.Data.SqlServerCe.dll +0 -0
  132. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/DataSvcUtil.exe +0 -0
  133. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/License.rtf +708 -0
  134. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Edm.dll +0 -0
  135. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Edm.xml +4150 -0
  136. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.OData.dll +0 -0
  137. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.OData.xml +1969 -0
  138. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Client.dll +0 -0
  139. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Client.xml +1442 -0
  140. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Design.dll +0 -0
  141. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Design.xml +191 -0
  142. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.dll +0 -0
  143. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.xml +2559 -0
  144. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Spatial.dll +0 -0
  145. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Spatial.xml +15 -0
  146. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.VsDesigner.DataServices.Adapter.dll +0 -0
  147. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/README.txt +6 -0
  148. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/System.Spatial.dll +0 -0
  149. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/System.Spatial.xml +2276 -0
  150. data/test/RubyODataService/packages/WebActivator.1.0.0.0/WebActivator.1.0.0.0.nupkg +0 -0
  151. data/test/RubyODataService/packages/WebActivator.1.0.0.0/lib/WebActivator.dll +0 -0
  152. data/test/RubyODataService/packages/repositories.config +4 -0
  153. data/test/applicationhost.config.template +2 -2
  154. data/test/blueprints.rb +5 -4
  155. data/test/iisExpress x64.bat b/data/test/iisExpress → x64.bat +0 -0
  156. data/test/iisExpress x86.bat b/data/test/iisExpress → x86.bat +0 -0
  157. data/test/setpath.rb +13 -3
  158. data/test/usage_samples/querying.rb +45 -0
  159. data/test/usage_samples/reflection.rb +16 -0
  160. data/test/usage_samples/sample_data.rb +30 -0
  161. metadata +327 -36
  162. data/test/Cassini x64.bat +0 -1
  163. data/test/Cassini x86.bat +0 -1
  164. data/test/SampleService/App_Code/AuditFields.cs +0 -13
  165. data/test/SampleService/App_Code/Entities.cs +0 -145
  166. data/test/SampleService/App_Code/Model.Designer.cs +0 -578
  167. data/test/SampleService/App_Code/Model.edmx +0 -157
  168. data/test/SampleService/App_Code/ModelContainerExtended.cs +0 -32
  169. data/test/SampleService/App_Data/_TestDB.mdf +0 -0
  170. data/test/SampleService/App_Data/_TestDB_Log.ldf +0 -0
  171. data/test/SampleService/BasicAuth/Entities.svc +0 -1
  172. data/test/SampleService/Entities.svc +0 -1
  173. data/test/SampleService/web.config +0 -37
@@ -0,0 +1,3 @@
1
+ HTTP_PORT_NUMBER = ENV.include?("ODATA_PORT") ? ENV["ODATA_PORT"] : 8989
2
+ HTTPS_PORT_NUMBER = ENV.include?("ODATA_SSL_PORT") ? ENV["ODATA_SSL_PORT"] : 44300
3
+ WEBSERVER = ENV.include?("ODATA_SERVER") ? ENV["ODATA_SERVER"] : 'localhost'
@@ -0,0 +1,54 @@
1
+ module CustomHelpers
2
+ # Used to access the first result of a query
3
+ def first_result
4
+ @service_result = @service_result.first if @service_result.is_a? Enumerable
5
+ end
6
+
7
+ # Used to access the first save result
8
+ def first_save
9
+ @saved_result = @saved_result.first if @saved_result.is_a? Enumerable
10
+ end
11
+
12
+ # Allows the string @@LastSave to be used when checking results
13
+ def handle_last_save_fields(val)
14
+ if val =~ /^@@LastSave.first$/
15
+ val = @saved_result.first
16
+ end
17
+ if val =~ /^@@LastSave$/
18
+ val = @saved_result
19
+ end
20
+ val
21
+ end
22
+
23
+ # Takes in comma-delimited fields string (like key: "value") and parses it into a hash
24
+ def parse_fields_string(fields)
25
+ fields_hash = {}
26
+
27
+ if !fields.nil?
28
+ fields.split(', ').each do |field|
29
+ if field =~ /^(?:(\w+): "(.*)")$/
30
+ key = $1
31
+ val = handle_last_save_fields($2)
32
+
33
+ fields_hash.merge!({key => val})
34
+ end
35
+ end
36
+ end
37
+ fields_hash
38
+ end
39
+
40
+ # Takes in a hash and convert special values (like @@LastSave) into the appropriate values
41
+ def parse_fields_hash(fields)
42
+ fields_hash = {}
43
+
44
+ if !fields.nil?
45
+ fields.each do |key, val|
46
+ val = handle_last_save_fields(val)
47
+ fields_hash.merge!({key => val})
48
+ end
49
+ end
50
+ fields_hash
51
+ end
52
+
53
+ end
54
+ World(CustomHelpers)
@@ -4,13 +4,3 @@ require lib + '/ruby_odata'
4
4
  require 'machinist/object'
5
5
  require 'sham'
6
6
  require 'faker'
7
-
8
- require 'fileutils'
9
- root_dir = File.expand_path(File.join(File.dirname(__FILE__), "../..", "test/SampleService/App_Data"))
10
-
11
- if !File.exists?("#{root_dir}/TestDB.mdf")
12
- FileUtils.copy("#{root_dir}/_TestDB.mdf", "#{root_dir}/TestDB.mdf")
13
- FileUtils.copy("#{root_dir}/_TestDB_Log.ldf", "#{root_dir}/TestDB_Log.ldf")
14
- end
15
-
16
- HTTP_PORT_NUMBER = 8989
@@ -1,4 +1,4 @@
1
1
  Before do
2
2
  Sham.reset
3
- RestClient.post "http://localhost:#{HTTP_PORT_NUMBER}/SampleService/Entities.svc/CleanDatabaseForTesting", {}
3
+ RestClient.post "http://#{WEBSERVER}:#{HTTP_PORT_NUMBER}/SampleService/RubyOData.svc/CleanDatabaseForTesting", {}
4
4
  end
@@ -0,0 +1,82 @@
1
+ require 'pickle/world'
2
+ require File.expand_path('../../../lib/ruby_odata', __FILE__)
3
+
4
+ module OData
5
+
6
+ module PickleAdapter
7
+ include Pickle::Adapter::Base
8
+
9
+ @@service = OData::Service.new "http://#{WEBSERVER}:#{HTTP_PORT_NUMBER}/SampleService/RubyOData.svc"
10
+
11
+ # Do not consider these to be part of the class list
12
+ def self.except_classes
13
+ @@except_classes ||= []
14
+ end
15
+
16
+ # Gets a list of the available models for this adapter
17
+ def self.model_classes
18
+ @@service.classes.values
19
+ end
20
+
21
+ # get a list of column names for a given class
22
+ def self.column_names(klass)
23
+ klass.properties.keys
24
+ end
25
+
26
+ # Get an instance by id of the model
27
+ def self.get_model(klass, id, expand = false)
28
+ collection = klass.to_s.split('::').last.pluralize
29
+ query = @@service.send collection, id
30
+
31
+ if expand then
32
+ # Expand all navigation properties
33
+ navigation_properties = klass.properties.select { |k, v| v.nav_prop }
34
+ # Ruby 1.9 Hash.select returns a Hash, 1.8 returns an Array, so normalize the return type
35
+ props = (navigation_properties.is_a? Hash) ? navigation_properties : Hash[*navigation_properties.flatten]
36
+ props.keys.each do |prop|
37
+ query.expand(prop)
38
+ end
39
+ end
40
+
41
+ @@service.execute.first
42
+ end
43
+
44
+ # Find the first instance matching conditions
45
+ def self.find_first_model(klass, conditions)
46
+ collection = klass.to_s.split('::').last.pluralize
47
+ q = @@service.send collection
48
+ q.filter(conditions)
49
+ q.take(1)
50
+ @@service.execute.first
51
+ end
52
+
53
+ # Find all models matching conditions
54
+ def self.find_all_models(klass, conditions)
55
+ collection = klass.to_s.split('::').last.pluralize
56
+ q = @@service.send collection
57
+ q.filter(conditions)
58
+ @@service.execute
59
+ end
60
+
61
+ # Create a model using attributes
62
+ def self.create_model(klass, attributes)
63
+ instance = klass.send :make, attributes
64
+
65
+ collection = klass.to_s.split('::').last.pluralize
66
+ @@service.send "AddTo#{collection}", instance
67
+ @@service.save_changes.first
68
+ end
69
+
70
+ end
71
+ end
72
+
73
+ module Pickle
74
+ module Session
75
+ # return a newly selected model with the navigation properties expanded
76
+ def model_with_associations(name)
77
+ model = created_model(name)
78
+ return nil unless model
79
+ OData::PickleAdapter.get_model(model.class, model.id, true)
80
+ end
81
+ end
82
+ end
@@ -4,7 +4,7 @@ Feature: Type conversion
4
4
  I want types returned to be accurately represented
5
5
 
6
6
  Background:
7
- Given a sample HTTP ODataService exists
7
+ Given a HTTP ODataService exists
8
8
  And blueprints exist for the service
9
9
 
10
10
  Scenario: Integers should be Fixnums
@@ -12,42 +12,42 @@ Scenario: Integers should be Fixnums
12
12
  And I save changes
13
13
  When I call "Products" on the service
14
14
  And I run the query
15
- Then the "Id" method should return a Fixnum
15
+ Then the "Id" method on the object should return a Fixnum
16
16
 
17
17
  Scenario: Decimals should be BigDecimals
18
18
  Given I call "AddToProducts" on the service with a new "Product" object
19
19
  And I save changes
20
20
  When I call "Products" on the service
21
21
  And I run the query
22
- Then the "Price" method should return a BigDecimal
22
+ Then the "Price" method on the object should return a BigDecimal
23
23
 
24
24
  Scenario: DateTimes should be Times
25
25
  Given I call "AddToProducts" on the service with a new "Product" object
26
26
  And I save changes
27
27
  When I call "Products" on the service
28
28
  And I run the query
29
- Then the "AuditFields.CreateDate" method should return a Time
29
+ Then the "AuditFields.CreateDate" method on the object should return a Time
30
30
 
31
31
  Scenario: Verify that DateTimes don't change if not modified on an update
32
32
  Given I call "AddToProducts" on the service with a new "Product" object with Name: "Test Product"
33
33
  When I save changes
34
34
  And I call "Products" on the service with args: "1"
35
35
  And I run the query
36
- Then I store the last query result for comparison
37
- When I set "Name" on the result to "Changed Test Product"
38
- Then the method "Name" on the result should equal: "Changed Test Product"
39
- And I call "update_object" on the service with the last query result
36
+ Then I store the first last query result for comparison
37
+ When I set "Name" on the first result to "Changed Test Product"
38
+ Then the method "Name" on the first result should equal: "Changed Test Product"
39
+ And I call "update_object" on the service with the first last query result
40
40
  And I save changes
41
41
  Then the save result should equal: "true"
42
42
  When I call "Products" on the service with args: "1"
43
43
  And I run the query
44
- Then the new query result's time "AuditFields.CreateDate" should equal the saved query result
44
+ Then the new query first result's time "AuditFields.CreateDate" should equal the saved query result
45
45
 
46
46
  Scenario: DateTimes should be able to be null
47
47
  Given I call "AddToProducts" on the service with a new "Product" object
48
48
  And I save changes
49
49
  When I call "Products" on the service
50
50
  And I run the query
51
- Then the "DiscontinuedDate" method should return a NilClass
51
+ Then the "DiscontinuedDate" method on the object should return a NilClass
52
52
 
53
53
 
@@ -10,9 +10,12 @@ require 'rest_client'
10
10
  require 'nokogiri'
11
11
  require 'bigdecimal'
12
12
  require 'bigdecimal/util'
13
+ require 'backports'
13
14
 
15
+ require lib + '/ruby_odata/association'
14
16
  require lib + '/ruby_odata/property_metadata'
15
17
  require lib + '/ruby_odata/query_builder'
16
18
  require lib + '/ruby_odata/class_builder'
17
19
  require lib + '/ruby_odata/operation'
18
- require lib + '/ruby_odata/service'
20
+ require lib + '/ruby_odata/service'
21
+ require lib + '/ruby_odata/helpers'
@@ -0,0 +1,36 @@
1
+ module OData
2
+ class Association
3
+
4
+ attr_reader :name, :namespace, :relationship, :from_role, :to_role
5
+
6
+ def initialize(nav_prop_element, edmx)
7
+ @edmx = edmx
8
+
9
+ # Get the edm namespace because it varies by version
10
+ edm_ns = @edmx.xpath("edmx:Edmx/edmx:DataServices/*", "edmx" => "http://schemas.microsoft.com/ado/2007/06/edmx").first.namespaces['xmlns'].to_s
11
+ @edmx_namespaces = { "edmx" => "http://schemas.microsoft.com/ado/2007/06/edmx", "edm" => edm_ns }
12
+ parse_nav_prop(nav_prop_element)
13
+ end
14
+
15
+ private
16
+
17
+ def parse_nav_prop(element)
18
+ @relationship = element['Relationship']
19
+ relationship_parts = @relationship.split('.')
20
+ @name = relationship_parts.pop
21
+ @namespace = relationship_parts.join('.')
22
+ @from_role = role_hash(@name, element['FromRole'])
23
+ @to_role = role_hash(@name, element['ToRole'])
24
+ end
25
+
26
+ def role_hash(association_name, role_name)
27
+ # Find the end role based on the assocation name
28
+ role_xpath = "/edmx:Edmx/edmx:DataServices/edm:Schema[@Namespace='#{@namespace}']/edm:Association[@Name='#{association_name}']/edm:End[@Role='#{role_name}']"
29
+ role_element = @edmx.xpath(role_xpath, @edmx_namespaces).first
30
+ { role_name => {
31
+ :edmx_type => "#{role_element['Type']}",
32
+ :multiplicity => "#{role_element['Multiplicity']}"
33
+ }}
34
+ end
35
+ end
36
+ end
@@ -4,61 +4,99 @@ module OData
4
4
  # Creates a new instance of the ClassBuilder class
5
5
  #
6
6
  # ==== Required Attributes
7
- # - klass_name: The name/type of the class to create
8
- # - methods: The accessor methods to add to the class
9
- # - nav_props: The accessor methods to add for navigation properties
10
- def initialize(klass_name, methods, nav_props)
7
+ # - klass_name: The name/type of the class to create
8
+ # - methods: The accessor methods to add to the class
9
+ # - nav_props: The accessor methods to add for navigation properties
10
+ # - context: The service context that this entity belongs to
11
+ # - namespaces: Optional namespace to create the classes in
12
+ def initialize(klass_name, methods, nav_props, context, namespace = nil)
11
13
  @klass_name = klass_name.camelcase
12
14
  @methods = methods
13
15
  @nav_props = nav_props
16
+ @context = context
17
+ @namespace = namespace
14
18
  end
15
-
19
+
16
20
  # Returns a dynamically generated class definition based on the constructor parameters
17
21
  def build
18
22
  # return if already built
19
23
  return @klass unless @klass.nil?
20
-
24
+
21
25
  # need the class name to build class
22
26
  return nil if @klass_name.nil?
23
-
27
+
24
28
  # return if we can find constant corresponding to class name
25
- if Object.const_defined? @klass_name
29
+ already_defined = eval("defined?(#{@klass_name}) == 'constant' and #{@klass_name}.class == Class")
30
+ if already_defined
26
31
  @klass = @klass_name.constantize
27
32
  return @klass
28
33
  end
29
34
 
30
- Object.const_set(@klass_name, Class.new.extend(ActiveSupport::JSON))
35
+ if @namespace
36
+ namespaces = @namespace.split(/\.|::/)
37
+
38
+ namespaces.each_with_index do |ns, index|
39
+ if index == 0
40
+ next if Object.const_defined? ns
41
+ Object.const_set(ns, Module.new)
42
+ else
43
+ current_ns = namespaces[0..index-1].join '::'
44
+ next if eval "#{current_ns}.const_defined? '#{ns}'"
45
+ eval "#{current_ns}.const_set('#{ns}', Module.new)"
46
+ end
47
+ end
48
+
49
+ klass_constant = @klass_name.split('::').last
50
+ eval "#{namespaces.join '::'}.const_set('#{klass_constant}', Class.new.extend(ActiveSupport::JSON))"
51
+ else
52
+ Object.const_set(@klass_name, Class.new.extend(ActiveSupport::JSON))
53
+ end
54
+
31
55
  @klass = @klass_name.constantize
32
56
  @klass.class_eval do
33
57
  include OData
34
58
  end
35
59
 
60
+ add_initializer(@klass)
36
61
  add_methods(@klass)
37
62
  add_nav_props(@klass)
38
-
63
+ add_class_methods(@klass)
64
+
39
65
  return @klass
40
66
  end
41
-
67
+
42
68
  private
69
+ def add_initializer(klass)
70
+ klass.send :define_method, :initialize do |*args|
71
+ return if args.blank?
72
+ props = args[0]
73
+ return unless props.is_a? Hash
74
+ props.each do |k,v|
75
+ raise NoMethodError, "undefined method `#{k}'" unless self.respond_to? k.to_sym
76
+ instance_variable_set("@#{k}", v)
77
+ end
78
+ end
79
+ end
80
+
43
81
  def add_methods(klass)
44
82
  # Add metadata methods
45
83
  klass.send :define_method, :__metadata do
46
84
  instance_variable_get("@__metadata")
47
85
  end
48
86
  klass.send :define_method, :__metadata= do |value|
49
- instance_variable_set("@__metadata", value)
87
+ instance_variable_set("@__metadata", value)
50
88
  end
51
89
  klass.send :define_method, :as_json do |*args|
52
- meta = RUBY_VERSION < "1.9" ? '@__metadata' :'@__metadata'.to_sym
90
+ meta = RUBY_VERSION < "1.9" ? '@__metadata' : ('@__metadata'.to_sym)
53
91
 
54
92
  options = args[0] || {}
55
93
  options[:type] ||= :unknown
56
94
 
57
95
  vars = self.instance_values
58
96
 
59
- # For adds, we need to get rid of all attributes except __metadata when passing
60
- # the object to the server
61
- if(options[:type] == :add)
97
+ if options[:type] == :add
98
+ # For adds, we need to get rid of all attributes except __metadata when passing
99
+ # the object to the server
62
100
  vars.each_value do |value|
63
101
  if value.is_a? OData
64
102
  child_vars = value.instance_variables
@@ -89,6 +127,12 @@ module OData
89
127
  vars[t[0]] = sdate
90
128
  end
91
129
 
130
+ if options[:type] == :link
131
+ # For links, delete all of the vars and just add a uri
132
+ uri = self.__metadata[:uri]
133
+ vars = { 'uri' => uri }
134
+ end
135
+
92
136
  vars
93
137
  end
94
138
 
@@ -101,6 +145,23 @@ module OData
101
145
  instance_variable_set("@#{method_name}", value)
102
146
  end
103
147
  end
148
+
149
+ # Add an id method pulling out the id from the uri (mainly for Pickle support)
150
+ klass.send :define_method, :id do
151
+ metadata = self.__metadata
152
+ id = nil
153
+ if metadata && metadata[:uri] =~ /\((\d+)\)$/
154
+ id = $~[1]
155
+ end
156
+ return (true if Integer(id) rescue false) ? id.to_i : id
157
+ end
158
+
159
+ # Override equals
160
+ klass.send :define_method, :== do |other|
161
+ self.class == other.class &&
162
+ self.id == other.id &&
163
+ self.__metadata == other.__metadata
164
+ end
104
165
  end
105
166
 
106
167
  def add_nav_props(klass)
@@ -113,5 +174,24 @@ module OData
113
174
  end
114
175
  end
115
176
  end
177
+
178
+ def add_class_methods(klass)
179
+ context = @context
180
+
181
+ # Retrieves a list of properties defined on a type (standard and navigation properties)
182
+ klass.send :define_singleton_method, 'properties' do
183
+ context.class_metadata[klass.to_s] || {}
184
+ end
185
+
186
+ # Finds a single model by ID
187
+ klass.send :define_singleton_method, 'first' do |id|
188
+ return nil if id.nil?
189
+ # TODO: Instead of just pluralizing the klass name, use the actual collection name
190
+ collection = klass.to_s.pluralize
191
+ context.send "#{collection}", id
192
+ results = context.execute
193
+ results.count == 0 ? nil : results.first
194
+ end
195
+ end
116
196
  end
117
197
  end # module OData