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,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"