odata_server 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -0
- data/MIT-LICENSE +20 -0
- data/README +14 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/app/controllers/o_data_controller.rb +145 -0
- data/app/helpers/o_data_helper.rb +203 -0
- data/app/views/o_data/metadata.xml.builder +62 -0
- data/app/views/o_data/resource.atom.builder +8 -0
- data/app/views/o_data/resource.json.erb +1 -0
- data/app/views/o_data/service.xml.builder +11 -0
- data/config/routes.rb +11 -0
- data/init.rb +3 -0
- data/install.rb +1 -0
- data/lib/o_data/abstract_query.rb +27 -0
- data/lib/o_data/abstract_query/base.rb +133 -0
- data/lib/o_data/abstract_query/countable.rb +22 -0
- data/lib/o_data/abstract_query/errors.rb +117 -0
- data/lib/o_data/abstract_query/option.rb +58 -0
- data/lib/o_data/abstract_query/options/enumerated_option.rb +39 -0
- data/lib/o_data/abstract_query/options/expand_option.rb +81 -0
- data/lib/o_data/abstract_query/options/format_option.rb +19 -0
- data/lib/o_data/abstract_query/options/inlinecount_option.rb +20 -0
- data/lib/o_data/abstract_query/options/orderby_option.rb +79 -0
- data/lib/o_data/abstract_query/options/select_option.rb +74 -0
- data/lib/o_data/abstract_query/options/skip_option.rb +32 -0
- data/lib/o_data/abstract_query/options/top_option.rb +32 -0
- data/lib/o_data/abstract_query/parser.rb +77 -0
- data/lib/o_data/abstract_query/segment.rb +43 -0
- data/lib/o_data/abstract_query/segments/collection_segment.rb +24 -0
- data/lib/o_data/abstract_query/segments/count_segment.rb +38 -0
- data/lib/o_data/abstract_query/segments/entity_type_and_key_values_segment.rb +116 -0
- data/lib/o_data/abstract_query/segments/entity_type_segment.rb +31 -0
- data/lib/o_data/abstract_query/segments/links_segment.rb +49 -0
- data/lib/o_data/abstract_query/segments/navigation_property_segment.rb +82 -0
- data/lib/o_data/abstract_query/segments/property_segment.rb +50 -0
- data/lib/o_data/abstract_query/segments/value_segment.rb +40 -0
- data/lib/o_data/abstract_schema.rb +9 -0
- data/lib/o_data/abstract_schema/association.rb +29 -0
- data/lib/o_data/abstract_schema/base.rb +48 -0
- data/lib/o_data/abstract_schema/comparable.rb +42 -0
- data/lib/o_data/abstract_schema/end.rb +50 -0
- data/lib/o_data/abstract_schema/entity_type.rb +64 -0
- data/lib/o_data/abstract_schema/navigation_property.rb +37 -0
- data/lib/o_data/abstract_schema/property.rb +35 -0
- data/lib/o_data/abstract_schema/schema_object.rb +37 -0
- data/lib/o_data/abstract_schema/serializable.rb +79 -0
- data/lib/o_data/active_record_schema.rb +8 -0
- data/lib/o_data/active_record_schema/association.rb +130 -0
- data/lib/o_data/active_record_schema/base.rb +37 -0
- data/lib/o_data/active_record_schema/entity_type.rb +128 -0
- data/lib/o_data/active_record_schema/navigation_property.rb +45 -0
- data/lib/o_data/active_record_schema/property.rb +45 -0
- data/lib/o_data/active_record_schema/serializable.rb +36 -0
- data/lib/odata_server.rb +12 -0
- data/public/clientaccesspolicy.xml +13 -0
- data/tasks/o_data_server_tasks.rake +4 -0
- data/test/helper.rb +17 -0
- data/uninstall.rb +1 -0
- 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"
|