query-interface-server 0.1.3 → 0.1.4
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.
- checksums.yaml +4 -4
- data/lib/query-interface-server.rb +3 -0
- data/lib/query-interface-server/lazy_query.rb +120 -0
- data/lib/query-interface-server/resource.rb +4 -111
- data/lib/query-interface-server/transformations.rb +75 -0
- data/lib/query-interface-server/transformations/sequel_transformer.rb +111 -0
- data/lib/query-interface-server/version.rb +1 -1
- data/spec/lib/resource_spec.rb +50 -0
- metadata +7 -5
- data/spec/lib/query_interface_spec.rb +0 -312
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31d192d0acccf634f378134910fae9a2d0856aa2
|
4
|
+
data.tar.gz: d777536d9062e1dfd91017e6749ae92303bd4a21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f5c49b92e0edc9ab581facf8e74e3f7051e56bd6060fa427baf56378bc38bb23d12b1560b85eadd1cd2ba02a8699920d4b192966173e837c39d9571189fb8e7
|
7
|
+
data.tar.gz: 89a73932e4564bb2584de61f094934846dd524cb7f2579604fc812ecb13fe3dae53bc28989f180649bf3b7a4deb96be313c077d80cef75c5a962c06819c8fa66
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module QueryInterface
|
2
|
+
module Server
|
3
|
+
class LazyQuery
|
4
|
+
|
5
|
+
attr_accessor :model, :result_model, :transformations
|
6
|
+
|
7
|
+
def initialize(model, transformations=nil, result_model=nil)
|
8
|
+
self.model = model
|
9
|
+
if transformations
|
10
|
+
self.transformations = transformations.map {|item| item.dup}
|
11
|
+
else
|
12
|
+
self.transformations = []
|
13
|
+
end
|
14
|
+
self.result_model = result_model
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse(data)
|
18
|
+
(self.result_model ? self.result_model.parse(data) : self.model.parse(data))
|
19
|
+
end
|
20
|
+
|
21
|
+
def instantiate(data)
|
22
|
+
(self.result_model ? self.result_model.new(data) : self.model.new(data))
|
23
|
+
end
|
24
|
+
|
25
|
+
def instantiate_collection(parsed_data)
|
26
|
+
(self.result_model ? self.result_model.new_collection(parsed_data) : self.model.new_collection(parsed_data))
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy(options = {})
|
30
|
+
self.class.new(self.model, self.transformations, self.result_model)
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_transformation(type, parameter=nil)
|
34
|
+
self.transformations << {'transformation' => type, 'parameter' => parameter}
|
35
|
+
end
|
36
|
+
|
37
|
+
def filter(conditions={})
|
38
|
+
self.copy.tap do |query|
|
39
|
+
conditions.each do |key, value|
|
40
|
+
query.add_transformation("filter", {"field" => key, "value" => value})
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def instance(id)
|
46
|
+
self.copy.tap do |query|
|
47
|
+
query.add_transformation("instance", id)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def context(association, model=nil)
|
52
|
+
self.copy.tap do |query|
|
53
|
+
query.result_model = (model ? model : association.to_s.singularize.camelize.constantize)
|
54
|
+
query.add_transformation("context", association.to_s)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def with(*fields)
|
59
|
+
self.copy.tap do |query|
|
60
|
+
fields.each do |field|
|
61
|
+
query.add_transformation("with", field.to_s)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def order(*fields)
|
67
|
+
self.copy.tap do |query|
|
68
|
+
fields.each do |field|
|
69
|
+
query.add_transformation("order", field)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def evaluate
|
75
|
+
self.do_query
|
76
|
+
end
|
77
|
+
|
78
|
+
def ids
|
79
|
+
query = self.copy
|
80
|
+
query.add_transformation("map_ids")
|
81
|
+
query.do_query
|
82
|
+
end
|
83
|
+
|
84
|
+
def count
|
85
|
+
query = self.copy
|
86
|
+
query.add_transformation("count")
|
87
|
+
query.do_query
|
88
|
+
end
|
89
|
+
|
90
|
+
def do_query
|
91
|
+
transformer = Transformations::SequelTransformer.new(self.model.filter)
|
92
|
+
transformer.run(self.transformations)
|
93
|
+
end
|
94
|
+
|
95
|
+
def first(*args)
|
96
|
+
one("first", *args)
|
97
|
+
end
|
98
|
+
|
99
|
+
def last(*args)
|
100
|
+
one("last", *args)
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_json(*args)
|
104
|
+
evaluate.to_json(*args)
|
105
|
+
end
|
106
|
+
|
107
|
+
def method_missing(method_name, *args, &block)
|
108
|
+
evaluate.send(method_name, *args, &block)
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def one(which, params = {})
|
114
|
+
query = self.copy
|
115
|
+
query.add_transformation(which)
|
116
|
+
query.do_query
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -11,121 +11,14 @@ module QueryInterface
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
class UnknownTransformation < Exception; end
|
15
|
-
class InvalidContextException < Exception; end
|
16
|
-
|
17
14
|
module InstanceMethods
|
18
|
-
|
19
|
-
attr_writer :context_type
|
20
|
-
attr_accessor :context
|
21
|
-
|
22
|
-
def validate_transformation(transformation)
|
23
|
-
methods = {
|
24
|
-
filter: {method: :filter_transformation, result: :dataset, accepts: :dataset},
|
25
|
-
with: {method: :with_transformation, result: :dataset, accepts: :dataset},
|
26
|
-
order: {method: :order_transformation, result: :dataset, accepts: :dataset},
|
27
|
-
instance: {method: :instance_transformation, result: :instance, accepts: :dataset},
|
28
|
-
context: {method: :context_transformation, result: :dataset, accepts: :instance},
|
29
|
-
map_ids: {method: :map_ids, result: :ids, accepts: :dataset},
|
30
|
-
paginate: {method: :paginate, result: :page, accepts: :dataset},
|
31
|
-
first: {method: :first, result: :instance, accepts: :dataset},
|
32
|
-
last: {method: :last, result: :instance, accepts: :dataset},
|
33
|
-
count: {method: :count, result: :count, accepts: :dataset}
|
34
|
-
}
|
35
|
-
argument_method = transformation['transformation'].to_sym
|
36
|
-
raise UnknownTransformation, "#{argument_method}" unless methods.has_key?(argument_method)
|
37
|
-
param = transformation['parameter']
|
38
|
-
description = methods[argument_method]
|
39
|
-
raise InvalidContextException, "expected: #{description[:accepts]}, got: #{self.context_type}" unless self.context_type == description[:accepts]
|
40
|
-
return [description[:method], param, description[:result]]
|
41
|
-
end
|
42
|
-
|
43
|
-
def context_type
|
44
|
-
@context_type ||= :dataset
|
45
|
-
end
|
46
|
-
|
47
|
-
def id_selector
|
48
|
-
primary_key = self.context.model.primary_key ? self.context.model.primary_key : :id
|
49
|
-
"#{self.context.model.table_name}__#{primary_key}".to_sym
|
50
|
-
end
|
51
|
-
|
52
|
-
def query_send(method_name, *args)
|
53
|
-
if respond_to?(method_name)
|
54
|
-
send(method_name, *args)
|
55
|
-
elsif self.context.respond_to?(method_name)
|
56
|
-
self.context.send(method_name, *args)
|
57
|
-
elsif block_given?
|
58
|
-
yield(*args)
|
59
|
-
else
|
60
|
-
raise Exception, "method #{method_name} not found"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def default_order
|
65
|
-
order_transformation(self.id_selector.to_s)
|
66
|
-
end
|
67
|
-
|
68
|
-
def filter_transformation(param)
|
69
|
-
self.query_send("filter_#{param['field']}", param['value'])
|
70
|
-
end
|
71
|
-
|
72
|
-
def with_transformation(param)
|
73
|
-
self.query_send("with_#{param}")
|
74
|
-
end
|
75
|
-
|
76
|
-
def order_transformation(param)
|
77
|
-
field = param
|
78
|
-
direction = :asc
|
79
|
-
if field.starts_with?('-')
|
80
|
-
field = field[1..-1]
|
81
|
-
direction = :desc
|
82
|
-
end
|
83
|
-
query_send("order_#{field}_#{direction}") do
|
84
|
-
self.context.order_append(Sequel.send(direction, field.to_sym))
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def instance_transformation(param)
|
89
|
-
self.context.filter(self.id_selector => param).first
|
90
|
-
end
|
91
|
-
|
92
|
-
def context_transformation(param)
|
93
|
-
context = query_send("#{param}_dataset")
|
94
|
-
context.select_all(context.model.table_name)
|
95
|
-
end
|
96
|
-
|
97
|
-
def map_ids(param)
|
98
|
-
default_order.select(self.id_selector).map(&:id)
|
99
|
-
end
|
100
|
-
|
101
|
-
def count(param)
|
102
|
-
{count: self.context.count}
|
103
|
-
end
|
104
|
-
|
105
|
-
def paginate(param)
|
106
|
-
context = default_order
|
107
|
-
{total: context.count, objects: context.paginate(param['page'].to_i, param['per_page'].to_i)}
|
108
|
-
end
|
109
|
-
|
110
|
-
def first(param)
|
111
|
-
default_order.first
|
112
|
-
end
|
113
|
-
|
114
|
-
def last(param)
|
115
|
-
default_order.last
|
116
|
-
end
|
117
|
-
|
118
15
|
def query
|
119
|
-
|
16
|
+
transformer = Transformations::SequelTransformer.new(self.query_model.filter)
|
120
17
|
transformations = params[:transformations] || []
|
121
18
|
transformations = JSON.parse(transformations) unless transformations.is_a?(Array)
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
126
|
-
self.context = self.default_order if self.context_type == :dataset
|
127
|
-
unless self.context.nil?
|
128
|
-
respond_with(self.context)
|
19
|
+
result = transformer.run(transformations)
|
20
|
+
unless result.nil?
|
21
|
+
respond_with(result)
|
129
22
|
else
|
130
23
|
render text: "", status: :not_found
|
131
24
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module QueryInterface
|
2
|
+
module Server
|
3
|
+
module Transformations
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
base.dataset_module do
|
8
|
+
def query_transformation(type, name, *args)
|
9
|
+
block = model.instance_variable_get(:@query_transformations)[type][name]
|
10
|
+
if block
|
11
|
+
self.instance_exec(*args, &block)
|
12
|
+
elsif type == :order and args.count == 1
|
13
|
+
self.order_append(Sequel.send(args[0], name.to_sym))
|
14
|
+
else
|
15
|
+
raise "No #{type} implementation for #{name}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
base.instance_variable_set(:@query_transformations, {
|
20
|
+
filter: {},
|
21
|
+
with: {},
|
22
|
+
order: {},
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def query_transformations(&block)
|
28
|
+
scope = TransformationScope.new
|
29
|
+
scope.instance_eval(&block)
|
30
|
+
|
31
|
+
@query_transformations[:filter].merge!(scope.filters)
|
32
|
+
@query_transformations[:with].merge!(scope.withs)
|
33
|
+
@query_transformations[:order].merge!(scope.orders)
|
34
|
+
end
|
35
|
+
|
36
|
+
def query
|
37
|
+
return QueryInterface::Server::LazyQuery.new(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class TransformationScope
|
43
|
+
attr_accessor :filters, :withs, :orders
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
self.filters = {}
|
47
|
+
self.withs = {}
|
48
|
+
self.orders = {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def auto_filter(*names)
|
52
|
+
names.each do |name|
|
53
|
+
self.filters[name] = Proc.new do |param|
|
54
|
+
field = "#{model.table_name}__#{name}".to_sym
|
55
|
+
filter(field => param)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def filter(name, &block)
|
61
|
+
self.filters[name] = block
|
62
|
+
end
|
63
|
+
|
64
|
+
def with(name, &block)
|
65
|
+
self.withs[name] = block
|
66
|
+
end
|
67
|
+
|
68
|
+
def order(name, &block)
|
69
|
+
self.orders[name] = block
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module QueryInterface
|
2
|
+
module Server
|
3
|
+
module Transformations
|
4
|
+
class SequelTransformer
|
5
|
+
attr_accessor :model, :result
|
6
|
+
|
7
|
+
def initialize(dataset)
|
8
|
+
self.result = dataset.select_all(dataset.model.table_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(transformations)
|
12
|
+
transformations.each do |transformation|
|
13
|
+
method, param = self.validate(transformation)
|
14
|
+
self.result = self.send(method, param)
|
15
|
+
end
|
16
|
+
(self.result_type == :collection ? self.default_order : self.result)
|
17
|
+
end
|
18
|
+
|
19
|
+
def result_type
|
20
|
+
if self.result.is_a?(Sequel::Dataset)
|
21
|
+
:collection
|
22
|
+
elsif self.result.is_a?(Sequel::Model)
|
23
|
+
:instance
|
24
|
+
else
|
25
|
+
:atomic
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate(transformation)
|
30
|
+
contraints = {
|
31
|
+
filter: :collection, with: :collection,order: :collection,
|
32
|
+
instance: :collection, context: :instance, map_ids: :collection,
|
33
|
+
paginate: :collection, first: :collection, last: :collection, count: :collection
|
34
|
+
}
|
35
|
+
method = transformation['transformation'].to_sym
|
36
|
+
raise UnknownTransformation, "#{method}" unless contraints.has_key?(method)
|
37
|
+
param = transformation['parameter']
|
38
|
+
unless self.result_type == contraints[method]
|
39
|
+
raise InvalidContextException, "expected: #{description[:accepts]}, got: #{self.result_type}"
|
40
|
+
end
|
41
|
+
return [method, param]
|
42
|
+
end
|
43
|
+
|
44
|
+
def table
|
45
|
+
self.result.model.table_name
|
46
|
+
end
|
47
|
+
|
48
|
+
def default_order
|
49
|
+
self.order(self.result.model.primary_key.to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
def id_selector
|
53
|
+
primary_key = self.result.model.primary_key ? self.result.model.primary_key : :id
|
54
|
+
"#{self.result.model.table_name}__#{primary_key}".to_sym
|
55
|
+
end
|
56
|
+
|
57
|
+
def filter(param)
|
58
|
+
self.result.query_transformation(:filter, param['field'].to_sym, param['value'])
|
59
|
+
end
|
60
|
+
|
61
|
+
def with(param)
|
62
|
+
self.result.query_transformation(:with, param.to_sym)
|
63
|
+
end
|
64
|
+
|
65
|
+
def order(param)
|
66
|
+
direction = :asc
|
67
|
+
if param.starts_with?('-')
|
68
|
+
param = param[1..-1]
|
69
|
+
direction = :desc
|
70
|
+
end
|
71
|
+
self.result.query_transformation(:order, param.to_sym, direction)
|
72
|
+
end
|
73
|
+
|
74
|
+
def instance(param)
|
75
|
+
self.result.filter(self.id_selector => param).first
|
76
|
+
end
|
77
|
+
|
78
|
+
def context(param)
|
79
|
+
name = "#{param}_dataset"
|
80
|
+
if self.result.respond_to?(name)
|
81
|
+
result = self.result.send(name)
|
82
|
+
else
|
83
|
+
raise Exception, "method #{name} not found"
|
84
|
+
end
|
85
|
+
result.select_all(result.model.table_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def map_ids(param)
|
89
|
+
self.default_order.select(self.id_selector).map(&:id)
|
90
|
+
end
|
91
|
+
|
92
|
+
def count(param)
|
93
|
+
{count: self.result.count}
|
94
|
+
end
|
95
|
+
|
96
|
+
def paginate(param)
|
97
|
+
context = self.default_order
|
98
|
+
{total: context.count, objects: context.paginate(param['page'].to_i, param['per_page'].to_i)}
|
99
|
+
end
|
100
|
+
|
101
|
+
def first(param)
|
102
|
+
self.default_order.first
|
103
|
+
end
|
104
|
+
|
105
|
+
def last(param)
|
106
|
+
self.default_order.last
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe QueryInterface::Server::Resource, type: :controller do
|
4
|
+
|
5
|
+
class Foo
|
6
|
+
end
|
7
|
+
|
8
|
+
controller(ActionController::Base) do
|
9
|
+
respond_to :json
|
10
|
+
include QueryInterface::Server::Resource.for(Foo)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'self.for' do
|
14
|
+
it 'defines a query_model method' do
|
15
|
+
subject.query_model.should eq(Foo)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'includes the instance methods' do
|
19
|
+
subject.should respond_to(:query)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "GET 'query'" do
|
24
|
+
before(:each) do
|
25
|
+
routes.draw { get "query" => "anonymous#query" }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'runs the transformations' do
|
29
|
+
dataset = double('dataset')
|
30
|
+
transformer = double('transformer')
|
31
|
+
result = double('result')
|
32
|
+
Foo.should_receive(:filter).and_return(dataset)
|
33
|
+
QueryInterface::Server::Transformations::SequelTransformer.should_receive(:new).with(dataset).and_return(transformer)
|
34
|
+
transformer.should_receive(:run).with([{'my' => 'transformations'}]).and_return(result)
|
35
|
+
get :query, transformations: [{'my' => 'transformations'}], format: :json
|
36
|
+
response.should be_success
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns 404 when result is nil' do
|
40
|
+
dataset = double('dataset')
|
41
|
+
transformer = double('transformer')
|
42
|
+
Foo.should_receive(:filter).and_return(dataset)
|
43
|
+
QueryInterface::Server::Transformations::SequelTransformer.should_receive(:new).with(dataset).and_return(transformer)
|
44
|
+
transformer.should_receive(:run).with([{'my' => 'transformations'}]).and_return(nil)
|
45
|
+
get :query, transformations: [{'my' => 'transformations'}], format: :json
|
46
|
+
response.should be_not_found
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: query-interface-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Kopecky <andreas.kopecky@radarservices.com>
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2014-01-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: sequel
|
@@ -97,10 +97,13 @@ files:
|
|
97
97
|
- README.md
|
98
98
|
- Rakefile
|
99
99
|
- lib/query-interface-server.rb
|
100
|
+
- lib/query-interface-server/lazy_query.rb
|
100
101
|
- lib/query-interface-server/resource.rb
|
102
|
+
- lib/query-interface-server/transformations.rb
|
103
|
+
- lib/query-interface-server/transformations/sequel_transformer.rb
|
101
104
|
- lib/query-interface-server/version.rb
|
102
105
|
- query-interface-server.gemspec
|
103
|
-
- spec/lib/
|
106
|
+
- spec/lib/resource_spec.rb
|
104
107
|
- spec/spec_helper.rb
|
105
108
|
homepage: http://github.com/rs-dev/query-interface-server
|
106
109
|
licenses:
|
@@ -122,9 +125,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
125
|
version: '0'
|
123
126
|
requirements: []
|
124
127
|
rubyforge_project:
|
125
|
-
rubygems_version: 2.
|
128
|
+
rubygems_version: 2.1.11
|
126
129
|
signing_key:
|
127
130
|
specification_version: 4
|
128
131
|
summary: Server for the radar query interface
|
129
132
|
test_files: []
|
130
|
-
has_rdoc:
|
@@ -1,312 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe QueryInterface, type: :controller do
|
4
|
-
class Foo
|
5
|
-
def self.table_name
|
6
|
-
:foo_table
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class Nested
|
11
|
-
def self.table_name
|
12
|
-
:nested_table
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
controller(ActionController::Base) do
|
18
|
-
respond_to :json
|
19
|
-
include QueryInterface::Server::Resource.for(Foo)
|
20
|
-
end
|
21
|
-
let(:foo){ double("Foo instance").as_null_object }
|
22
|
-
let(:foos){ double('foo instances', to_json: '{}', model: Foo) }
|
23
|
-
|
24
|
-
it "provides query_model as an instance method in the base class" do
|
25
|
-
controller.query_model.should == Foo
|
26
|
-
end
|
27
|
-
|
28
|
-
context "GET 'query'" do
|
29
|
-
|
30
|
-
context "transformations" do
|
31
|
-
it "only allows for a predefined list of transformations" do
|
32
|
-
expect {
|
33
|
-
controller.validate_transformation({'transformation' => 'bogus', 'parameter' => nil})
|
34
|
-
}.to raise_error("bogus")
|
35
|
-
controller.validate_transformation({'transformation' => 'with', 'parameter' => nil}).should eq([:with_transformation, nil, :dataset])
|
36
|
-
end
|
37
|
-
|
38
|
-
it "only allows transformations who accept the current content_type to run" do
|
39
|
-
controller.context_type = :instance
|
40
|
-
expect {
|
41
|
-
controller.validate_transformation({'transformation' => 'with', 'parameter' => 'y'})
|
42
|
-
}.to raise_error("expected: dataset, got: instance")
|
43
|
-
end
|
44
|
-
|
45
|
-
context "id selector" do
|
46
|
-
it "creates the id selector qualified from table name and primary key" do
|
47
|
-
controller.context = double('context', model: double('model', table_name: :table, primary_key: :id))
|
48
|
-
controller.id_selector.should == :table__id
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context "context accessor" do
|
53
|
-
it "returns the existing context" do
|
54
|
-
context = double("context")
|
55
|
-
controller.instance_variable_set(:@context, context)
|
56
|
-
controller.context.should eq(context)
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
context "transformation methods" do
|
64
|
-
let(:context) {double("context")}
|
65
|
-
|
66
|
-
it "performs a filter transformation" do
|
67
|
-
controller.should_receive(:query_send).with('filter_a', 'b').and_return(context)
|
68
|
-
controller.filter_transformation({'field' => 'a', 'value' => 'b'}).should eq(context)
|
69
|
-
end
|
70
|
-
|
71
|
-
it "performs a with transformation" do
|
72
|
-
controller.should_receive(:query_send).with('with_x').and_return(context)
|
73
|
-
controller.with_transformation('x').should eq(context)
|
74
|
-
end
|
75
|
-
|
76
|
-
it "performs a instance transformation on an existing context" do
|
77
|
-
id_selector = :id_selector
|
78
|
-
controller.context = context
|
79
|
-
controller.should_receive(:id_selector).and_return(id_selector)
|
80
|
-
context.should_receive(:filter).with({id_selector => 42}).and_return(context)
|
81
|
-
context.should_receive(:first).and_return(context)
|
82
|
-
controller.instance_transformation(42).should eq(context)
|
83
|
-
end
|
84
|
-
|
85
|
-
it "performs a context transformation" do
|
86
|
-
controller.should_receive(:query_send).with('context_dataset').and_return(context)
|
87
|
-
context.should_receive(:model).and_return(double("model", table_name: :foo_table))
|
88
|
-
context.should_receive(:select_all).with(:foo_table).and_return(context)
|
89
|
-
controller.context_transformation('context').should eq(context)
|
90
|
-
end
|
91
|
-
|
92
|
-
context "order transformation" do
|
93
|
-
it "should call query_send with order_*_* for the supplied order field" do
|
94
|
-
ordered = double("ordered a")
|
95
|
-
controller.should_receive(:query_send).with("order_a_asc").and_return(ordered)
|
96
|
-
controller.order_transformation("a").should eq(ordered)
|
97
|
-
end
|
98
|
-
|
99
|
-
it "performs descending ordering if appropriate" do
|
100
|
-
ordered = double("ordered a")
|
101
|
-
controller.should_receive(:query_send).with("order_a_desc").and_return(ordered)
|
102
|
-
controller.order_transformation("-a").should eq(ordered)
|
103
|
-
end
|
104
|
-
|
105
|
-
it "performs a fallback sequel ordering operation when no concrete implementation is available" do
|
106
|
-
context = double("context")
|
107
|
-
ordered = double("ordered a")
|
108
|
-
controller.instance_variable_set(:@context, context)
|
109
|
-
context.should_receive(:order_append).with(Sequel.asc(:a)).and_return(ordered)
|
110
|
-
controller.order_transformation("a").should eq(ordered)
|
111
|
-
end
|
112
|
-
|
113
|
-
it "performs fallback for descending ordering" do
|
114
|
-
context = double("context")
|
115
|
-
ordered = double("ordered a")
|
116
|
-
controller.instance_variable_set(:@context, context)
|
117
|
-
context.should_receive(:order_append).with(Sequel.desc(:a)).and_return(ordered)
|
118
|
-
controller.order_transformation("-a").should eq(ordered)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
context "query" do
|
125
|
-
before(:each) do
|
126
|
-
routes.draw { get "query" => "anonymous#query" }
|
127
|
-
Foo.stub(:filter).and_return(foos)
|
128
|
-
Foo.stub(:primary_key).and_return(:id)
|
129
|
-
controller.stub(:order_query)
|
130
|
-
foos.stub(:select_all).with(:foo_table).and_return(foos)
|
131
|
-
end
|
132
|
-
|
133
|
-
|
134
|
-
context "transformation processing" do
|
135
|
-
let(:filtered) { double('filtered').as_null_object }
|
136
|
-
|
137
|
-
it "calls the corresponding methods in any transformation" do
|
138
|
-
controller.should_receive(:filter_foo).with('bar').and_return(filtered)
|
139
|
-
get :query,
|
140
|
-
transformations: [
|
141
|
-
{transformation: :filter, parameter: {field: :foo, value: 'bar'}},
|
142
|
-
], format: :json
|
143
|
-
assigns(:context).should == filtered
|
144
|
-
response.should be_ok
|
145
|
-
end
|
146
|
-
|
147
|
-
it "creates a new context if none is present" do
|
148
|
-
context = double("context")
|
149
|
-
controller.query_model.should_receive(:filter).and_return(foos)
|
150
|
-
controller.stub(:default_order).and_return(context)
|
151
|
-
foos.should_receive(:select_all).with(:foo_table).and_return(context)
|
152
|
-
get :query, transformations: [], format: :json
|
153
|
-
controller.context.should eq(context)
|
154
|
-
assigns(:context).should eq(context)
|
155
|
-
response.should be_ok
|
156
|
-
end
|
157
|
-
|
158
|
-
it "should respond with 404 when no result can be reached" do
|
159
|
-
context = double("context")
|
160
|
-
controller.query_model.should_receive(:filter).and_return(foos)
|
161
|
-
controller.stub(:default_order).and_return(nil)
|
162
|
-
foos.should_receive(:select_all).with(:foo_table).and_return(nil)
|
163
|
-
get :query, transformations: [], format: :json
|
164
|
-
controller.context.should eq(nil)
|
165
|
-
assigns(:context).should eq(nil)
|
166
|
-
response.should_not be_ok
|
167
|
-
response.status.should eq(404)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
|
172
|
-
context "end points" do
|
173
|
-
|
174
|
-
it "should return a count of instances when an 'count' transformation is given" do
|
175
|
-
foos.should_receive(:count).and_return(42)
|
176
|
-
get :query,
|
177
|
-
transformations: [
|
178
|
-
{transformation: :count, parameter: nil}
|
179
|
-
], format: :json
|
180
|
-
response.should be_ok
|
181
|
-
assigns(:context).should == {count: 42}
|
182
|
-
end
|
183
|
-
|
184
|
-
it "should return a hash with objects and total when a 'pagination' transformation is given" do
|
185
|
-
controller.should_receive(:default_order).and_return(foos)
|
186
|
-
foos.should_receive(:count).and_return(42)
|
187
|
-
foos.should_receive(:paginate).with(1, 10).and_return('paginated')
|
188
|
-
get :query,
|
189
|
-
transformations: [
|
190
|
-
{transformation: :paginate, parameter: {page: 1, per_page: 10}}
|
191
|
-
], format: :json
|
192
|
-
response.should be_ok
|
193
|
-
assigns(:context).should == {objects: 'paginated', total: 42}
|
194
|
-
end
|
195
|
-
|
196
|
-
it "should return a single object when a 'first' transformation is given" do
|
197
|
-
controller.should_receive(:default_order).and_return(foos)
|
198
|
-
foos.should_receive(:first).and_return('first object')
|
199
|
-
get :query,
|
200
|
-
transformations: [
|
201
|
-
{transformation: :first, parameter: nil}
|
202
|
-
], format: :json
|
203
|
-
assigns(:context).should == 'first object'
|
204
|
-
end
|
205
|
-
|
206
|
-
it "should return a single object when a 'last' transformation is given" do
|
207
|
-
controller.should_receive(:default_order).and_return(foos)
|
208
|
-
foos.should_receive(:last).and_return('last object')
|
209
|
-
get :query,
|
210
|
-
transformations: [
|
211
|
-
{transformation: :last, parameter: nil}
|
212
|
-
], format: :json
|
213
|
-
assigns(:context).should == 'last object'
|
214
|
-
end
|
215
|
-
|
216
|
-
it "should return a list of ids when a 'map_ids' transformation is given" do
|
217
|
-
controller.should_receive(:default_order).and_return(foos)
|
218
|
-
foos.should_receive(:select).with(:foo_table__id).and_return([double("one", id: 1), double("two", id: 2)])
|
219
|
-
get :query,
|
220
|
-
transformations: [
|
221
|
-
{transformation: :map_ids, parameter: nil}
|
222
|
-
], format: :json
|
223
|
-
assigns(:context).should == [1, 2]
|
224
|
-
end
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
context "instance calls" do
|
232
|
-
before(:each) do
|
233
|
-
routes.draw { get "query" => "anonymous#query" }
|
234
|
-
Foo.stub(:filter).and_return(foos)
|
235
|
-
Foo.stub(:table_name).and_return(:foo_table)
|
236
|
-
controller.stub(:order_query)
|
237
|
-
end
|
238
|
-
|
239
|
-
context "instance evaluation" do
|
240
|
-
|
241
|
-
it "queries for the single object supplied by instance" do
|
242
|
-
foos.should_receive(:select_all).with(:foo_table).and_return(foos)
|
243
|
-
id_selector = double("id selector")
|
244
|
-
controller.should_receive(:id_selector).and_return(id_selector)
|
245
|
-
foos.should_receive(:filter).with(id_selector => 99).and_return(foos)
|
246
|
-
foos.should_receive(:first).and_return(foo)
|
247
|
-
get :query, transformations:
|
248
|
-
[
|
249
|
-
{transformation: :instance, parameter: 99},
|
250
|
-
], format: :json
|
251
|
-
response.should be_ok
|
252
|
-
assigns(:context).should eq(foo)
|
253
|
-
end
|
254
|
-
|
255
|
-
it "assigns an association dataset if a context is supplied" do
|
256
|
-
foos.should_receive(:select_all).with(:foo_table).and_return(foos)
|
257
|
-
id_selector = double("id selector")
|
258
|
-
nested = double("nested things", model: Nested)
|
259
|
-
controller.should_receive(:id_selector).and_return(id_selector)
|
260
|
-
foos.should_receive(:filter).with(id_selector => 99).and_return(foos)
|
261
|
-
foos.should_receive(:first).and_return(foo)
|
262
|
-
controller.should_receive(:query_send).with("nested_dataset").and_return(nested)
|
263
|
-
nested.should_receive(:select_all).with(:nested_table).and_return(nested)
|
264
|
-
controller.should_receive(:default_order).and_return(nested)
|
265
|
-
get :query, transformations:
|
266
|
-
[
|
267
|
-
{transformation: :instance, parameter: 99},
|
268
|
-
{transformation: :context, parameter: 'nested'},
|
269
|
-
], format: :json
|
270
|
-
assigns(:context).should eq(nested)
|
271
|
-
response.should be_ok
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
context "query send" do
|
277
|
-
let(:send_result) {double("send result")}
|
278
|
-
|
279
|
-
before(:each) do
|
280
|
-
@context = double("context")
|
281
|
-
controller.instance_variable_set(:@context, @context)
|
282
|
-
end
|
283
|
-
|
284
|
-
it "should call the supplied method name on the controller if it responds to such a message" do
|
285
|
-
controller.should_receive(:respond_to?).with("method_name_foo").and_return(true)
|
286
|
-
controller.should_receive(:send).with("method_name_foo", "argument").and_return(send_result)
|
287
|
-
controller.query_send("method_name_foo", "argument").should eq(send_result)
|
288
|
-
end
|
289
|
-
|
290
|
-
it "should call the supplied method name on @context if avaiable and controller doesnt provide the method" do
|
291
|
-
controller.should_receive(:respond_to?).with("method_name_foo").and_return(false)
|
292
|
-
@context.should_receive(:respond_to?).with("method_name_foo").and_return(true)
|
293
|
-
@context.should_receive(:send).with("method_name_foo", "argument").and_return(send_result)
|
294
|
-
controller.query_send("method_name_foo", "argument").should eq(send_result)
|
295
|
-
end
|
296
|
-
|
297
|
-
it "should yield arguments to a supplied block if neither context nor controller provide the requested method" do
|
298
|
-
controller.should_receive(:respond_to?).with("method_name_foo").and_return(false)
|
299
|
-
@context.should_receive(:respond_to?).with("method_name_foo").and_return(false)
|
300
|
-
controller.query_send("method_name_foo", "argument") {|*args| args[0].should eq("argument")}
|
301
|
-
end
|
302
|
-
|
303
|
-
it "should raise an exception if non of the above can be done" do
|
304
|
-
controller.should_receive(:respond_to?).with("method_name_foo").and_return(false)
|
305
|
-
@context.should_receive(:respond_to?).with("method_name_foo").and_return(false)
|
306
|
-
expect {
|
307
|
-
controller.query_send("method_name_foo", "argument")
|
308
|
-
}.to raise_error(Exception, "method method_name_foo not found")
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
end
|