odata_server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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