odata_server 0.0.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 (60) hide show
  1. data/Gemfile +12 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +14 -0
  4. data/Rakefile +53 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/o_data_controller.rb +145 -0
  7. data/app/helpers/o_data_helper.rb +203 -0
  8. data/app/views/o_data/metadata.xml.builder +62 -0
  9. data/app/views/o_data/resource.atom.builder +8 -0
  10. data/app/views/o_data/resource.json.erb +1 -0
  11. data/app/views/o_data/service.xml.builder +11 -0
  12. data/config/routes.rb +11 -0
  13. data/init.rb +3 -0
  14. data/install.rb +1 -0
  15. data/lib/o_data/abstract_query.rb +27 -0
  16. data/lib/o_data/abstract_query/base.rb +133 -0
  17. data/lib/o_data/abstract_query/countable.rb +22 -0
  18. data/lib/o_data/abstract_query/errors.rb +117 -0
  19. data/lib/o_data/abstract_query/option.rb +58 -0
  20. data/lib/o_data/abstract_query/options/enumerated_option.rb +39 -0
  21. data/lib/o_data/abstract_query/options/expand_option.rb +81 -0
  22. data/lib/o_data/abstract_query/options/format_option.rb +19 -0
  23. data/lib/o_data/abstract_query/options/inlinecount_option.rb +20 -0
  24. data/lib/o_data/abstract_query/options/orderby_option.rb +79 -0
  25. data/lib/o_data/abstract_query/options/select_option.rb +74 -0
  26. data/lib/o_data/abstract_query/options/skip_option.rb +32 -0
  27. data/lib/o_data/abstract_query/options/top_option.rb +32 -0
  28. data/lib/o_data/abstract_query/parser.rb +77 -0
  29. data/lib/o_data/abstract_query/segment.rb +43 -0
  30. data/lib/o_data/abstract_query/segments/collection_segment.rb +24 -0
  31. data/lib/o_data/abstract_query/segments/count_segment.rb +38 -0
  32. data/lib/o_data/abstract_query/segments/entity_type_and_key_values_segment.rb +116 -0
  33. data/lib/o_data/abstract_query/segments/entity_type_segment.rb +31 -0
  34. data/lib/o_data/abstract_query/segments/links_segment.rb +49 -0
  35. data/lib/o_data/abstract_query/segments/navigation_property_segment.rb +82 -0
  36. data/lib/o_data/abstract_query/segments/property_segment.rb +50 -0
  37. data/lib/o_data/abstract_query/segments/value_segment.rb +40 -0
  38. data/lib/o_data/abstract_schema.rb +9 -0
  39. data/lib/o_data/abstract_schema/association.rb +29 -0
  40. data/lib/o_data/abstract_schema/base.rb +48 -0
  41. data/lib/o_data/abstract_schema/comparable.rb +42 -0
  42. data/lib/o_data/abstract_schema/end.rb +50 -0
  43. data/lib/o_data/abstract_schema/entity_type.rb +64 -0
  44. data/lib/o_data/abstract_schema/navigation_property.rb +37 -0
  45. data/lib/o_data/abstract_schema/property.rb +35 -0
  46. data/lib/o_data/abstract_schema/schema_object.rb +37 -0
  47. data/lib/o_data/abstract_schema/serializable.rb +79 -0
  48. data/lib/o_data/active_record_schema.rb +8 -0
  49. data/lib/o_data/active_record_schema/association.rb +130 -0
  50. data/lib/o_data/active_record_schema/base.rb +37 -0
  51. data/lib/o_data/active_record_schema/entity_type.rb +128 -0
  52. data/lib/o_data/active_record_schema/navigation_property.rb +45 -0
  53. data/lib/o_data/active_record_schema/property.rb +45 -0
  54. data/lib/o_data/active_record_schema/serializable.rb +36 -0
  55. data/lib/odata_server.rb +12 -0
  56. data/public/clientaccesspolicy.xml +13 -0
  57. data/tasks/o_data_server_tasks.rake +4 -0
  58. data/test/helper.rb +17 -0
  59. data/uninstall.rb +1 -0
  60. metadata +171 -0
@@ -0,0 +1,43 @@
1
+ module OData
2
+ module AbstractQuery
3
+ class Segment
4
+ attr_reader :query, :value
5
+
6
+ def self.parse!(query, str)
7
+ nil
8
+ end
9
+
10
+ def self.segment_name
11
+ name.to_s.demodulize.sub(/Segment$/, '')
12
+ end
13
+
14
+ def initialize(query, value)
15
+ @query = query
16
+ @value = value
17
+
18
+ raise Errors::InvalidSegmentContext.new(@query, self) unless can_follow?(@query.segments.last)
19
+ end
20
+
21
+ def self.can_follow?(anOtherSegment)
22
+ # self (Segment class) can_follow? anOtherSegment (instance of Segment class)
23
+ false
24
+ end
25
+
26
+ def can_follow?(anOtherSegment)
27
+ self.class.can_follow?(anOtherSegment)
28
+ end
29
+
30
+ def execute!(acc)
31
+ acc
32
+ end
33
+
34
+ def inspect
35
+ "#<< #{@query.schema.namespace.to_s}(#{self.class.segment_name}: #{self.value.inspect}) >>"
36
+ end
37
+
38
+ def valid?(results)
39
+ !results.blank?
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class CollectionSegment < EntityTypeAndKeyValuesSegment
5
+ def self.parse!(query, str)
6
+ return nil unless query.segments.empty?
7
+ schema_object_name, key_values, keys = extract_schema_object_name_and_key_values_and_keys(str)
8
+ return nil if schema_object_name.blank?
9
+
10
+ entity_type = query.schema.find_entity_type(:name => schema_object_name.singularize)
11
+ return nil if entity_type.blank?
12
+
13
+ sanitized_key_values = sanitize_key_values_and_keys_for!(query, entity_type, key_values, keys)
14
+
15
+ query.Segment(self, entity_type, sanitized_key_values)
16
+ end
17
+
18
+ def self.can_follow?(anOtherSegment)
19
+ anOtherSegment.blank?
20
+ end
21
+ end # CollectionSegment
22
+ end # Segments
23
+ end # AbstractQuery
24
+ end # OData
@@ -0,0 +1,38 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class CountSegment < OData::AbstractQuery::Segment
5
+ def self.parse!(query, str)
6
+ return nil unless str.to_s == segment_name
7
+
8
+ query.Segment(self)
9
+ end
10
+
11
+ def self.segment_name
12
+ "$count"
13
+ end
14
+
15
+ def initialize(query)
16
+ super(query, self.class.segment_name)
17
+ end
18
+
19
+ def self.can_follow?(anOtherSegment)
20
+ if anOtherSegment.is_a?(Class)
21
+ anOtherSegment == CollectionSegment || anOtherSegment == NavigationPropertySegment
22
+ else
23
+ (anOtherSegment.is_a?(CollectionSegment) || anOtherSegment.is_a?(NavigationPropertySegment)) && anOtherSegment.countable?
24
+ end
25
+ end
26
+
27
+ def execute!(acc)
28
+ return acc.length if acc.respond_to?(:length)
29
+ 1
30
+ end
31
+
32
+ def valid?(results)
33
+ results.is_a?(Fixnum)
34
+ end
35
+ end # CountSegment
36
+ end # Segments
37
+ end # AbstractQuery
38
+ end # OData
@@ -0,0 +1,116 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class EntityTypeAndKeyValuesSegment < EntityTypeSegment
5
+ def self.remove_quotes(str)
6
+ str.to_s.sub(/^\'(.*?)\'$/, '\1')
7
+ end
8
+
9
+ def self.extract_schema_object_name_and_key_values_and_keys(str)
10
+ if md1 = str.to_s.match(/^([^\(]+)(?:\(([^\)]+)\)|\(\s*\))?$/)
11
+ schema_object_name = md1[1]
12
+ key_values = {}
13
+ keys = []
14
+
15
+ if key_values_string = md1[2]
16
+ key_values_string.split(/\s*,\s*/).each do |key_value_pair|
17
+ key, value = key_value_pair.split('=', 2)
18
+
19
+ if value
20
+ key_values[key.to_sym] = remove_quotes(value)
21
+ else
22
+ keys << remove_quotes(key)
23
+ end
24
+ end
25
+ end
26
+
27
+ [schema_object_name, key_values, keys]
28
+ else
29
+ nil
30
+ end
31
+ end
32
+
33
+ def self.sanitize_key_values_and_keys_for!(query, entity_type, key_values = {}, keys = [])
34
+ key_property_name = entity_type.key_property.name
35
+
36
+ sanitized_key_values = key_values.inject({}) { |acc, key_value_pair|
37
+ key, value = key_value_pair
38
+
39
+ property = entity_type.properties.find { |p| p.name == key.to_s }
40
+ raise OData::AbstractQuery::Errors::PropertyNotFound.new(query, key) if property.blank?
41
+
42
+ raise OData::AbstractQuery::Errors::AbstractQueryKeyValueException.new(query, key, value) unless acc[key.to_sym].blank?
43
+
44
+ acc[property.name.to_sym] = value
45
+ acc
46
+ }
47
+
48
+ keys.inject(sanitized_key_values) { |acc, key_value|
49
+ raise OData::AbstractQuery::Errors::AbstractQueryKeyValueException.new(query, key_property_name, key_value) unless acc[key_property_name.to_sym].blank?
50
+
51
+ acc[key_property_name.to_sym] = key_value
52
+ acc
53
+ }
54
+ end
55
+
56
+ attr_reader :key_values
57
+
58
+ def initialize(query, entity_type, key_values = {})
59
+ @key_values = key_values
60
+
61
+ super(query, entity_type)
62
+ end
63
+
64
+ def countable?
65
+ !key?
66
+ end
67
+
68
+ def execute!(acc)
69
+ return [] if self.entity_type.blank?
70
+
71
+ if key?
72
+ self.entity_type.find_one(key_property_value)
73
+ else
74
+ self.entity_type.find_all(@key_values)
75
+ end
76
+ end
77
+
78
+ def key?
79
+ !self.entity_type.blank? && self.class.key?(self.entity_type, @key_values)
80
+ end
81
+
82
+ def key_property_value
83
+ return nil if self.entity_type.blank?
84
+
85
+ self.class.key_property_value_for(self.entity_type, @key_values)
86
+ end
87
+
88
+ def value
89
+ if @key_values.blank?
90
+ super
91
+ elsif key?
92
+ super + '(' + key_property_value.to_s + ')'
93
+ else
94
+ super + '(' + @key_values.collect { |key, value| "#{key}=#{value}" }.join(',') + ')'
95
+ end
96
+ end
97
+
98
+ def self.key?(entity_type, key_values = {})
99
+ !!key_property_value_for(entity_type, key_values)
100
+ end
101
+
102
+ def self.key_property_value_for(entity_type, key_values = {})
103
+ return nil if entity_type.blank?
104
+
105
+ return nil if key_values.blank?
106
+ return nil unless key_values.size == 1
107
+
108
+ key_property = entity_type.key_property
109
+ return nil if key_property.blank?
110
+
111
+ key_values[key_property.name.to_sym]
112
+ end
113
+ end # EntityTypeAndKeyValuesSegment
114
+ end # Segments
115
+ end # AbstractQuery
116
+ end # OData
@@ -0,0 +1,31 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class EntityTypeSegment < OData::AbstractQuery::Segment
5
+ include OData::AbstractQuery::Countable
6
+
7
+ attr_reader :entity_type
8
+
9
+ def initialize(query, entity_type, value = nil)
10
+ @entity_type = entity_type
11
+
12
+ super(query, value || (@entity_type.is_a?(OData::AbstractSchema::EntityType) ? @entity_type.plural_name : @entity_type))
13
+ end
14
+
15
+ def self.can_follow?(anOtherSegment)
16
+ false
17
+ end
18
+
19
+ def execute!(acc)
20
+ return [] if @entity_type.blank?
21
+
22
+ @entity_type.find_all
23
+ end
24
+
25
+ def valid?(results)
26
+ countable? ? results.is_a?(Array) : !results.blank?
27
+ end
28
+ end # EntityTypeSegment
29
+ end # Segments
30
+ end # AbstractQuery
31
+ end # OData
@@ -0,0 +1,49 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class LinksSegment < OData::AbstractQuery::Segment
5
+ include OData::AbstractQuery::Countable
6
+
7
+ def self.parse!(query, str)
8
+ return nil unless str.to_s == segment_name
9
+
10
+ query.Segment(self)
11
+ end
12
+
13
+ def self.segment_name
14
+ "$links"
15
+ end
16
+
17
+ def self.countable?
18
+ true
19
+ end
20
+
21
+ def initialize(query)
22
+ super(query, self.class.segment_name)
23
+ end
24
+
25
+ def self.can_follow?(anOtherSegment)
26
+ if anOtherSegment.is_a?(Class)
27
+ anOtherSegment == CollectionSegment || anOtherSegment == NavigationPropertySegment
28
+ else
29
+ (anOtherSegment.is_a?(CollectionSegment) || anOtherSegment.is_a?(NavigationPropertySegment)) # && anOtherSegment.countable?
30
+ end
31
+ end
32
+
33
+ def execute!(acc)
34
+ [acc].flatten.compact.collect { |one|
35
+ if entity_type = self.query.schema.entity_types.find { |et| et.name == one.class.name }
36
+ [one, entity_type.plural_name + '(' + entity_type.primary_key_for(one).to_s + ')']
37
+ else
38
+ [one, nil]
39
+ end
40
+ }
41
+ end
42
+
43
+ def valid?(results)
44
+ results.all? { |pair| !pair[1].blank? }
45
+ end
46
+ end # LinksSegment
47
+ end # Segments
48
+ end # AbstractQuery
49
+ end # OData
@@ -0,0 +1,82 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class NavigationPropertySegment < EntityTypeAndKeyValuesSegment
5
+ def self.parse!(query, str)
6
+ return nil if query.segments.empty?
7
+ return nil unless query.segments.last.respond_to?(:entity_type)
8
+ entity_type = query.segments.last.entity_type
9
+ return nil if entity_type.blank?
10
+
11
+ schema_object_name, key_values, keys = extract_schema_object_name_and_key_values_and_keys(str)
12
+ return nil if schema_object_name.blank?
13
+
14
+ navigation_property = entity_type.navigation_properties.find { |np| np.name == schema_object_name }
15
+ return nil if navigation_property.blank?
16
+
17
+ if navigation_property.to_end.polymorphic?
18
+ raise OData::AbstractQuery::Errors::AbstractQueryKeyValueException.new(query, key_values.keys.first, key_values.values.first) unless key_values.empty?
19
+ raise OData::AbstractQuery::Errors::AbstractQueryKeyValueException.new(query, '$polymorphic#Key', keys.first) unless keys.empty?
20
+
21
+ query.Segment(self, entity_type, navigation_property.to_end.return_type, navigation_property, {})
22
+ else
23
+ sanitized_key_values = sanitize_key_values_and_keys_for!(query, navigation_property.to_end.entity_type, key_values, keys)
24
+
25
+ unless sanitized_key_values.empty?
26
+ raise OData::AbstractQuery::Errors::AbstractQueryKeyValueException.new(query, sanitized_key_values.keys.first, sanitized_key_values.values.first) unless navigation_property.to_end.multiple?
27
+ end
28
+
29
+ query.Segment(self, entity_type, navigation_property.to_end.entity_type, navigation_property, sanitized_key_values)
30
+ end
31
+ end
32
+
33
+ alias_method :to_entity_type, :entity_type
34
+
35
+ attr_reader :navigation_property
36
+
37
+ def initialize(query, from_entity_type, to_entity_type, navigation_property, key_values = {})
38
+ @from_entity_type = from_entity_type
39
+ @navigation_property = navigation_property
40
+
41
+ super(query, to_entity_type, key_values)
42
+ end
43
+
44
+ def self.can_follow?(anOtherSegment)
45
+ if anOtherSegment.is_a?(Class)
46
+ anOtherSegment == CollectionSegment || anOtherSegment == NavigationPropertySegment
47
+ else
48
+ (anOtherSegment.is_a?(CollectionSegment) || anOtherSegment.is_a?(NavigationPropertySegment)) && !anOtherSegment.countable?
49
+ end
50
+ end
51
+
52
+ def countable?
53
+ multiple? && super
54
+ end
55
+
56
+ def execute!(acc)
57
+ [acc].flatten.compact.collect { |one|
58
+ if key?
59
+ @navigation_property.find_one(one, key_property_value)
60
+ else
61
+ @navigation_property.find_all(one, @key_values)
62
+ end
63
+ }.first
64
+ end
65
+
66
+ def multiple?
67
+ @navigation_property.to_end.multiple?
68
+ end
69
+
70
+ def value
71
+ if self.key_values.blank? || !multiple?
72
+ @navigation_property.name
73
+ elsif key?
74
+ @navigation_property.name + '(' + key_property_value.to_s + ')'
75
+ else
76
+ @navigation_property.name + '(' + self.key_values.collect { |key, value| "#{key}=#{value}" }.join(',') + ')'
77
+ end
78
+ end
79
+ end # NavigationPropertySegment
80
+ end # Segments
81
+ end # AbstractQuery
82
+ end # OData
@@ -0,0 +1,50 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class PropertySegment < EntityTypeSegment
5
+ attr_reader :property
6
+
7
+ def self.parse!(query, str)
8
+ return nil if query.segments.empty?
9
+ return nil unless query.segments.last.respond_to?(:entity_type)
10
+ entity_type = query.segments.last.entity_type
11
+ return nil if entity_type.nil?
12
+ property = entity_type.properties.find { |p| p.name == str }
13
+ return nil if property.blank?
14
+
15
+ query.Segment(self, entity_type, property)
16
+ end
17
+
18
+ def initialize(query, entity_type, property)
19
+ @property = property
20
+
21
+ super(query, entity_type, @property.name)
22
+ end
23
+
24
+ def self.can_follow?(anOtherSegment)
25
+ if anOtherSegment.is_a?(Class)
26
+ anOtherSegment == CollectionSegment || anOtherSegment == NavigationPropertySegment
27
+ else
28
+ (anOtherSegment.is_a?(CollectionSegment) || anOtherSegment.is_a?(NavigationPropertySegment)) && !anOtherSegment.countable?
29
+ end
30
+ end
31
+
32
+ def countable?
33
+ false
34
+ end
35
+
36
+ def execute!(acc)
37
+ # [acc].flatten.compact.collect { |one|
38
+ # [one, @property.value_for(one)]
39
+ # }
40
+ { @property => @property.value_for([acc].flatten.compact.first) }
41
+ end
42
+
43
+ def valid?(results)
44
+ # results.is_a?(Array)
45
+ !results.blank?
46
+ end
47
+ end # PropertySegment
48
+ end # Segments
49
+ end # AbstractQuery
50
+ end # OData
@@ -0,0 +1,40 @@
1
+ module OData
2
+ module AbstractQuery
3
+ module Segments
4
+ class ValueSegment < OData::AbstractQuery::Segment
5
+ def self.parse!(query, str)
6
+ return nil unless str.to_s == segment_name
7
+
8
+ query.Segment(self)
9
+ end
10
+
11
+ def self.segment_name
12
+ "$value"
13
+ end
14
+
15
+ def initialize(query)
16
+ super(query, self.class.segment_name)
17
+ end
18
+
19
+ def self.can_follow?(anOtherSegment)
20
+ if anOtherSegment.is_a?(Class)
21
+ anOtherSegment == PropertySegment
22
+ else
23
+ anOtherSegment.is_a?(PropertySegment)
24
+ end
25
+ end
26
+
27
+ def execute!(acc)
28
+ # acc
29
+ acc.values.first
30
+ end
31
+
32
+ def valid?(results)
33
+ # # results.is_a?(Array)
34
+ # !results.blank?
35
+ true
36
+ end
37
+ end # ValueSegment
38
+ end # Segments
39
+ end # AbstractQuery
40
+ end # OData