frodata 0.9.1

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 (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,67 @@
1
+ module FrOData
2
+ module Properties
3
+ # Defines the Float FrOData type.
4
+ class Float < FrOData::Property
5
+ include FrOData::Properties::Number
6
+
7
+ # Returns the property value, properly typecast
8
+ # @return [Float,nil]
9
+ def value
10
+ if (@value.nil? || @value.empty?) && allows_nil?
11
+ nil
12
+ else
13
+ @value.to_f
14
+ end
15
+ end
16
+
17
+ # Sets the property value
18
+ # @params new_value [to_f]
19
+ def value=(new_value)
20
+ validate(new_value.to_f)
21
+ @value = new_value.to_f.to_s
22
+ end
23
+
24
+ # The FrOData type name
25
+ def type
26
+ 'Edm.Double'
27
+ end
28
+
29
+ private
30
+
31
+ def min_value
32
+ @min ||= -(1.7 * (10**308))
33
+ end
34
+
35
+ def max_value
36
+ @max ||= (1.7 * (10**308))
37
+ end
38
+ end
39
+
40
+ # Defines the Double (Float) FrOData type.
41
+ class Double < FrOData::Properties::Float; end
42
+
43
+ # Defines the Single (Float) FrOData type.
44
+ class Single < FrOData::Properties::Float
45
+ # The FrOData type name
46
+ def type
47
+ 'Edm.Single'
48
+ end
49
+
50
+ # Value to be used in URLs.
51
+ # @return [String]
52
+ def url_value
53
+ "#{value}F"
54
+ end
55
+
56
+ private
57
+
58
+ def min_value
59
+ @min ||= -(3.4 * (10**38))
60
+ end
61
+
62
+ def max_value
63
+ @max ||= (3.4 * (10**38))
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,13 @@
1
+ require 'frodata/properties/geography/base'
2
+ require 'frodata/properties/geography/point'
3
+ require 'frodata/properties/geography/line_string'
4
+ require 'frodata/properties/geography/polygon'
5
+
6
+ FrOData::Properties::Geography.constants.each do |property_name|
7
+ next if property_name =~ /Base$/
8
+ klass = FrOData::Properties::Geography.const_get(property_name)
9
+ if klass.is_a?(Class)
10
+ property = klass.new('test', nil)
11
+ FrOData::PropertyRegistry.add(property.type, property.class)
12
+ end
13
+ end
@@ -0,0 +1,162 @@
1
+ module FrOData
2
+ module Properties
3
+ module Geography
4
+ class Base < FrOData::Property
5
+ # The SRID (Spatial Reference ID) of this property.
6
+ attr_accessor :srid
7
+
8
+ # The default SRID (same as used by GPS)
9
+ DEFAULT_SRID = 4326
10
+
11
+ # Initializes a geography property.
12
+ #
13
+ # Special options available for geographic types:
14
+ #
15
+ # +srid+: the SRID (spatial reference ID) of the
16
+ # coordinate system being used.
17
+ # Defaults to 4326 (same as GPS).
18
+ #
19
+ # @param name [to_s]
20
+ # @param value [Hash|Array|to_s|nil]
21
+ # @param options [Hash]
22
+ def initialize(name, value, options = {})
23
+ super(name, value, options)
24
+ self.value = value
25
+ self.srid = srid || options[:srid] || DEFAULT_SRID
26
+ end
27
+
28
+ # Sets the value of the property.
29
+ # @param value [Hash|Array|to_s|nil]
30
+ def value=(value)
31
+ if value.nil? && allows_nil?
32
+ @value = nil
33
+ elsif value.is_a?(Hash)
34
+ @value = value['coordinates']
35
+ elsif value.is_a?(Array)
36
+ @value = value
37
+ else
38
+ @value = parse_wkt(value.to_s)
39
+ end
40
+ end
41
+
42
+ # Value to be used in URLs.
43
+ # @return [String]
44
+ def url_value
45
+ "geography'SRID=#{srid};#{type_name}(#{coords_to_s})'"
46
+ end
47
+
48
+ # Value to be used in JSON.
49
+ # @return [Hash]
50
+ def json_value
51
+ {
52
+ type: type_name,
53
+ coordinates: value,
54
+ crs: crs
55
+ }
56
+ end
57
+
58
+ # The name of the SRS (Spatial Reference System) used.
59
+ # Basically, the SRID in URI/URL form.
60
+ # @return [String]
61
+ def srs_name
62
+ if srid == DEFAULT_SRID
63
+ "http://www.opengis.net/def/crs/EPSG/0/#{srid}"
64
+ else
65
+ raise NotImplementedError, "Unsupported SRID #{srid}"
66
+ end
67
+ end
68
+
69
+ # The name of the CRS (Coordinate Reference System) used.
70
+ # Used in GeoJSON representation
71
+ # @return [String]
72
+ def crs_name
73
+ if srid == DEFAULT_SRID
74
+ "EPSG:#{srid}"
75
+ else
76
+ raise NotImplementedError, "Unsupported SRID #{srid}"
77
+ end
78
+ end
79
+
80
+ # The full CRS representation as used by GeoJSON
81
+ # @return [Hash]
82
+ def crs
83
+ {
84
+ type: 'name',
85
+ properties: { name: crs_name }
86
+ }
87
+ end
88
+
89
+ # Returns the XML representation of the property to the supplied XML
90
+ # builder.
91
+ # @param xml_builder [Nokogiri::XML::Builder]
92
+ def to_xml(xml_builder)
93
+ attributes = { 'metadata:type' => type }
94
+ type_attrs = { 'gml:srsName' => srs_name }
95
+
96
+ xml_builder['data'].send(name.to_sym, attributes) do
97
+ xml_builder['gml'].send(type_name, type_attrs) do
98
+ value_to_xml(xml_value, xml_builder)
99
+ end
100
+ end
101
+ end
102
+
103
+ # Creates a new property instance from an XML element
104
+ # @param property_xml [Nokogiri::XML::Element]
105
+ # @param options [Hash]
106
+ # @return [FrOData::Properties::Geography]
107
+ def self.from_xml(property_xml, options = {})
108
+ if property_xml.attributes['null'].andand.value == 'true'
109
+ content = nil
110
+ else
111
+ content = parse_xml(property_xml)
112
+ options.merge!(srid: srid_from_xml(property_xml))
113
+ end
114
+
115
+ new(property_xml.name, content, options)
116
+ end
117
+
118
+ protected
119
+
120
+ def type_name
121
+ self.class.name.split('::').last
122
+ end
123
+
124
+ def parse_wkt(value)
125
+ if value =~ /^geography'(SRID=(\d+);)+(\w+)\((.+)\)'$/
126
+ $3 == type_name or raise ArgumentError, "Invalid geography type '#{$3}'"
127
+ self.srid = $1 ? $2.to_i : DEFAULT_SRID
128
+ coords_from_s($4)
129
+ else
130
+ raise ArgumentError, "Invalid geography value '#{value}'"
131
+ end
132
+ end
133
+
134
+ # Recursively turn a JSON-like data structure into XML
135
+ def value_to_xml(value, xml_builder)
136
+ if value.is_a?(Hash)
137
+ value.each do |key, val|
138
+ xml_builder['gml'].send(key) do
139
+ value_to_xml(val, xml_builder)
140
+ end
141
+ end
142
+ elsif value.is_a?(Array)
143
+ value.each do |val|
144
+ value_to_xml(val, xml_builder)
145
+ end
146
+ else
147
+ xml_builder.text(value)
148
+ end
149
+ end
150
+
151
+ # Extract the SRID from a GML element's `srsName` attribute
152
+ def self.srid_from_xml(property_xml)
153
+ gml_elem = property_xml.element_children.first
154
+ srs_attr = gml_elem.attributes['srsName']
155
+ if srs_attr
156
+ srs_attr.value.split(/[\/:]/).last.to_i
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,33 @@
1
+ module FrOData
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 FrOData
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 FrOData
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 FrOData
2
+ module Properties
3
+ # Defines the GUID FrOData type.
4
+ class Guid < FrOData::Property
5
+ # The FrOData 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 FrOData
2
+ module Properties
3
+ # Defines the Integer FrOData type.
4
+ class Integer < FrOData::Property
5
+ include FrOData::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 FrOData 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) FrOData type.
45
+ class Int16 < Integer
46
+ # The FrOData 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) FrOData type.
59
+ class Int32 < Integer
60
+ # The FrOData 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) FrOData type.
73
+ class Int64 < Integer; end
74
+
75
+ # Defines the Byte FrOData type.
76
+ class Byte < Integer
77
+ # The FrOData 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 FrOData type.
94
+ class SByte < Integer
95
+ # The FrOData 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