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,9 @@
1
+ require "o_data/abstract_schema/base"
2
+ require "o_data/abstract_schema/schema_object"
3
+ require "o_data/abstract_schema/entity_type"
4
+ require "o_data/abstract_schema/comparable"
5
+ require "o_data/abstract_schema/serializable"
6
+ require "o_data/abstract_schema/property"
7
+ require "o_data/abstract_schema/navigation_property"
8
+ require "o_data/abstract_schema/association"
9
+ require "o_data/abstract_schema/end"
@@ -0,0 +1,29 @@
1
+ module OData
2
+ module AbstractSchema
3
+ class Association < SchemaObject
4
+ cattr_reader :polymorphic_namespace_name
5
+ @@polymorphic_namespace_name = '$polymorphic'
6
+
7
+ attr_accessor :from_end, :to_end
8
+
9
+ def initialize(schema, name, from_end_options = {}, to_end_options = {})
10
+ super(schema, name)
11
+
12
+ self.FromEnd(from_end_options.delete(:entity_type), from_end_options.delete(:return_type), from_end_options.delete(:name), from_end_options)
13
+ self.ToEnd(to_end_options.delete(:entity_type), to_end_options.delete(:return_type), to_end_options.delete(:name), to_end_options)
14
+ end
15
+
16
+ def FromEnd(*args)
17
+ @from_end = End.new(self.schema, self, *args)
18
+ end
19
+
20
+ def ToEnd(*args)
21
+ @to_end = End.new(self.schema, self, *args)
22
+ end
23
+
24
+ def inspect
25
+ "#<< #{qualified_name.to_s}(#{[@from_end, @to_end].flatten.collect { |e| "#{e.name.to_s}: #{e.return_type.to_s}" }.join(", ")}) >>"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ module OData
2
+ module AbstractSchema
3
+ class Base
4
+ attr_accessor :namespace
5
+ attr_accessor :entity_types
6
+
7
+ def initialize(namespace = "OData")
8
+ @namespace = namespace
9
+
10
+ @entity_types = []
11
+ end
12
+
13
+ def Association(*args)
14
+ Association.new(self, *args)
15
+ end
16
+
17
+ def EntityType(*args)
18
+ entity_type = EntityType.new(self, *args)
19
+ @entity_types << entity_type
20
+ entity_type
21
+ end
22
+
23
+ def associations
24
+ @entity_types.collect(&:navigation_properties).flatten.collect(&:association).uniq
25
+ end
26
+
27
+ def find_entity_type(options = {})
28
+ if options[:name]
29
+ self.entity_types.find { |et| et.name == options[:name].to_s }
30
+ else
31
+ nil
32
+ end
33
+ end
34
+
35
+ def qualify(str)
36
+ namespace.to_s + '.' + str.to_s
37
+ end
38
+
39
+ def inspect
40
+ "#<< #{namespace.to_s}(#{@entity_types.collect(&:name).join(', ')}) >>"
41
+ end
42
+
43
+ def to_json
44
+ { "d" => { "EntitySets" => @entity_types.collect(&:plural_name).sort } }.to_json
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,42 @@
1
+ module OData
2
+ module AbstractSchema
3
+ module Comparable
4
+ def compare(a, b, property_order_pairs = [])
5
+ _pairs = [] + property_order_pairs
6
+ _compare(a, b, _pairs.shift, _pairs)
7
+ end
8
+
9
+ def sort(many, property_order_pairs = [])
10
+ [many].compact.flatten.sort { |a, b| compare(a, b, property_order_pairs) }
11
+ end
12
+
13
+ protected
14
+
15
+ def _compare(a, b, head, rest)
16
+ return 0 if head.blank?
17
+
18
+ property, asc_or_desc = head
19
+ asc_or_desc ||= :asc
20
+
21
+ if b.blank?
22
+ (asc_or_desc == :asc) ? 1 : -1
23
+ else
24
+ a_value = property.value_for(a)
25
+ b_value = property.value_for(b)
26
+
27
+ if a_value.blank?
28
+ (asc_or_desc == :asc) ? -1 : 1
29
+ elsif b_value.blank?
30
+ (asc_or_desc == :asc) ? 1 : -1
31
+ elsif (c = a_value <=> b_value) != 0
32
+ (asc_or_desc == :asc) ? c : c * -1
33
+ else
34
+ _compare(a, b, rest.shift, rest)
35
+ end
36
+ end
37
+ end
38
+ end # Comparable
39
+ end # AbstractSchema
40
+ end # OData
41
+
42
+ OData::AbstractSchema::EntityType.send(:include, OData::AbstractSchema::Comparable)
@@ -0,0 +1,50 @@
1
+ module OData
2
+ module AbstractSchema
3
+ class End < SchemaObject
4
+ cattr_reader :end_option_names
5
+ @@end_option_names = %w{nullable multiple polymorphic}
6
+
7
+ @@end_option_names.each do |option_name|
8
+ define_method(:"#{option_name.to_s}?") do
9
+ !!self.options[option_name.to_sym]
10
+ end
11
+ end
12
+
13
+ attr_reader :association
14
+ attr_reader :entity_type, :return_type
15
+ attr_accessor :options
16
+
17
+ def initialize(schema, association, entity_type, return_type, name, options = {})
18
+ super(schema, name)
19
+
20
+ @association = association
21
+ @entity_type = entity_type
22
+ @return_type = return_type
23
+
24
+ unless @entity_type.nil?
25
+ @return_type ||= @entity_type.qualified_name
26
+ end
27
+
28
+ @options = {}
29
+ options.keys.select { |key| @@end_option_names.include?(key.to_s) }.each do |key|
30
+ @options[key.to_sym] = options[key]
31
+ end
32
+ end
33
+
34
+ # def return_type
35
+ # @options[:multiple] ? 'Collection(' + @return_type.to_s + ')' : @return_type.to_s
36
+ # end
37
+
38
+ def to_multiplicity
39
+ m = (@options[:nullable] ? '0' : '1') + '..' + (@options[:multiple] ? '*' : '1')
40
+ m = '1' if m == '1..1'
41
+ m = '*' if m == '0..*'
42
+ m
43
+ end
44
+
45
+ def inspect
46
+ "#<< #{qualified_name.to_s}(return_type: #{@return_type.to_s}, to_multiplicity: #{to_multiplicity.to_s}) >>"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ module OData
2
+ module AbstractSchema
3
+ class EntityType < SchemaObject
4
+ attr_accessor :properties
5
+
6
+ attr_accessor :navigation_properties
7
+
8
+ def initialize(schema, name)
9
+ super(schema, name)
10
+
11
+ @properties = []
12
+ @key_property = nil
13
+
14
+ @navigation_properties = []
15
+ end
16
+
17
+ attr_reader :key_property
18
+
19
+ def key_property=(property)
20
+ return nil unless property.is_a?(Property)
21
+ return nil unless @properties.include?(property)
22
+ @key_property = property
23
+ end
24
+
25
+ def Property(*args)
26
+ property = Property.new(self.schema, self, *args)
27
+ @properties << property
28
+ property
29
+ end
30
+
31
+ def NavigationProperty(*args)
32
+ navigation_property = NavigationProperty.new(self.schema, self, *args)
33
+ @navigation_properties << navigation_property
34
+ navigation_property
35
+ end
36
+
37
+ def find_all(key_values = {})
38
+ []
39
+ end
40
+
41
+ def find_one(key_value)
42
+ return nil if @key_property.blank?
43
+ find_all(@key_property => key_value).first
44
+ end
45
+
46
+ def exists?(key_value)
47
+ !!find_one(key_value)
48
+ end
49
+
50
+ def href_for(one)
51
+ collection_name + '(' + primary_key_for(one) + ')'
52
+ end
53
+
54
+ def primary_key_for(one)
55
+ return nil if @key_property.blank?
56
+ @key_property.value_for(one)
57
+ end
58
+
59
+ def inspect
60
+ "#<< #{qualified_name.to_s}(#{[@properties, @navigation_properties].flatten.collect { |p| "#{p.name.to_s}: #{p.return_type.to_s}" }.join(', ')}) >>"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ module OData
2
+ module AbstractSchema
3
+ class NavigationProperty < SchemaObject
4
+ attr_reader :entity_type
5
+ attr_accessor :association, :from_end, :to_end
6
+
7
+ def initialize(schema, entity_type, name, association, options = {})
8
+ super(schema, name)
9
+
10
+ @entity_type = entity_type
11
+ @association = association
12
+
13
+ options.reverse_merge!(:source => true)
14
+
15
+ if options[:source]
16
+ @from_end = @association.from_end
17
+ @to_end = @association.to_end
18
+ else
19
+ @to_end = @association.to_end
20
+ @from_end = @association.from_end
21
+ end
22
+ end
23
+
24
+ def return_type
25
+ @to_end.return_type
26
+ end
27
+
28
+ def find_all(one, key_values = {})
29
+ nil
30
+ end
31
+
32
+ def find_one(one, key_value = nil)
33
+ nil
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ module OData
2
+ module AbstractSchema
3
+ class Property < SchemaObject
4
+ cattr_reader :edm_null
5
+ @@edm_null = 'Edm.Null'.freeze
6
+
7
+ attr_reader :entity_type
8
+ attr_accessor :return_type, :nullable
9
+
10
+ def initialize(schema, entity_type, name, return_type = @@edm_null, nullable = true)
11
+ super(schema, name)
12
+
13
+ @entity_type = entity_type
14
+ @return_type = return_type
15
+ @nullable = nullable
16
+ end
17
+
18
+ def nullable?
19
+ !!@nullable
20
+ end
21
+
22
+ def value_for(one)
23
+ nil
24
+ end
25
+
26
+ def qualified_name
27
+ @entity_type.qualified_name.to_s + '#' + self.name
28
+ end
29
+
30
+ def inspect
31
+ "#<< {qualified_name.to_s}(return_type: #{@return_type.to_s}, nullable: #{nullable?}) >>"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ module OData
2
+ module AbstractSchema
3
+ class SchemaObject
4
+ attr_reader :schema
5
+ attr_accessor :name
6
+
7
+ def initialize(schema, name)
8
+ @schema = schema
9
+ @name = name
10
+ end
11
+
12
+ def qualified_name
13
+ @schema.qualify(@name)
14
+ end
15
+
16
+ include Comparable
17
+
18
+ def <=>(other)
19
+ return qualified_name <=> other.qualified_name if other.is_a?(OData::SchemaObject)
20
+ return -1 if other.blank?
21
+ 1
22
+ end
23
+
24
+ def singular_name
25
+ name.to_s.singularize
26
+ end
27
+
28
+ def plural_name
29
+ name.to_s.pluralize
30
+ end
31
+
32
+ def inspect
33
+ "#<< #{@schema.namespace}(<< #{@name.inspect}: #{self.class.name.to_s} >>) >>"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,79 @@
1
+ module OData
2
+ module AbstractSchema
3
+ module Serializable
4
+ def self.atom_element_names
5
+ %w{title summary updated_at}
6
+ end
7
+
8
+ module SchemaInstanceMethods
9
+ def atom_title_for(one)
10
+ entity_type = find_entity_type(:active_record => one.class)
11
+ return nil if entity_type.blank?
12
+
13
+ entity_type.atom_title_for(one)
14
+ end
15
+
16
+ def atom_summary_for(one)
17
+ entity_type = find_entity_type(:active_record => one.class)
18
+ return nil if entity_type.blank?
19
+
20
+ entity_type.atom_summary_for(one)
21
+ end
22
+
23
+ def atom_updated_at_for(one)
24
+ entity_type = find_entity_type(:active_record => one.class)
25
+ return nil if entity_type.blank?
26
+
27
+ entity_type.atom_updated_at_for(one)
28
+ end
29
+ end
30
+
31
+ module EntityTypeInstanceMethods
32
+ def self.included(base)
33
+ base.instance_eval do
34
+ attr_reader *OData::AbstractSchema::Serializable.atom_element_names.collect { |atom_element_name| :"atom_#{atom_element_name}_property" }
35
+ end
36
+ end
37
+
38
+ def atom_title_for(one)
39
+ return href_for(one) if self.atom_title_property.blank?
40
+ self.atom_title_property.value_for(one)
41
+ end
42
+
43
+ def atom_summary_for(one)
44
+ return nil if self.atom_summary_property.blank?
45
+ self.atom_summary_property.value_for(one)
46
+ end
47
+
48
+ def atom_updated_at_for(one)
49
+ return nil if self.atom_updated_at_property.blank?
50
+ self.atom_updated_at_property.value_for(one)
51
+ end
52
+
53
+ def atom_title_property=(property)
54
+ return nil unless property.is_a?(Property)
55
+ return nil unless property.return_type.to_s == 'Edm.String'
56
+ return nil unless self.properties.find { |p| p.name == property.name }
57
+ @atom_title_property = property
58
+ end
59
+
60
+ def atom_summary_property=(property)
61
+ return nil unless property.is_a?(Property)
62
+ return nil unless property.return_type.to_s == 'Edm.String'
63
+ return nil unless self.properties.find { |p| p.name == property.name }
64
+ @atom_summary_property = property
65
+ end
66
+
67
+ def atom_updated_at_property=(property)
68
+ return nil unless property.is_a?(Property)
69
+ return nil unless %w{Edm.Date Edm.DateTime Edm.Time}.include?(property.return_type.to_s)
70
+ return nil unless self.properties.find { |p| p.name == property.name }
71
+ @atom_updated_at_property = property
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ OData::AbstractSchema::Base.send(:include, OData::AbstractSchema::Serializable::SchemaInstanceMethods)
79
+ OData::AbstractSchema::EntityType.send(:include, OData::AbstractSchema::Serializable::EntityTypeInstanceMethods)
@@ -0,0 +1,8 @@
1
+ require "active_record"
2
+
3
+ require "o_data/active_record_schema/entity_type"
4
+ require "o_data/active_record_schema/serializable"
5
+ require "o_data/active_record_schema/property"
6
+ require "o_data/active_record_schema/navigation_property"
7
+ require "o_data/active_record_schema/association"
8
+ require "o_data/active_record_schema/base"