odata4 0.7.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 (114) 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 +120 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +287 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/lib/odata4.rb +37 -0
  15. data/lib/odata4/complex_type.rb +76 -0
  16. data/lib/odata4/complex_type/property.rb +114 -0
  17. data/lib/odata4/entity.rb +319 -0
  18. data/lib/odata4/entity_set.rb +162 -0
  19. data/lib/odata4/enum_type.rb +95 -0
  20. data/lib/odata4/enum_type/property.rb +62 -0
  21. data/lib/odata4/navigation_property.rb +29 -0
  22. data/lib/odata4/navigation_property/proxy.rb +76 -0
  23. data/lib/odata4/properties.rb +25 -0
  24. data/lib/odata4/properties/binary.rb +50 -0
  25. data/lib/odata4/properties/boolean.rb +37 -0
  26. data/lib/odata4/properties/date.rb +27 -0
  27. data/lib/odata4/properties/date_time.rb +83 -0
  28. data/lib/odata4/properties/date_time_offset.rb +17 -0
  29. data/lib/odata4/properties/decimal.rb +50 -0
  30. data/lib/odata4/properties/float.rb +67 -0
  31. data/lib/odata4/properties/geography.rb +13 -0
  32. data/lib/odata4/properties/geography/base.rb +162 -0
  33. data/lib/odata4/properties/geography/line_string.rb +33 -0
  34. data/lib/odata4/properties/geography/point.rb +31 -0
  35. data/lib/odata4/properties/geography/polygon.rb +38 -0
  36. data/lib/odata4/properties/guid.rb +17 -0
  37. data/lib/odata4/properties/integer.rb +107 -0
  38. data/lib/odata4/properties/number.rb +14 -0
  39. data/lib/odata4/properties/string.rb +72 -0
  40. data/lib/odata4/properties/time.rb +40 -0
  41. data/lib/odata4/properties/time_of_day.rb +27 -0
  42. data/lib/odata4/property.rb +118 -0
  43. data/lib/odata4/property_registry.rb +41 -0
  44. data/lib/odata4/query.rb +231 -0
  45. data/lib/odata4/query/criteria.rb +92 -0
  46. data/lib/odata4/query/criteria/comparison_operators.rb +49 -0
  47. data/lib/odata4/query/criteria/date_functions.rb +61 -0
  48. data/lib/odata4/query/criteria/geography_functions.rb +21 -0
  49. data/lib/odata4/query/criteria/lambda_operators.rb +27 -0
  50. data/lib/odata4/query/criteria/string_functions.rb +40 -0
  51. data/lib/odata4/query/in_batches.rb +58 -0
  52. data/lib/odata4/query/result.rb +84 -0
  53. data/lib/odata4/query/result/atom.rb +41 -0
  54. data/lib/odata4/query/result/json.rb +42 -0
  55. data/lib/odata4/railtie.rb +19 -0
  56. data/lib/odata4/service.rb +344 -0
  57. data/lib/odata4/service_registry.rb +52 -0
  58. data/lib/odata4/version.rb +3 -0
  59. data/odata4.gemspec +34 -0
  60. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  61. data/spec/fixtures/files/metadata.xml +150 -0
  62. data/spec/fixtures/files/product_0.json +10 -0
  63. data/spec/fixtures/files/product_0.xml +28 -0
  64. data/spec/fixtures/files/products.json +106 -0
  65. data/spec/fixtures/files/products.xml +308 -0
  66. data/spec/fixtures/files/supplier_0.json +26 -0
  67. data/spec/fixtures/files/supplier_0.xml +32 -0
  68. data/spec/fixtures/vcr_cassettes/complex_type_specs.yml +127 -0
  69. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1348 -0
  70. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  71. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  72. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  73. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  74. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  75. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  76. data/spec/fixtures/vcr_cassettes/query_specs.yml +663 -0
  77. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  78. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  79. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +749 -0
  80. data/spec/odata4/complex_type_spec.rb +116 -0
  81. data/spec/odata4/entity/shared_examples.rb +82 -0
  82. data/spec/odata4/entity_set_spec.rb +168 -0
  83. data/spec/odata4/entity_spec.rb +151 -0
  84. data/spec/odata4/enum_type_spec.rb +134 -0
  85. data/spec/odata4/navigation_property/proxy_spec.rb +44 -0
  86. data/spec/odata4/navigation_property_spec.rb +55 -0
  87. data/spec/odata4/properties/binary_spec.rb +50 -0
  88. data/spec/odata4/properties/boolean_spec.rb +72 -0
  89. data/spec/odata4/properties/date_spec.rb +23 -0
  90. data/spec/odata4/properties/date_time_offset_spec.rb +30 -0
  91. data/spec/odata4/properties/date_time_spec.rb +23 -0
  92. data/spec/odata4/properties/decimal_spec.rb +24 -0
  93. data/spec/odata4/properties/float_spec.rb +45 -0
  94. data/spec/odata4/properties/geography/line_string_spec.rb +33 -0
  95. data/spec/odata4/properties/geography/point_spec.rb +29 -0
  96. data/spec/odata4/properties/geography/polygon_spec.rb +55 -0
  97. data/spec/odata4/properties/geography/shared_examples.rb +72 -0
  98. data/spec/odata4/properties/guid_spec.rb +17 -0
  99. data/spec/odata4/properties/integer_spec.rb +58 -0
  100. data/spec/odata4/properties/string_spec.rb +46 -0
  101. data/spec/odata4/properties/time_of_day_spec.rb +23 -0
  102. data/spec/odata4/properties/time_spec.rb +15 -0
  103. data/spec/odata4/property_registry_spec.rb +16 -0
  104. data/spec/odata4/property_spec.rb +32 -0
  105. data/spec/odata4/query/criteria_spec.rb +229 -0
  106. data/spec/odata4/query/result_spec.rb +53 -0
  107. data/spec/odata4/query_spec.rb +196 -0
  108. data/spec/odata4/service_registry_spec.rb +18 -0
  109. data/spec/odata4/service_spec.rb +80 -0
  110. data/spec/odata4/usage_example_spec.rb +176 -0
  111. data/spec/spec_helper.rb +32 -0
  112. data/spec/support/coverage.rb +2 -0
  113. data/spec/support/vcr.rb +9 -0
  114. metadata +380 -0
@@ -0,0 +1,33 @@
1
+ module OData4
2
+ module Properties
3
+ module Geography
4
+ class LineString < Base
5
+ def type
6
+ 'Edm.GeographyLineString'
7
+ end
8
+
9
+ def coords_to_s
10
+ value.map { |pos| pos.join(' ') }.join(',')
11
+ end
12
+
13
+ def coords_from_s(str)
14
+ str.split(',').map { |pos| pos.split(' ').map(&:to_f) }
15
+ end
16
+
17
+ def xml_value
18
+ value.map do |coords|
19
+ { pos: coords.join(' ') }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def self.parse_xml(property_xml)
26
+ property_xml.xpath('//pos').map do |el|
27
+ el.content.split(' ').map(&:to_f)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ module OData4
2
+ module Properties
3
+ module Geography
4
+ class Point < Base
5
+ def type
6
+ 'Edm.GeographyPoint'
7
+ end
8
+
9
+ def coords_to_s
10
+ value.join(' ')
11
+ end
12
+
13
+ def coords_from_s(str)
14
+ str.split(' ').map(&:to_f)
15
+ end
16
+
17
+ def xml_value
18
+ { pos: coords_to_s }
19
+ end
20
+
21
+ private
22
+
23
+ def self.parse_xml(property_xml)
24
+ property_xml.xpath('//pos').map do |el|
25
+ el.content.split(' ').map(&:to_f)
26
+ end.flatten
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ module OData4
2
+ module Properties
3
+ module Geography
4
+ class Polygon < Base
5
+ def type
6
+ 'Edm.GeographyPolygon'
7
+ end
8
+
9
+ def coords_to_s
10
+ '(' + value.map { |pos| pos.join(' ') }.join(',') + ')'
11
+ end
12
+
13
+ def coords_from_s(str)
14
+ str.gsub(/[()]/, '')
15
+ .split(',').map { |pos| pos.split(' ').map(&:to_f) }
16
+ end
17
+
18
+ def xml_value
19
+ {
20
+ exterior: {
21
+ LinearRing: value.map do |coords|
22
+ { pos: coords.join(' ') }
23
+ end
24
+ }
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ def self.parse_xml(property_xml)
31
+ property_xml.xpath('//pos').map do |el|
32
+ el.content.split(' ').map(&:to_f)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module OData4
2
+ module Properties
3
+ # Defines the GUID OData4 type.
4
+ class Guid < OData4::Property
5
+ # The OData4 type name
6
+ def type
7
+ 'Edm.Guid'
8
+ end
9
+
10
+ # Value to be used in URLs.
11
+ # @return [String]
12
+ def url_value
13
+ "guid'#{value}'"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,107 @@
1
+ module OData4
2
+ module Properties
3
+ # Defines the Integer OData4 type.
4
+ class Integer < OData4::Property
5
+ include OData4::Properties::Number
6
+
7
+ # Returns the property value, properly typecast
8
+ # @return [Integer,nil]
9
+ def value
10
+ if (@value.nil? || @value.empty?) && allows_nil?
11
+ nil
12
+ else
13
+ @value.to_i
14
+ end
15
+ end
16
+
17
+ # Sets the property value
18
+ # @params new_value [to_i]
19
+ def value=(new_value)
20
+ validate(new_value.to_i)
21
+ @value = new_value.to_i.to_s
22
+ end
23
+
24
+ # The OData4 type name
25
+ def type
26
+ 'Edm.Int64'
27
+ end
28
+
29
+ private
30
+
31
+ def exponent_size
32
+ 63
33
+ end
34
+
35
+ def min_value
36
+ @min ||= -(2**exponent_size)
37
+ end
38
+
39
+ def max_value
40
+ @max ||= (2**exponent_size)-1
41
+ end
42
+ end
43
+
44
+ # Defines the Integer (16 bit) OData4 type.
45
+ class Int16 < Integer
46
+ # The OData4 type name
47
+ def type
48
+ 'Edm.Int16'
49
+ end
50
+
51
+ private
52
+
53
+ def exponent_size
54
+ 15
55
+ end
56
+ end
57
+
58
+ # Defines the Integer (32 bit) OData4 type.
59
+ class Int32 < Integer
60
+ # The OData4 type name
61
+ def type
62
+ 'Edm.Int32'
63
+ end
64
+
65
+ private
66
+
67
+ def exponent_size
68
+ 31
69
+ end
70
+ end
71
+
72
+ # Defines the Integer (64 bit) OData4 type.
73
+ class Int64 < Integer; end
74
+
75
+ # Defines the Byte OData4 type.
76
+ class Byte < Integer
77
+ # The OData4 type name
78
+ def type
79
+ 'Edm.Byte'
80
+ end
81
+
82
+ private
83
+
84
+ def exponent_size
85
+ 8
86
+ end
87
+
88
+ def min_value
89
+ 0
90
+ end
91
+ end
92
+
93
+ # Defines the Signed Byte OData4 type.
94
+ class SByte < Integer
95
+ # The OData4 type name
96
+ def type
97
+ 'Edm.SByte'
98
+ end
99
+
100
+ private
101
+
102
+ def exponent_size
103
+ 7
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,14 @@
1
+ module OData4
2
+ module Properties
3
+ # Defines common behavior for OData4 numeric types.
4
+ module Number
5
+ private
6
+
7
+ def validate(value)
8
+ if value > max_value || value < min_value
9
+ raise ::ArgumentError, "Value is outside accepted range: #{min_value} to #{max_value}"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,72 @@
1
+ module OData4
2
+ module Properties
3
+ # Defines the String OData4 type.
4
+ class String < OData4::Property
5
+ # Returns the property value, properly typecast
6
+ # @return [String,nil]
7
+ def value
8
+ if (@value.nil? || @value.empty?) && allows_nil?
9
+ nil
10
+ else
11
+ encode_value(@value)
12
+ end
13
+ end
14
+
15
+ # Sets the property value
16
+ # @params new_value [to_s,nil]
17
+ def value=(new_value)
18
+ validate(new_value)
19
+ @value = new_value.nil? ? nil : encode_value(new_value.to_s)
20
+ end
21
+
22
+ # Value to be used in URLs.
23
+ # @return [String]
24
+ def url_value
25
+ "'#{value}'"
26
+ end
27
+
28
+ # The OData4 type name
29
+ def type
30
+ 'Edm.String'
31
+ end
32
+
33
+ # Is the property value Unicode encoded
34
+ def is_unicode?
35
+ options[:unicode]
36
+ end
37
+
38
+ # Does the property have a default value
39
+ def has_default_value?
40
+ not(options[:default_value].nil?)
41
+ end
42
+
43
+ # The default value for the property
44
+ def default_value
45
+ options[:default_value]
46
+ end
47
+
48
+ private
49
+
50
+ def default_options
51
+ super.merge({
52
+ unicode: true,
53
+ default_value: nil
54
+ })
55
+ end
56
+
57
+ def validate(new_value)
58
+ if new_value.nil? && !allows_nil?
59
+ raise ArgumentError, 'This property does not allow for nil values to be set'
60
+ end
61
+ end
62
+
63
+ def encode_value(new_value)
64
+ if options[:unicode]
65
+ new_value.encode(Encoding::UTF_8)
66
+ else
67
+ new_value.encode(Encoding::ASCII)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,40 @@
1
+ module OData4
2
+ module Properties
3
+ # Defines the Time OData4 type.
4
+ class Time < OData4::Property
5
+ # Returns the property value, properly typecast
6
+ # @return [Time,nil]
7
+ def value
8
+ if (@value.nil? || @value.empty?) && allows_nil?
9
+ nil
10
+ else
11
+ ::Time.strptime(@value, '%H:%M:%S%:z')
12
+ end
13
+ end
14
+
15
+ # Sets the property value
16
+ # @params new_value [Time]
17
+ def value=(new_value)
18
+ validate(new_value)
19
+ @value = parse_value(new_value)
20
+ end
21
+
22
+ # The OData4 type name
23
+ def type
24
+ 'Edm.Time'
25
+ end
26
+
27
+ private
28
+
29
+ def validate(value)
30
+ unless value.is_a?(::Time)
31
+ raise ArgumentError, 'Value is not a time object'
32
+ end
33
+ end
34
+
35
+ def parse_value(value)
36
+ value.strftime('%H:%M:%S%:z')
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ require 'odata4/properties/date_time'
2
+
3
+ module OData4
4
+ module Properties
5
+ # Defines the Date OData4 type.
6
+ class TimeOfDay < OData4::Properties::DateTime
7
+ # The OData4 type name
8
+ def type
9
+ 'Edm.TimeOfDay'
10
+ end
11
+
12
+ def url_value
13
+ @value
14
+ end
15
+
16
+ protected
17
+
18
+ def date_class
19
+ ::Time
20
+ end
21
+
22
+ def strptime_format
23
+ '%H:%M:%S.%L'
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,118 @@
1
+ module OData4
2
+ # OData4::Property represents an abstract property, defining the basic
3
+ # interface and required methods, with some default implementations. All
4
+ # supported property types should be implemented under the OData4::Properties
5
+ # namespace.
6
+ class Property
7
+ # The property's name
8
+ attr_reader :name
9
+ # The property's value
10
+ attr_accessor :value
11
+
12
+ # Default intialization for a property with a name, value and options.
13
+ # @param name [to_s]
14
+ # @param value [to_s,nil]
15
+ # @param options [Hash]
16
+ def initialize(name, value, options = {})
17
+ @name = name.to_s
18
+ @value = value.nil? ? nil : value.to_s
19
+ @options = default_options.merge(options)
20
+ end
21
+
22
+ # Abstract implementation, should return property type, based on
23
+ # OData4::Service metadata in proper implementation.
24
+ # @raise NotImplementedError
25
+ def type
26
+ raise NotImplementedError
27
+ end
28
+
29
+ # Provides for value-based equality checking.
30
+ # @param other [value] object for comparison
31
+ # @return [Boolean]
32
+ def ==(other)
33
+ self.value == other.value
34
+ end
35
+
36
+ # Whether the property permits a nil value.
37
+ # (Default=true)
38
+ # @return [Boolean]
39
+ def allows_nil?
40
+ options[:allows_nil]
41
+ end
42
+
43
+ # Whether the property uses strict validation.
44
+ # (Default=false)
45
+ # @return [Boolean]
46
+ def strict?
47
+ options[:strict]
48
+ end
49
+
50
+ # The configured concurrency mode for the property.
51
+ # @return [String]
52
+ def concurrency_mode
53
+ @concurrecy_mode ||= options[:concurrency_mode]
54
+ end
55
+
56
+ # Value to be used in XML.
57
+ # @return [String]
58
+ def xml_value
59
+ @value
60
+ end
61
+
62
+ # Value to be used in JSON.
63
+ # @return [*]
64
+ def json_value
65
+ value
66
+ end
67
+
68
+ # Value to be used in URLs.
69
+ # @return [String]
70
+ def url_value
71
+ @value
72
+ end
73
+
74
+ # Returns the XML representation of the property to the supplied XML
75
+ # builder.
76
+ # @param xml_builder [Nokogiri::XML::Builder]
77
+ def to_xml(xml_builder)
78
+ attributes = {
79
+ 'metadata:type' => type,
80
+ }
81
+
82
+ if value.nil?
83
+ attributes['metadata:null'] = 'true'
84
+ xml_builder['data'].send(name.to_sym, attributes)
85
+ else
86
+ xml_builder['data'].send(name.to_sym, attributes, xml_value)
87
+ end
88
+ end
89
+
90
+ # Creates a new property instance from an XML element
91
+ # @param property_xml [Nokogiri::XML::Element]
92
+ # @param options [Hash]
93
+ # @return [OData4::Property]
94
+ def self.from_xml(property_xml, options = {})
95
+ if property_xml.attributes['null'].andand.value == 'true'
96
+ content = nil
97
+ else
98
+ content = property_xml.content
99
+ end
100
+
101
+ new(property_xml.name, content, options)
102
+ end
103
+
104
+ private
105
+
106
+ def default_options
107
+ {
108
+ allows_nil: true,
109
+ concurrency_mode: :none,
110
+ strict: false
111
+ }
112
+ end
113
+
114
+ def options
115
+ @options
116
+ end
117
+ end
118
+ end