frodata 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +75 -0
  8. data/CHANGELOG.md +150 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +427 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/frodata.gemspec +34 -0
  15. data/lib/frodata.rb +36 -0
  16. data/lib/frodata/entity.rb +332 -0
  17. data/lib/frodata/entity_container.rb +75 -0
  18. data/lib/frodata/entity_set.rb +161 -0
  19. data/lib/frodata/errors.rb +68 -0
  20. data/lib/frodata/navigation_property.rb +29 -0
  21. data/lib/frodata/navigation_property/proxy.rb +80 -0
  22. data/lib/frodata/properties.rb +32 -0
  23. data/lib/frodata/properties/binary.rb +50 -0
  24. data/lib/frodata/properties/boolean.rb +37 -0
  25. data/lib/frodata/properties/collection.rb +50 -0
  26. data/lib/frodata/properties/complex.rb +114 -0
  27. data/lib/frodata/properties/date.rb +27 -0
  28. data/lib/frodata/properties/date_time.rb +83 -0
  29. data/lib/frodata/properties/date_time_offset.rb +17 -0
  30. data/lib/frodata/properties/decimal.rb +50 -0
  31. data/lib/frodata/properties/enum.rb +62 -0
  32. data/lib/frodata/properties/float.rb +67 -0
  33. data/lib/frodata/properties/geography.rb +13 -0
  34. data/lib/frodata/properties/geography/base.rb +162 -0
  35. data/lib/frodata/properties/geography/line_string.rb +33 -0
  36. data/lib/frodata/properties/geography/point.rb +31 -0
  37. data/lib/frodata/properties/geography/polygon.rb +38 -0
  38. data/lib/frodata/properties/guid.rb +17 -0
  39. data/lib/frodata/properties/integer.rb +107 -0
  40. data/lib/frodata/properties/number.rb +14 -0
  41. data/lib/frodata/properties/string.rb +72 -0
  42. data/lib/frodata/properties/time.rb +40 -0
  43. data/lib/frodata/properties/time_of_day.rb +27 -0
  44. data/lib/frodata/property.rb +139 -0
  45. data/lib/frodata/property_registry.rb +41 -0
  46. data/lib/frodata/query.rb +233 -0
  47. data/lib/frodata/query/criteria.rb +92 -0
  48. data/lib/frodata/query/criteria/comparison_operators.rb +49 -0
  49. data/lib/frodata/query/criteria/date_functions.rb +61 -0
  50. data/lib/frodata/query/criteria/geography_functions.rb +21 -0
  51. data/lib/frodata/query/criteria/lambda_operators.rb +27 -0
  52. data/lib/frodata/query/criteria/string_functions.rb +40 -0
  53. data/lib/frodata/query/in_batches.rb +58 -0
  54. data/lib/frodata/railtie.rb +19 -0
  55. data/lib/frodata/schema.rb +155 -0
  56. data/lib/frodata/schema/complex_type.rb +79 -0
  57. data/lib/frodata/schema/enum_type.rb +95 -0
  58. data/lib/frodata/service.rb +254 -0
  59. data/lib/frodata/service/request.rb +85 -0
  60. data/lib/frodata/service/response.rb +162 -0
  61. data/lib/frodata/service/response/atom.rb +40 -0
  62. data/lib/frodata/service/response/json.rb +41 -0
  63. data/lib/frodata/service/response/plain.rb +36 -0
  64. data/lib/frodata/service/response/xml.rb +40 -0
  65. data/lib/frodata/service_registry.rb +52 -0
  66. data/lib/frodata/version.rb +3 -0
  67. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  68. data/spec/fixtures/files/error.xml +5 -0
  69. data/spec/fixtures/files/metadata.xml +150 -0
  70. data/spec/fixtures/files/product_0.json +10 -0
  71. data/spec/fixtures/files/product_0.xml +28 -0
  72. data/spec/fixtures/files/products.json +106 -0
  73. data/spec/fixtures/files/products.xml +308 -0
  74. data/spec/fixtures/files/supplier_0.json +26 -0
  75. data/spec/fixtures/files/supplier_0.xml +32 -0
  76. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1635 -0
  77. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  78. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  79. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  80. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  81. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  82. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  83. data/spec/fixtures/vcr_cassettes/query_specs.yml +1060 -0
  84. data/spec/fixtures/vcr_cassettes/schema/complex_type_specs.yml +127 -0
  85. data/spec/fixtures/vcr_cassettes/service/request_specs.yml +193 -0
  86. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  87. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  88. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +1330 -0
  89. data/spec/frodata/entity/shared_examples.rb +82 -0
  90. data/spec/frodata/entity_container_spec.rb +38 -0
  91. data/spec/frodata/entity_set_spec.rb +168 -0
  92. data/spec/frodata/entity_spec.rb +151 -0
  93. data/spec/frodata/errors_spec.rb +48 -0
  94. data/spec/frodata/navigation_property/proxy_spec.rb +44 -0
  95. data/spec/frodata/navigation_property_spec.rb +55 -0
  96. data/spec/frodata/properties/binary_spec.rb +50 -0
  97. data/spec/frodata/properties/boolean_spec.rb +72 -0
  98. data/spec/frodata/properties/collection_spec.rb +44 -0
  99. data/spec/frodata/properties/date_spec.rb +23 -0
  100. data/spec/frodata/properties/date_time_offset_spec.rb +30 -0
  101. data/spec/frodata/properties/date_time_spec.rb +23 -0
  102. data/spec/frodata/properties/decimal_spec.rb +51 -0
  103. data/spec/frodata/properties/float_spec.rb +45 -0
  104. data/spec/frodata/properties/geography/line_string_spec.rb +33 -0
  105. data/spec/frodata/properties/geography/point_spec.rb +29 -0
  106. data/spec/frodata/properties/geography/polygon_spec.rb +55 -0
  107. data/spec/frodata/properties/geography/shared_examples.rb +72 -0
  108. data/spec/frodata/properties/guid_spec.rb +17 -0
  109. data/spec/frodata/properties/integer_spec.rb +58 -0
  110. data/spec/frodata/properties/string_spec.rb +46 -0
  111. data/spec/frodata/properties/time_of_day_spec.rb +23 -0
  112. data/spec/frodata/properties/time_spec.rb +15 -0
  113. data/spec/frodata/property_registry_spec.rb +16 -0
  114. data/spec/frodata/property_spec.rb +71 -0
  115. data/spec/frodata/query/criteria_spec.rb +229 -0
  116. data/spec/frodata/query_spec.rb +199 -0
  117. data/spec/frodata/schema/complex_type_spec.rb +96 -0
  118. data/spec/frodata/schema/enum_type_spec.rb +112 -0
  119. data/spec/frodata/schema_spec.rb +97 -0
  120. data/spec/frodata/service/request_spec.rb +49 -0
  121. data/spec/frodata/service/response_spec.rb +85 -0
  122. data/spec/frodata/service_registry_spec.rb +18 -0
  123. data/spec/frodata/service_spec.rb +191 -0
  124. data/spec/frodata/usage_example_spec.rb +188 -0
  125. data/spec/spec_helper.rb +32 -0
  126. data/spec/support/coverage.rb +2 -0
  127. data/spec/support/vcr.rb +9 -0
  128. metadata +401 -0
@@ -0,0 +1,229 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples 'an operator criterium' do |operator, value, to_s|
4
+ it { expect(criteria).to eq(subject) }
5
+ it { expect(criteria.operator).to eq(operator) }
6
+ it { expect(criteria.value).to eq(value) }
7
+ it { expect(criteria.to_s).to eq(to_s) }
8
+ end
9
+
10
+ shared_examples 'a function criterium' do |function, argument, to_s|
11
+ it { expect(criteria).to eq(subject) }
12
+ it { expect(criteria.function).to eq(function) }
13
+ it { expect(criteria.argument).to eq(argument) }
14
+ it { expect(criteria.to_s).to eq(to_s) }
15
+ end
16
+
17
+ shared_examples 'an operator-function criterium' do |function, operator, value, to_s|
18
+ it { expect(criteria).to eq(subject) }
19
+ it { expect(criteria.function).to eq(function) }
20
+ it { expect(criteria.operator).to eq(operator) }
21
+ it { expect(criteria.value).to eq(value) }
22
+ it { expect(criteria.to_s).to eq(to_s) }
23
+ end
24
+
25
+ describe FrOData::Query::Criteria do
26
+ let(:string_property) { FrOData::Properties::String.new(:Name, nil) }
27
+ let(:integer_property) { FrOData::Properties::Integer.new(:Age, nil) }
28
+ let(:datetime_property) { FrOData::Properties::DateTime.new(:BirthDate, nil) }
29
+ let(:geo_property) { FrOData::Properties::Geography::Point.new(:Position, nil) }
30
+ # TODO: use actual `NavigationProperty` class here
31
+ let(:navigation_property) { FrOData::Property.new(:Items, nil) }
32
+
33
+ let(:subject) { FrOData::Query::Criteria.new(property: string_property) }
34
+
35
+ it { expect(subject).to respond_to(:property) }
36
+ it { expect(subject.property).to eq(string_property)}
37
+
38
+ it { expect(subject).to respond_to(:operator) }
39
+ it { expect(subject).to respond_to(:value) }
40
+ it { expect(subject).to respond_to(:to_s) }
41
+
42
+ describe 'comparison operators' do
43
+ describe '#eq' do
44
+ let(:criteria) { subject.eq('Bread') }
45
+
46
+ it { expect(subject).to respond_to(:eq) }
47
+ it_behaves_like 'an operator criterium', :eq, 'Bread', "Name eq 'Bread'"
48
+ end
49
+
50
+ describe '#ne' do
51
+ let(:criteria) { subject.ne('Bread') }
52
+
53
+ it { expect(subject).to respond_to(:ne) }
54
+ it_behaves_like 'an operator criterium', :ne, 'Bread', "Name ne 'Bread'"
55
+ end
56
+
57
+ describe '#gt' do
58
+ let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
59
+ let(:criteria) { subject.gt(5) }
60
+
61
+ it { expect(subject).to respond_to(:gt) }
62
+ it_behaves_like 'an operator criterium', :gt, 5, "Age gt 5"
63
+ end
64
+
65
+ describe '#ge' do
66
+ let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
67
+ let(:criteria) { subject.ge(5) }
68
+
69
+ it { expect(subject).to respond_to(:ge) }
70
+ it_behaves_like 'an operator criterium', :ge, 5, 'Age ge 5'
71
+ end
72
+
73
+ describe '#lt' do
74
+ let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
75
+ let(:criteria) { subject.lt(5) }
76
+
77
+ it { expect(subject).to respond_to(:lt) }
78
+ it_behaves_like 'an operator criterium', :lt, 5, 'Age lt 5'
79
+ end
80
+
81
+ describe '#le' do
82
+ let(:subject) { FrOData::Query::Criteria.new(property: integer_property) }
83
+ let(:criteria) { subject.le(5) }
84
+
85
+ it { expect(subject).to respond_to(:le) }
86
+ it_behaves_like 'an operator criterium', :le, 5, 'Age le 5'
87
+ end
88
+ end
89
+
90
+ describe 'string functions' do
91
+ describe '#contains' do
92
+ let(:criteria) { subject.contains('freds') }
93
+
94
+ it { expect(subject).to respond_to(:contains) }
95
+ it_behaves_like 'a function criterium', :contains, 'freds', "contains(Name,'freds')"
96
+ end
97
+
98
+ describe '#startswith' do
99
+ let(:criteria) { subject.startswith('Alfreds') }
100
+
101
+ it { expect(subject).to respond_to(:startswith) }
102
+ it_behaves_like 'a function criterium', :startswith, 'Alfreds', "startswith(Name,'Alfreds')"
103
+ end
104
+
105
+ describe '#endswith' do
106
+ let(:criteria) { subject.endswith('Futterkiste') }
107
+
108
+ it { expect(subject).to respond_to(:endswith) }
109
+ it_behaves_like 'a function criterium', :endswith, 'Futterkiste', "endswith(Name,'Futterkiste')"
110
+ end
111
+
112
+ describe '#tolower' do
113
+ let(:criteria) { subject.tolower.eq('alfreds futterkiste') }
114
+
115
+ it { expect(subject).to respond_to(:tolower) }
116
+ it_behaves_like 'an operator-function criterium', :tolower, :eq, 'alfreds futterkiste', "tolower(Name) eq 'alfreds futterkiste'"
117
+ end
118
+
119
+ describe '#toupper' do
120
+ let(:criteria) { subject.toupper.eq('ALFREDS FUTTERKISTE') }
121
+
122
+ it { expect(subject).to respond_to(:toupper) }
123
+ it_behaves_like 'an operator-function criterium', :toupper, :eq, 'ALFREDS FUTTERKISTE', "toupper(Name) eq 'ALFREDS FUTTERKISTE'"
124
+ end
125
+ end
126
+
127
+ describe 'date functions' do
128
+ let(:subject) { FrOData::Query::Criteria.new(property: datetime_property) }
129
+
130
+ describe '#year' do
131
+ let(:criteria) { subject.year.eq(1981) }
132
+
133
+ it { expect(subject).to respond_to(:year) }
134
+ it_behaves_like 'an operator-function criterium', :year, :eq, 1981, 'year(BirthDate) eq 1981'
135
+ end
136
+
137
+ describe '#month' do
138
+ let(:criteria) { subject.month.eq(8) }
139
+
140
+ it { expect(subject).to respond_to(:month) }
141
+ it_behaves_like 'an operator-function criterium', :month, :eq, 8, 'month(BirthDate) eq 8'
142
+ end
143
+
144
+ describe '#day' do
145
+ let(:criteria) { subject.day.eq(27) }
146
+
147
+ it { expect(subject).to respond_to(:day) }
148
+ it_behaves_like 'an operator-function criterium', :day, :eq, 27, 'day(BirthDate) eq 27'
149
+ end
150
+
151
+ describe '#hour' do
152
+ let(:criteria) { subject.hour.eq(9) }
153
+
154
+ it { expect(subject).to respond_to(:hour) }
155
+ it_behaves_like 'an operator-function criterium', :hour, :eq, 9, 'hour(BirthDate) eq 9'
156
+ end
157
+
158
+ describe '#minute' do
159
+ let(:criteria) { subject.minute.eq(35) }
160
+
161
+ it { expect(subject).to respond_to(:minute) }
162
+ it_behaves_like 'an operator-function criterium', :minute, :eq, 35, 'minute(BirthDate) eq 35'
163
+ end
164
+
165
+ describe '#second' do
166
+ let(:criteria) { subject.second.eq(11) }
167
+
168
+ it { expect(subject).to respond_to(:second) }
169
+ it_behaves_like 'an operator-function criterium', :second, :eq, 11, 'second(BirthDate) eq 11'
170
+ end
171
+
172
+ describe '#fractionalseconds' do
173
+ let(:criteria) { subject.fractionalseconds.eq(0) }
174
+
175
+ it { expect(subject).to respond_to(:fractionalseconds) }
176
+ it_behaves_like 'an operator-function criterium', :fractionalseconds, :eq, 0, 'fractionalseconds(BirthDate) eq 0'
177
+ end
178
+
179
+ describe '#date' do
180
+ let(:criteria) { subject.date.ne('date(EndTime)') }
181
+
182
+ it { expect(subject).to respond_to(:date) }
183
+ it_behaves_like 'an operator-function criterium', :date, :ne, 'date(EndTime)', 'date(BirthDate) ne date(EndTime)'
184
+ end
185
+
186
+ describe '#time' do
187
+ let(:criteria) { subject.time.le('StartOfDay') }
188
+
189
+ it { expect(subject).to respond_to(:time) }
190
+ it_behaves_like 'an operator-function criterium', :time, :le, 'StartOfDay', 'time(BirthDate) le StartOfDay'
191
+ end
192
+ end
193
+
194
+ describe 'geospatial functions' do
195
+ let(:subject) { FrOData::Query::Criteria.new(property: geo_property) }
196
+
197
+ describe '#distance' do
198
+ let(:criteria) { subject.distance('TargetPosition').le(42) }
199
+
200
+ it { expect(subject).to respond_to(:distance) }
201
+ it_behaves_like 'an operator-function criterium', :'geo.distance', :le, 42, 'geo.distance(Position,TargetPosition) le 42'
202
+ end
203
+
204
+ describe '#intersects' do
205
+ let(:criteria) { subject.intersects('TargetArea') }
206
+
207
+ it { expect(subject).to respond_to(:intersects) }
208
+ it_behaves_like 'a function criterium', :'geo.intersects', 'TargetArea', 'geo.intersects(Position,TargetArea)'
209
+ end
210
+ end
211
+
212
+ describe 'lambda operators' do
213
+ let(:subject) { FrOData::Query::Criteria.new(property: navigation_property) }
214
+
215
+ describe '#any' do
216
+ let(:criteria) { subject.any('Quantity').gt(100) }
217
+
218
+ it { expect(subject).to respond_to(:any) }
219
+ it_behaves_like 'an operator-function criterium', :any, :gt, 100, 'Items/any(d:d/Quantity gt 100)'
220
+ end
221
+
222
+ describe '#all' do
223
+ let(:criteria) { subject.all('Quantity').gt(100) }
224
+
225
+ it { expect(subject).to respond_to(:all) }
226
+ it_behaves_like 'an operator-function criterium', :all, :gt, 100, 'Items/all(d:d/Quantity gt 100)'
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+
3
+ describe FrOData::Query, vcr: {cassette_name: 'query_specs'} do
4
+ before(:example) do
5
+ FrOData::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
6
+ end
7
+
8
+ let(:subject) { FrOData::Query.new(entity_set) }
9
+ let(:entity_set) { FrOData::EntitySet.new(options) }
10
+ let(:options) { {
11
+ service_name: 'ODataDemo',
12
+ container: 'DemoService',
13
+ namespace: 'ODataDemo',
14
+ name: 'Products',
15
+ type: 'ODataDemo.Product'
16
+ } }
17
+
18
+ it { expect(subject).to respond_to(:to_s) }
19
+ it { expect(subject.to_s).to eq('Products')}
20
+
21
+ it { expect(subject).to respond_to(:[]) }
22
+ describe '#[]' do
23
+ it { expect(subject[:Name]).to be_a(FrOData::Query::Criteria) }
24
+ it { expect(subject[:Name].property).to be_a(FrOData::Property) }
25
+ end
26
+
27
+ it { expect(subject).to respond_to(:find) }
28
+ describe '#find' do
29
+ let(:product) { subject.find(0) }
30
+
31
+ it 'finds an entity by its ID' do
32
+ expect(product).to be_a(FrOData::Entity)
33
+ expect(product['ID']).to eq(0)
34
+ end
35
+
36
+ it 'allows expanding navigational properties' do
37
+ product_with_categories = subject.expand('Categories').find(0)
38
+ expect(product_with_categories['Categories']).to eq([
39
+ { "ID" => 0, "Name" => "Food" }
40
+ ])
41
+ end
42
+ end
43
+
44
+ it { expect(subject).to respond_to(:where) }
45
+ describe '#where' do
46
+ let(:criteria) { subject[:Name].eq('Bread') }
47
+ let(:query_string) { "Products?$filter=Name eq 'Bread'" }
48
+
49
+ it { expect(subject.where(criteria)).to eq(subject) }
50
+ it { expect(subject.where(criteria).to_s).to eq(query_string) }
51
+ end
52
+
53
+ it { expect(subject).to respond_to(:search) }
54
+ describe '#search' do
55
+ let(:term) { '"mountain bike"' }
56
+ let(:query_string) { 'Products?$search="mountain bike"' }
57
+
58
+ it { expect(subject.search(term)).to eq(subject) }
59
+ it { expect(subject.search(term).to_s).to eq(query_string) }
60
+
61
+ describe 'with multiple terms' do
62
+ let(:query_string) { 'Products?$search="mountain bike" AND NOT clothing' }
63
+
64
+ it { expect(subject.search(term).search('NOT clothing').to_s).to eq(query_string) }
65
+ end
66
+ end
67
+
68
+ #it { expect(subject).to respond_to(:and) }
69
+ describe '#and' do
70
+ it { pending; fail }
71
+ end
72
+
73
+ #it { expect(subject).to respond_to(:or) }
74
+ describe '#or' do
75
+ it { pending; fail }
76
+ end
77
+
78
+ it { expect(subject).to respond_to(:skip) }
79
+ describe '#skip' do
80
+ it { expect(subject.skip(5)).to eq(subject) }
81
+ it 'properly formats query with skip specified' do
82
+ subject.skip(5)
83
+ expect(subject.to_s).to eq('Products?$skip=5')
84
+ end
85
+ end
86
+
87
+ it { expect(subject).to respond_to(:limit) }
88
+ describe '#limit' do
89
+
90
+ it { expect(subject.limit(5)).to eq(subject) }
91
+ it 'properly formats query with limit specified' do
92
+ subject.limit(5)
93
+ expect(subject.to_s).to eq('Products?$top=5')
94
+ end
95
+ end
96
+
97
+ it { expect(subject).to respond_to(:include_count) }
98
+ describe '#include_count' do
99
+ it { expect(subject.include_count).to eq(subject) }
100
+ it 'properly formats query with include_count specified' do
101
+ subject.include_count
102
+ expect(subject.to_s).to eq('Products?$count=true')
103
+ end
104
+ end
105
+
106
+ it { expect(subject).to respond_to(:select) }
107
+ describe '#select' do
108
+ it { expect(subject.select(:Name, :Price)).to eq(subject) }
109
+ it 'properly formats query with select operation specified' do
110
+ subject.select(:Name, :Price)
111
+ expect(subject.to_s).to eq('Products?$select=Name,Price')
112
+ end
113
+ end
114
+
115
+ it { expect(subject).to respond_to(:expand) }
116
+ describe '#expand' do
117
+ it { expect(subject.expand(:Supplier)).to eq(subject) }
118
+ it 'properly formats query with expand operation specified' do
119
+ subject.expand(:Supplier)
120
+ expect(subject.to_s).to eq('Products?$expand=Supplier')
121
+ end
122
+ end
123
+
124
+ it { expect(subject).to respond_to(:order_by) }
125
+ describe '#order_by' do
126
+ it { expect(subject.order_by(:Name, :Price)).to eq(subject) }
127
+ it 'properly formats query with orderby operation specified' do
128
+ subject.order_by(:Name, :Price)
129
+ expect(subject.to_s).to eq('Products?$orderby=Name,Price')
130
+ end
131
+ end
132
+
133
+ describe '#execute' do
134
+ it { expect(subject).to respond_to(:execute) }
135
+ it { expect(subject.execute).to be_a(FrOData::Service::Response) }
136
+ end
137
+
138
+ describe '#count' do
139
+ it { expect(subject).to respond_to(:count) }
140
+ it { expect(subject.count).to be_a(Integer) }
141
+ it { expect(subject.count).to eq(11) }
142
+
143
+ context 'with filters' do
144
+ let(:criteria) { subject[:Name].eq('Bread') }
145
+
146
+ it { expect(subject.where(criteria).count).to eq(1) }
147
+ end
148
+ end
149
+
150
+ describe '#empty?' do
151
+ it { expect(subject).to respond_to(:empty?) }
152
+ it { expect(subject.empty?).to eq(false) }
153
+
154
+ context 'with filters' do
155
+ let(:non_empty_criteria) { subject[:Name].eq('Bread') }
156
+ let(:empty_criteria) { subject[:Name].eq('NonExistent') }
157
+
158
+ it { expect(subject.where(non_empty_criteria).empty?).to eq(false) }
159
+ it { expect(subject.where(empty_criteria).empty?).to eq(true) }
160
+ end
161
+ end
162
+
163
+ describe '#in_batches' do
164
+ it { expect(subject).to respond_to(:in_batches) }
165
+ it 'returns FrOData::Entities in batches of specified size' do
166
+ batch_count = entity_count = 0
167
+
168
+ subject.in_batches(of: 5) do |batch|
169
+ expect(batch).to be_a(FrOData::Service::Response)
170
+ expect(batch.count).to eq(5) unless batch_count == 2
171
+
172
+ batch.each do |entity|
173
+ expect(entity).to be_a(FrOData::Entity)
174
+ expect(entity.type).to eq('ODataDemo.Product')
175
+ entity_count += 1
176
+ end
177
+
178
+ batch_count += 1
179
+ end
180
+
181
+ expect(batch_count).to eq(3)
182
+ expect(entity_count).to eq(11)
183
+ end
184
+
185
+ describe '#each' do
186
+ it 'returns FrOData::Entities' do
187
+ entity_count = 0
188
+
189
+ subject.in_batches(of: 5).each do |entity|
190
+ expect(entity).to be_a(FrOData::Entity)
191
+ expect(entity.type).to eq('ODataDemo.Product')
192
+ entity_count += 1
193
+ end
194
+
195
+ expect(entity_count).to eq(11)
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe FrOData::Schema::ComplexType, vcr: {cassette_name: 'schema/complex_type_specs'} do
4
+ before(:example) do
5
+ FrOData::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
6
+ end
7
+
8
+ let(:service) { FrOData::ServiceRegistry['ODataDemo'] }
9
+
10
+ let(:address) { {
11
+ 'Street' => '123 Main St',
12
+ 'City' => 'Huntington Beach',
13
+ 'State' => 'CA',
14
+ 'ZipCode' => '92648',
15
+ 'Country' => 'USA'
16
+ } }
17
+
18
+ let(:complex_type) { service.complex_types['ODataDemo.Address'] }
19
+ let(:subject) { complex_type.property_class.new('Address', nil) }
20
+
21
+ describe 'is properly parsed from service metadata' do
22
+ it { expect(complex_type.name).to eq('Address') }
23
+ it { expect(complex_type.namespace).to eq('ODataDemo') }
24
+ it { expect(complex_type.type).to eq('ODataDemo.Address') }
25
+ it { expect(complex_type.property_names).to eq(%w{Street City State ZipCode Country}) }
26
+ end
27
+
28
+ # Check property instance inheritance hierarchy
29
+ it { expect(subject).to be_a(FrOData::Property) }
30
+ it { expect(subject).to be_a(FrOData::Properties::Complex) }
31
+
32
+ it { expect(subject).to respond_to(:name) }
33
+ it { expect(subject).to respond_to(:type) }
34
+ it { expect(subject).to respond_to(:property_names) }
35
+ it { expect(subject).to respond_to(:[]) }
36
+ it { expect(subject).to respond_to(:[]=) }
37
+
38
+ it { expect(subject[ 'Street']).to be_nil }
39
+ it { expect(subject[ 'City']).to be_nil }
40
+ it { expect(subject[ 'State']).to be_nil }
41
+ it { expect(subject['ZipCode']).to be_nil }
42
+ it { expect(subject['Country']).to be_nil }
43
+
44
+ describe '#[]=' do
45
+ before do
46
+ address.each { |key, val| subject[key] = val }
47
+ end
48
+
49
+ it { expect(subject.value).to eq(address) }
50
+
51
+ it { expect(subject[ 'Street']).to eq(address[ 'Street']) }
52
+ it { expect(subject[ 'City']).to eq(address[ 'City']) }
53
+ it { expect(subject[ 'State']).to eq(address[ 'State']) }
54
+ it { expect(subject['ZipCode']).to eq(address['ZipCode']) }
55
+ it { expect(subject['Country']).to eq(address['Country']) }
56
+ end
57
+
58
+ describe '#value=' do
59
+ before { subject.value = address }
60
+
61
+ it { expect(subject.value).to eq(address) }
62
+
63
+ it { expect(subject[ 'Street']).to eq(address[ 'Street']) }
64
+ it { expect(subject[ 'City']).to eq(address[ 'City']) }
65
+ it { expect(subject[ 'State']).to eq(address[ 'State']) }
66
+ it { expect(subject['ZipCode']).to eq(address['ZipCode']) }
67
+ it { expect(subject['Country']).to eq(address['Country']) }
68
+ end
69
+
70
+ describe '#to_xml' do
71
+ let(:builder) do
72
+ Nokogiri::XML::Builder.new do |xml|
73
+ xml.entry(
74
+ 'xmlns' => 'http://www.w3.org/2005/Atom',
75
+ 'xmlns:data' => 'http://docs.oasis-open.org/odata/ns/data',
76
+ 'xmlns:metadata' => 'http://docs.oasis-open.org/odata/ns/metadata',
77
+ ) do
78
+ subject.to_xml(xml)
79
+ end
80
+ end
81
+ end
82
+ let(:xml) { Nokogiri::XML(builder.to_xml) }
83
+
84
+ before(:each) do
85
+ subject.value = address
86
+ xml.remove_namespaces!
87
+ end
88
+
89
+ it { expect(xml.xpath("/entry/Address[@type='ODataDemo.Address']").count).to eq(1) }
90
+ it { expect(xml.xpath('/entry/Address/Street').count).to eq(1) }
91
+ it { expect(xml.xpath('/entry/Address/City').count).to eq(1) }
92
+ it { expect(xml.xpath('/entry/Address/State').count).to eq(1) }
93
+ it { expect(xml.xpath('/entry/Address/ZipCode').count).to eq(1) }
94
+ it { expect(xml.xpath('/entry/Address/Country').count).to eq(1) }
95
+ end
96
+ end