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