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,62 @@
|
|
1
|
+
xml.instruct!
|
2
|
+
xml.edmx(:Edmx, :Version => "1.0", "xmlns:edmx" => "http://schemas.microsoft.com/ado/2007/06/edmx", "xml:base" => o_data_metadata_url, "xml:id" => "") do
|
3
|
+
xml.edmx(:DataServices, "m:DataServiceVersion" => "2.0", "xmlns:m" => "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata") do
|
4
|
+
xml.tag!(:Schema, :Namespace => ODataController.schema.namespace, "xmlns:d" => "http://schemas.microsoft.com/ado/2007/08/dataservices", "xmlns" => "http://schemas.microsoft.com/ado/2007/05/edm", "xml:id" => "Schema") do
|
5
|
+
ODataController.schema.entity_types.sort_by(&:qualified_name).each do |entity_type|
|
6
|
+
xml.tag!(:EntityType, :Name => entity_type.name) do
|
7
|
+
unless entity_type.key_property.blank?
|
8
|
+
xml.tag!(:Key) do
|
9
|
+
xml.tag!(:PropertyRef, :Name => entity_type.key_property.name)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
entity_type.properties.each do |property|
|
14
|
+
property_attrs = { :Name => property.name, :Type => property.return_type, :Nullable => property.nullable? }
|
15
|
+
|
16
|
+
property_attrs.merge!("m:FC_TargetPath" => "SyndicationTitle", "m:FC_ContentKind" => "text", "m:FC_KeepInContent" => "true") if property == entity_type.atom_title_property
|
17
|
+
property_attrs.merge!("m:FC_TargetPath" => "SyndicationSummary", "m:FC_ContentKind" => "text", "m:FC_KeepInContent" => "false") if property == entity_type.atom_summary_property
|
18
|
+
property_attrs.merge!("m:FC_TargetPath" => "SyndicationUpdated", "m:FC_ContentKind" => "text", "m:FC_KeepInContent" => "true") if property == entity_type.atom_updated_at_property
|
19
|
+
|
20
|
+
xml.tag!(:Property, property_attrs)
|
21
|
+
end
|
22
|
+
|
23
|
+
entity_type.navigation_properties.sort_by(&:qualified_name).each do |navigation_property|
|
24
|
+
xml.tag!(:NavigationProperty, :Name => navigation_property.name, :Relationship => navigation_property.association.qualified_name, :FromRole => navigation_property.from_end.name, :ToRole => navigation_property.to_end.name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
ODataController.schema.associations.sort_by(&:qualified_name).each do |association|
|
30
|
+
xml.tag!(:Association, :Name => association.name) do
|
31
|
+
xml.tag!(:End, :Role => association.from_end.name, :Type => association.from_end.return_type, :Multiplicity => association.from_end.to_multiplicity)
|
32
|
+
xml.tag!(:End, :Role => association.to_end.name, :Type => association.to_end.return_type, :Multiplicity => association.to_end.to_multiplicity)
|
33
|
+
xml.tag!(:ReferentialConstraint) do
|
34
|
+
xml.tag!(:Dependent, :Role => association.from_end.name) do
|
35
|
+
OData::ActiveRecordSchema::Association.column_names_for_from_end(association.reflection).each do |column_name|
|
36
|
+
xml.tag!(:PropertyRef, :Name => column_name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
xml.tag!(:Principal, :Role => association.to_end.name) do
|
40
|
+
OData::ActiveRecordSchema::Association.column_names_for_to_end(association.reflection).each do |column_name|
|
41
|
+
xml.tag!(:PropertyRef, :Name => column_name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
xml.tag!(:EntityContainer, :Name => ODataController.schema.namespace, "m:IsDefaultEntityContainer" => true) do
|
49
|
+
ODataController.schema.entity_types.sort_by(&:qualified_name).each do |entity_type|
|
50
|
+
xml.tag!(:EntitySet, :Name => entity_type.plural_name, :EntityType => entity_type.qualified_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
ODataController.schema.associations.sort_by(&:qualified_name).each do |association|
|
54
|
+
xml.tag!(:AssociationSet, :Name => association.name, :Association => association.qualified_name) do
|
55
|
+
xml.tag!(:End, :EntitySet => association.from_end.entity_type.plural_name, :Role => association.from_end.name)
|
56
|
+
xml.tag!(:End, :EntitySet => association.reflection.options[:polymorphic] ? association.to_end.return_type : association.to_end.entity_type.plural_name, :Role => association.to_end.name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
xml.instruct!
|
2
|
+
|
3
|
+
if @countable
|
4
|
+
o_data_atom_feed(xml, @query, @results, :expand => @expand_navigation_property_paths)
|
5
|
+
else
|
6
|
+
first_result = [@results].flatten.compact.first
|
7
|
+
o_data_atom_entry(xml, @query, first_result, :expand => @expand_navigation_property_paths)
|
8
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= if @countable then o_data_json_feed(@query, @results, :expand => @expand_navigation_property_paths).to_json else o_data_json_entry(@query, [@results].flatten.compact.first, :d => true, :expand => @expand_navigation_property_paths).to_json end %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
xml.instruct!
|
2
|
+
xml.tag!(:service, "xmlns:atom" => "http://www.w3.org/2005/Atom", "xmlns" => "http://www.w3.org/2007/app", "xml:base" => o_data_service_url) do
|
3
|
+
xml.tag!(:workspace) do
|
4
|
+
xml.atom(:title, ODataController.schema.namespace)
|
5
|
+
ODataController.schema.entity_types.collect(&:plural_name).sort.each do |plural_name|
|
6
|
+
xml.tag!(:collection, :href => plural_name) do
|
7
|
+
xml.atom(:title, plural_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
ActionController::Routing::Routes.draw do |map|
|
2
|
+
map.with_options(:controller => "o_data") do |o_data|
|
3
|
+
o_data.o_data_service "/OData/OData.svc", :action => "service"
|
4
|
+
o_data.o_data_metadata "/OData/OData.svc/$metadata", :action => "metadata"
|
5
|
+
o_data.o_data_resource "/OData/OData.svc/*#{ODataController.path_param.to_s}", :action => "resource"
|
6
|
+
|
7
|
+
o_data.connect "/OData", :action => "redirect_to_service"
|
8
|
+
o_data.connect "/OData/$metadata", :action => "redirect_to_metadata"
|
9
|
+
o_data.connect "/OData/*#{ODataController.path_param.to_s}", :action => "redirect_to_resource"
|
10
|
+
end
|
11
|
+
end
|
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "o_data/abstract_query/errors"
|
2
|
+
require "o_data/abstract_query/countable"
|
3
|
+
require "o_data/abstract_query/base"
|
4
|
+
require "o_data/abstract_query/segment"
|
5
|
+
require "o_data/abstract_query/option"
|
6
|
+
|
7
|
+
require "o_data/abstract_query/segments/entity_type_segment"
|
8
|
+
require "o_data/abstract_query/segments/entity_type_and_key_values_segment"
|
9
|
+
|
10
|
+
require "o_data/abstract_query/segments/collection_segment"
|
11
|
+
require "o_data/abstract_query/segments/navigation_property_segment"
|
12
|
+
require "o_data/abstract_query/segments/property_segment"
|
13
|
+
require "o_data/abstract_query/segments/value_segment"
|
14
|
+
require "o_data/abstract_query/segments/links_segment"
|
15
|
+
require "o_data/abstract_query/segments/count_segment"
|
16
|
+
|
17
|
+
require "o_data/abstract_query/options/enumerated_option"
|
18
|
+
|
19
|
+
require "o_data/abstract_query/options/format_option"
|
20
|
+
require "o_data/abstract_query/options/inlinecount_option"
|
21
|
+
require "o_data/abstract_query/options/top_option"
|
22
|
+
require "o_data/abstract_query/options/skip_option"
|
23
|
+
require "o_data/abstract_query/options/orderby_option"
|
24
|
+
require "o_data/abstract_query/options/select_option"
|
25
|
+
require "o_data/abstract_query/options/expand_option"
|
26
|
+
|
27
|
+
require "o_data/abstract_query/parser"
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module OData
|
2
|
+
module AbstractQuery
|
3
|
+
class Base
|
4
|
+
attr_reader :schema
|
5
|
+
attr_reader :segments, :options
|
6
|
+
|
7
|
+
def initialize(schema, segments = [], options = [])
|
8
|
+
@schema = schema
|
9
|
+
|
10
|
+
@segments = segments
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"#<< #{@schema.namespace.to_s}(#{self.to_uri.inspect}) >>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def Segment(*args)
|
19
|
+
segment_class = begin
|
20
|
+
if args.first.is_a?(Symbol) || args.first.is_a?(String)
|
21
|
+
"OData::AbstractQuery::Segments::#{args.shift.to_s}Segment".constantize
|
22
|
+
else
|
23
|
+
args.shift
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
segment = segment_class.new(self, *args)
|
28
|
+
|
29
|
+
@segments << segment
|
30
|
+
segment
|
31
|
+
end
|
32
|
+
|
33
|
+
def Option(*args)
|
34
|
+
option_class = begin
|
35
|
+
if args.first.is_a?(Symbol) || args.first.is_a?(String)
|
36
|
+
"OData::AbstractQuery::Options::#{args.shift.to_s}Option".constantize
|
37
|
+
else
|
38
|
+
args.shift
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
option = option_class.new(self, *args)
|
43
|
+
|
44
|
+
@options << option
|
45
|
+
option
|
46
|
+
end
|
47
|
+
|
48
|
+
def resource_path
|
49
|
+
@segments.collect(&:value).join('/')
|
50
|
+
end
|
51
|
+
|
52
|
+
def query_string
|
53
|
+
@options.collect { |o| "#{o.key.to_s}=#{o.value.to_s}" }.join('&')
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_uri
|
57
|
+
[resource_path, query_string].reject(&:blank?).join('?')
|
58
|
+
end
|
59
|
+
|
60
|
+
def execute!
|
61
|
+
_execute!
|
62
|
+
end
|
63
|
+
|
64
|
+
# def entity_type
|
65
|
+
# return nil if @segments.empty?
|
66
|
+
# return nil unless @segments.last.respond_to?(:entity_type)
|
67
|
+
# @segments.last.entity_type
|
68
|
+
# end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
def _execute!
|
73
|
+
_segments = [@segments].flatten.compact
|
74
|
+
results = __execute!([], nil, _segments.shift, _segments)
|
75
|
+
|
76
|
+
results = with_skip_and_top_options(with_orderby_option(results))
|
77
|
+
|
78
|
+
results
|
79
|
+
end
|
80
|
+
|
81
|
+
def __execute!(seen, acc, head, rest)
|
82
|
+
return acc if head.blank?
|
83
|
+
raise Errors::InvalidSegmentContext.new(self, head) unless seen.empty? || head.can_follow?(seen.last)
|
84
|
+
|
85
|
+
results = head.execute!(acc)
|
86
|
+
raise Errors::ExecutionOfSegmentFailedValidation.new(self, head) unless head.valid?(results)
|
87
|
+
|
88
|
+
seen << head
|
89
|
+
__execute!(seen, results, rest.shift, rest)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def with_orderby_option(results)
|
95
|
+
orderby_option = @options.find { |o| o.option_name == Options::OrderbyOption.option_name }
|
96
|
+
|
97
|
+
orderby = orderby_option.blank? ? nil : orderby_option.pairs
|
98
|
+
|
99
|
+
if orderby && (entity_type = orderby_option.entity_type)
|
100
|
+
results = entity_type.sort(results, orderby)
|
101
|
+
else
|
102
|
+
results
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def with_skip_and_top_options(results)
|
107
|
+
skip_option = @options.find { |o| o.option_name == Options::SkipOption.option_name }
|
108
|
+
top_option = @options.find { |o| o.option_name == Options::TopOption.option_name }
|
109
|
+
|
110
|
+
skip = skip_option.blank? ? nil : skip_option.value.to_i
|
111
|
+
top = top_option.blank? ? nil : top_option.value.to_i
|
112
|
+
|
113
|
+
if skip && top
|
114
|
+
results = results.slice(skip, top)
|
115
|
+
elsif skip
|
116
|
+
results = results.slice(skip..-1)
|
117
|
+
elsif top
|
118
|
+
results = results.slice(0, top)
|
119
|
+
else
|
120
|
+
results
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module AbstractSchema
|
127
|
+
class Base
|
128
|
+
def Query(*args)
|
129
|
+
OData::AbstractQuery::Base.new(self, *args)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module OData
|
2
|
+
module AbstractQuery
|
3
|
+
module Countable
|
4
|
+
def self.included(base)
|
5
|
+
base.send(:extend, SingletonMethods)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module SingletonMethods
|
10
|
+
def countable?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
def countable?
|
17
|
+
self.class.countable?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module OData
|
2
|
+
module AbstractQuery
|
3
|
+
module Errors
|
4
|
+
class AbstractQueryException < OData::ODataException
|
5
|
+
attr_reader :query
|
6
|
+
|
7
|
+
def initialize(query)
|
8
|
+
@query = query
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"An unknown error has occured."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ParseQuerySegmentException < AbstractQueryException
|
17
|
+
attr_reader :str
|
18
|
+
|
19
|
+
def initialize(query, str)
|
20
|
+
super(query)
|
21
|
+
@str = str
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"Resource not found for the segment '#{@str.to_s}'."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class EntityTypeNotFound < ParseQuerySegmentException
|
30
|
+
def to_s
|
31
|
+
"EntityType not found for the segment '#{self.str.to_s}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class PropertyNotFound < ParseQuerySegmentException
|
36
|
+
def to_s
|
37
|
+
"Property not found for the segment '#{self.str.to_s}'"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class NavigationPropertyNotFound < ParseQuerySegmentException
|
42
|
+
def to_s
|
43
|
+
"NavigationProperty not found for the segment '#{self.str.to_s}'"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class AbstractQuerySegmentException < AbstractQueryException
|
48
|
+
attr_reader :segment
|
49
|
+
|
50
|
+
def initialize(query, segment)
|
51
|
+
super(query)
|
52
|
+
|
53
|
+
@segment = segment
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ExecutionOfSegmentFailedValidation < AbstractQuerySegmentException
|
58
|
+
def to_s
|
59
|
+
"Execution of the segment '#{@segment.value}' did not return a valid result."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class InvalidSegmentContext < AbstractQuerySegmentException
|
64
|
+
def to_s
|
65
|
+
"Invalid context for the segment '#{@segment.value}'."
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class AbstractQueryKeyValueException < AbstractQueryException
|
70
|
+
attr_reader :key, :value
|
71
|
+
|
72
|
+
def initialize(query, key, value)
|
73
|
+
super(query)
|
74
|
+
|
75
|
+
@key = key
|
76
|
+
@value = value
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
"An unknown error has occured for the query option '#{@key.to_s}'."
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class InvalidReservedOptionName < AbstractQueryKeyValueException
|
85
|
+
def to_s
|
86
|
+
"The query parameter '#{self.key.to_s}' begins with a system-reserved '$' character but is not recognized."
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class AbstractQueryOptionException < AbstractQueryException
|
91
|
+
attr_reader :option
|
92
|
+
|
93
|
+
def initialize(query, option)
|
94
|
+
super(query)
|
95
|
+
|
96
|
+
@option = option
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
"Invalid '#{@option.option_name.to_s}' query option."
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class InvalidOptionContext < AbstractQueryOptionException
|
105
|
+
def to_s
|
106
|
+
"Invalid context for the query option '#{self.option.option_name.to_s}'."
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class InvalidOptionValue < AbstractQueryOptionException
|
111
|
+
def to_s
|
112
|
+
"Invalid value for '#{self.option.option_name.to_s}' query option#{" - the only acceptable values are #{self.option.valid_values.collect { |v| v.to_s.inspect }.to_sentence}" if self.option.respond_to?(:valid_values)}."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module OData
|
2
|
+
module AbstractQuery
|
3
|
+
class BasicOption
|
4
|
+
def self.option_name(option)
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def option_name
|
9
|
+
@key
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :query, :key, :value
|
13
|
+
|
14
|
+
def initialize(query, key, value = nil)
|
15
|
+
@query = query
|
16
|
+
@key = key
|
17
|
+
@value = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"#<< #{@query.schema.namespace.to_s}(#{@key.to_s}=#{@value.to_s}) >>"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Option < BasicOption
|
26
|
+
def self.option_name
|
27
|
+
name.to_s.demodulize.sub(/Option$/, '')
|
28
|
+
end
|
29
|
+
|
30
|
+
def option_name
|
31
|
+
self.class.option_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(query, key, value = nil)
|
35
|
+
super(query, key, value)
|
36
|
+
|
37
|
+
raise Errors::InvalidOptionContext.new(self.query, self) unless applies_to?
|
38
|
+
raise Errors::InvalidOptionValue.new(self.query, self) unless valid?
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.applies_to?(query)
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.parse!(query, key, value = nil)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def applies_to?
|
50
|
+
self.class.applies_to?(self.query)
|
51
|
+
end
|
52
|
+
|
53
|
+
def valid?
|
54
|
+
true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|