appfuel 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/appfuel.gemspec +1 -1
- data/lib/appfuel/application/app_container.rb +0 -1
- data/lib/appfuel/application/dispatcher.rb +32 -0
- data/lib/appfuel/application/root.rb +4 -4
- data/lib/appfuel/application.rb +2 -1
- data/lib/appfuel/domain/domain_name_parser.rb +9 -5
- data/lib/appfuel/domain.rb +0 -7
- data/lib/appfuel/handler/base.rb +8 -14
- data/lib/appfuel/storage/db/mapper.rb +31 -35
- data/lib/appfuel/storage/db/repository.rb +53 -121
- data/lib/appfuel/storage/repository/base.rb +246 -0
- data/lib/appfuel/storage/repository/criteria.rb +317 -0
- data/lib/appfuel/{domain → storage/repository}/expr.rb +1 -1
- data/lib/appfuel/storage/repository/expr_conjunction.rb +41 -0
- data/lib/appfuel/{domain → storage/repository}/expr_parser.rb +10 -22
- data/lib/appfuel/{domain → storage/repository}/expr_transform.rb +7 -7
- data/lib/appfuel/{repository → storage/repository}/mapper.rb +8 -3
- data/lib/appfuel/storage/repository/order_expr.rb +51 -0
- data/lib/appfuel/storage/repository/runner.rb +62 -0
- data/lib/appfuel/storage/repository/search_parser.rb +50 -0
- data/lib/appfuel/storage/repository/search_transform.rb +58 -0
- data/lib/appfuel/{domain/criteria_settings.rb → storage/repository/settings.rb} +20 -5
- data/lib/appfuel/{repository.rb → storage/repository.rb} +13 -0
- data/lib/appfuel/storage.rb +1 -0
- data/lib/appfuel/version.rb +1 -1
- data/lib/appfuel.rb +0 -2
- metadata +21 -19
- data/lib/appfuel/domain/base_criteria.rb +0 -171
- data/lib/appfuel/domain/exists_criteria.rb +0 -57
- data/lib/appfuel/domain/expr_conjunction.rb +0 -27
- data/lib/appfuel/domain/search_criteria.rb +0 -137
- data/lib/appfuel/repository/base.rb +0 -86
- data/lib/appfuel/repository_runner.rb +0 -60
- /data/lib/appfuel/{repository → storage/repository}/initializer.rb +0 -0
- /data/lib/appfuel/{repository → storage/repository}/mapping_dsl.rb +0 -0
- /data/lib/appfuel/{repository → storage/repository}/mapping_entry.rb +0 -0
@@ -1,7 +1,5 @@
|
|
1
|
-
require 'parslet'
|
2
|
-
|
3
1
|
module Appfuel
|
4
|
-
module
|
2
|
+
module Repository
|
5
3
|
# A PEG (Parser Expression Grammer) transformer for our domain language.
|
6
4
|
#
|
7
5
|
class ExprTransform < Parslet::Transform
|
@@ -41,13 +39,15 @@ module Appfuel
|
|
41
39
|
end
|
42
40
|
|
43
41
|
def self.build_conjunction_node(data)
|
44
|
-
if data.
|
42
|
+
if data.is_a?(Expr) || data.is_a?(ExprConjunction)
|
43
|
+
node = data
|
44
|
+
elsif data.key?(:root)
|
45
45
|
node = data[:root]
|
46
46
|
elsif data.key?(:domain_expr)
|
47
|
-
node
|
47
|
+
node = data[:domain_expr]
|
48
48
|
elsif data.key?(:and) || data.key?(:or)
|
49
|
-
op =
|
50
|
-
node = build_conjunction(op, data)
|
49
|
+
op = data.key?(:and) ? :and : :or
|
50
|
+
node = build_conjunction(op, data[op])
|
51
51
|
end
|
52
52
|
node
|
53
53
|
end
|
@@ -138,13 +138,14 @@ module Appfuel
|
|
138
138
|
app_container[key]
|
139
139
|
end
|
140
140
|
|
141
|
-
def to_entity_hash(domain_name,
|
141
|
+
def to_entity_hash(domain_name, storage)
|
142
142
|
entity_attrs = {}
|
143
|
+
storage_data = storage_hash(storage)
|
143
144
|
each_entity_attr(domain_name) do |entry|
|
144
145
|
attr_name = entry.storage_attr
|
145
146
|
domain_attr = entry.domain_attr
|
146
|
-
next unless
|
147
|
-
update_entity_hash(domain_attr,
|
147
|
+
next unless storage_data.key?(attr_name)
|
148
|
+
update_entity_hash(domain_attr, storage_data[attr_name], entity_attrs)
|
148
149
|
end
|
149
150
|
|
150
151
|
entity_attrs
|
@@ -221,6 +222,10 @@ module Appfuel
|
|
221
222
|
target
|
222
223
|
end
|
223
224
|
|
225
|
+
def expr_conjunction?(value)
|
226
|
+
value.instance_of?(ExprConjunction)
|
227
|
+
end
|
228
|
+
|
224
229
|
private
|
225
230
|
def validate_domain(entity_name)
|
226
231
|
unless entity?(entity_name)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Appfuel
|
2
|
+
module Repository
|
3
|
+
class OrderExpr < Expr
|
4
|
+
|
5
|
+
def self.build(data)
|
6
|
+
return [data] if data.instance_of?(self)
|
7
|
+
data = [data] if data.is_a?(String)
|
8
|
+
unless data.respond_to?(:each)
|
9
|
+
fail "order must be a string or implement :each"
|
10
|
+
end
|
11
|
+
|
12
|
+
results = []
|
13
|
+
data.each do |item|
|
14
|
+
item = parse_order_string(item) if item.is_a?(String)
|
15
|
+
if item.instance_of?(self)
|
16
|
+
results << item
|
17
|
+
next
|
18
|
+
end
|
19
|
+
|
20
|
+
if !item.is_a?(Hash)
|
21
|
+
fail "order array must be a list of strings or hashes"
|
22
|
+
end
|
23
|
+
domain_attr, dir = item.first
|
24
|
+
results << self.new(domain_attr, dir)
|
25
|
+
end
|
26
|
+
results
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(domain_attr, op = nil)
|
30
|
+
op ||= 'asc'
|
31
|
+
|
32
|
+
super(domain_attr, op, nil)
|
33
|
+
@op = @op.downcase
|
34
|
+
unless ['asc', 'desc'].include?(@op)
|
35
|
+
fail "order direction must be either asc or desc"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{attr_list.join('.')} #{op}"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def self.parse_order_string(str)
|
45
|
+
str, dir = str.split(' ')
|
46
|
+
dir = 'asc' if dir.nil?
|
47
|
+
{str => dir.downcase}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Appfuel
|
2
|
+
# Used in the validation system by custom predicates to ask the question if
|
3
|
+
# an entity exists in the database
|
4
|
+
module Repository
|
5
|
+
class Runner
|
6
|
+
attr_reader :repo_namespace, :criteria_class
|
7
|
+
|
8
|
+
# The call relies on the fact that we can build a criteria find the
|
9
|
+
# correct repo and call the exists? interface on that repo. The identity
|
10
|
+
# of any given repo requires its namespace + its class name.
|
11
|
+
#
|
12
|
+
# @param namespace [String] fully qualified namespace string fro repos
|
13
|
+
# @param criteria_class [Class] class used to represent the criteria
|
14
|
+
# @returns [ExistsInDbRunner]
|
15
|
+
def initialize(namespace, criteria_class)
|
16
|
+
@repo_namespace = namespace
|
17
|
+
@criteria_class = criteria_class
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_criteria(entity_key, opts = {})
|
21
|
+
criteria_class.new(entity_key, opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def query(criteria)
|
25
|
+
load_repo(criteria).query(criteria)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param entity_key [String] the type identifier for an entity
|
29
|
+
# @param opts [Hash] one attr => value pair is required
|
30
|
+
# repo => name is optional
|
31
|
+
#
|
32
|
+
# @return [Bool]
|
33
|
+
def exists?(entity_key, opts = {})
|
34
|
+
fail "opts must be a hash" unless opts.is_a?(Hash)
|
35
|
+
|
36
|
+
criteria_opts = {}
|
37
|
+
if opts.key?(:repo)
|
38
|
+
criteria_opts[:repo] = opts.delete(:repo)
|
39
|
+
end
|
40
|
+
fail "opts hash must have one attr => value pair" if opts.empty?
|
41
|
+
|
42
|
+
property, value = opts.first
|
43
|
+
criteria = create_criteria(entity_key, criteria_opts)
|
44
|
+
criteria.exists(property, value)
|
45
|
+
|
46
|
+
load_repo(criteria).exists?(criteria)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def load_repo(criteria)
|
53
|
+
klass = "#{repo_namespace}::#{criteria.repo_name}"
|
54
|
+
unless Kernel.const_defined?(klass)
|
55
|
+
fail "RepositoryRunner: failed - repo #{klass} not defined"
|
56
|
+
end
|
57
|
+
|
58
|
+
Kernel.const_get(klass).new
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Appfuel
|
2
|
+
module Repository
|
3
|
+
class SearchParser < ExprParser
|
4
|
+
rule(:filter_identifier) { stri('filter') }
|
5
|
+
rule(:order_identifier) { stri('order') }
|
6
|
+
rule(:limit_identifier) { stri('limit') }
|
7
|
+
|
8
|
+
rule(:order_dir) do
|
9
|
+
(stri('asc') | stri('desc')).as(:order_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
rule(:domain_name) do
|
13
|
+
attr_label.as(:feature) >> str('.') >> attr_label.as(:basename)
|
14
|
+
end
|
15
|
+
|
16
|
+
rule(:limit_expr) do
|
17
|
+
(
|
18
|
+
limit_identifier >> space >> space? >> integer.as(:value)
|
19
|
+
).as(:limit)
|
20
|
+
end
|
21
|
+
|
22
|
+
rule(:order_expr) do
|
23
|
+
(
|
24
|
+
(domain_attr >> space >> order_dir) | domain_attr
|
25
|
+
).as(:order_expr)
|
26
|
+
end
|
27
|
+
|
28
|
+
# order id
|
29
|
+
# order id asc
|
30
|
+
# order foo.id asc
|
31
|
+
# order foo.id, code desc, foo.bar.id asc
|
32
|
+
rule(:order_by) do
|
33
|
+
(
|
34
|
+
order_identifier >> space >> space? >>
|
35
|
+
(order_expr >> (comma >> order_expr).repeat).maybe
|
36
|
+
).as(:order)
|
37
|
+
end
|
38
|
+
|
39
|
+
rule(:search) do
|
40
|
+
(
|
41
|
+
domain_name.as(:domain) >> space >> space? >>
|
42
|
+
filter_identifier >> space >> space? >>
|
43
|
+
or_operation.as(:filters) >> order_by.maybe >> space? >> limit_expr.maybe
|
44
|
+
).as(:search)
|
45
|
+
end
|
46
|
+
|
47
|
+
root(:search)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Appfuel
|
2
|
+
module Repository
|
3
|
+
class SearchTransform < ExprTransform
|
4
|
+
|
5
|
+
|
6
|
+
rule(order_dir: simple(:n)) {
|
7
|
+
value = n.to_s.downcase
|
8
|
+
value == 'desc' ? 'desc' : 'asc'
|
9
|
+
}
|
10
|
+
|
11
|
+
rule(order_expr: subtree(:expr)) do |dict|
|
12
|
+
expr = dict[:expr]
|
13
|
+
domain_attr = expr[:domain_attr]
|
14
|
+
order_dir = expr[:order_dir] || 'asc'
|
15
|
+
OrderExpr.new(domain_attr, order_dir)
|
16
|
+
end
|
17
|
+
|
18
|
+
rule(attr_label: simple(:n)) { n.to_s }
|
19
|
+
|
20
|
+
rule(domain_attr: simple(:n)) {
|
21
|
+
list = n.is_a?(Array) ? n : [n]
|
22
|
+
{domain_attr: list}
|
23
|
+
}
|
24
|
+
|
25
|
+
rule(domain_expr: subtree(:domain_expr)) do |dict|
|
26
|
+
data = dict[:domain_expr]
|
27
|
+
domain_attr = data[:domain_attr]
|
28
|
+
op = data[:op]
|
29
|
+
value = data[:value]
|
30
|
+
Expr.new(domain_attr, op, value)
|
31
|
+
end
|
32
|
+
|
33
|
+
rule(search: subtree(:search)) do |dict|
|
34
|
+
search = dict[:search]
|
35
|
+
domain = search[:domain]
|
36
|
+
filters = search[:filters]
|
37
|
+
orders = search[:order]
|
38
|
+
limit = search[:limit]
|
39
|
+
|
40
|
+
if filters.is_a?(Hash) && filters.key?(:root)
|
41
|
+
filters = filters[:root]
|
42
|
+
end
|
43
|
+
result = {}
|
44
|
+
result[:domain] = "#{domain[:feature]}.#{domain[:basename]}"
|
45
|
+
result[:filters] = filters
|
46
|
+
unless limit.nil?
|
47
|
+
result[:limit] = limit[:value]
|
48
|
+
end
|
49
|
+
|
50
|
+
unless orders.nil?
|
51
|
+
orders = orders.is_a?(Array) ? orders : [orders]
|
52
|
+
result[:order] = orders
|
53
|
+
end
|
54
|
+
{search: Criteria.build(result)}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Appfuel
|
2
|
-
module
|
3
|
-
|
2
|
+
module Repository
|
4
3
|
# The Criteria represents the interface between the repositories and actions
|
5
4
|
# or commands. The allow you to find entities in the application storage (
|
6
5
|
# a database) without knowledge of that storage system. The criteria will
|
@@ -19,7 +18,7 @@ module Appfuel
|
|
19
18
|
# transform
|
20
19
|
# search_name
|
21
20
|
#
|
22
|
-
class
|
21
|
+
class Settings
|
23
22
|
DEFAULT_PAGE = 1
|
24
23
|
DEFAULT_PER_PAGE = 20
|
25
24
|
|
@@ -29,8 +28,8 @@ module Appfuel
|
|
29
28
|
# @param opts [Hash] options for initializing criteria
|
30
29
|
# @return [Criteria]
|
31
30
|
def initialize(settings = {})
|
32
|
-
@parser = settings[:
|
33
|
-
@transform = settings[:
|
31
|
+
@parser = settings[:parser] || SearchParser.new
|
32
|
+
@transform = settings[:transform] || SearchTransform.new
|
34
33
|
|
35
34
|
|
36
35
|
empty_dataset_is_valid!
|
@@ -55,10 +54,22 @@ module Appfuel
|
|
55
54
|
all
|
56
55
|
end
|
57
56
|
|
57
|
+
manual_query(settings[:manual_query]) if settings.key?(:manual_query)
|
58
|
+
|
58
59
|
page(settings[:page] || DEFAULT_PAGE)
|
59
60
|
per_page(settings[:per_page] || DEFAULT_PER_PAGE)
|
60
61
|
end
|
61
62
|
|
63
|
+
def manual_query?
|
64
|
+
!manual_query.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
def manual_query(value = nil)
|
68
|
+
return @manual_query if value.nil?
|
69
|
+
@manual_query = value
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
62
73
|
def disable_pagination?
|
63
74
|
@disable_pagination
|
64
75
|
end
|
@@ -85,6 +96,10 @@ module Appfuel
|
|
85
96
|
self
|
86
97
|
end
|
87
98
|
|
99
|
+
def single?
|
100
|
+
first? || last?
|
101
|
+
end
|
102
|
+
|
88
103
|
def all?
|
89
104
|
@all
|
90
105
|
end
|
@@ -1,8 +1,21 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require 'parslet/convenience'
|
3
|
+
|
1
4
|
require_relative 'repository/base'
|
2
5
|
require_relative 'repository/mapping_entry'
|
3
6
|
require_relative 'repository/mapping_dsl'
|
4
7
|
require_relative 'repository/mapper'
|
5
8
|
require_relative 'repository/initializer'
|
9
|
+
require_relative 'repository/runner'
|
10
|
+
require_relative 'repository/expr'
|
11
|
+
require_relative 'repository/expr_conjunction'
|
12
|
+
require_relative 'repository/order_expr'
|
13
|
+
require_relative 'repository/criteria'
|
14
|
+
require_relative 'repository/expr_parser'
|
15
|
+
require_relative 'repository/search_parser'
|
16
|
+
require_relative 'repository/expr_transform'
|
17
|
+
require_relative 'repository/search_transform'
|
18
|
+
require_relative 'repository/settings'
|
6
19
|
|
7
20
|
module Appfuel
|
8
21
|
module Repository
|
data/lib/appfuel/storage.rb
CHANGED
data/lib/appfuel/version.rb
CHANGED
data/lib/appfuel.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appfuel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Scott-Buccleuch
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.0
|
19
|
+
version: 5.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.0
|
26
|
+
version: 5.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: dry-types
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -259,6 +259,7 @@ files:
|
|
259
259
|
- lib/appfuel/application.rb
|
260
260
|
- lib/appfuel/application/app_container.rb
|
261
261
|
- lib/appfuel/application/container_class_registration.rb
|
262
|
+
- lib/appfuel/application/dispatcher.rb
|
262
263
|
- lib/appfuel/application/root.rb
|
263
264
|
- lib/appfuel/cli_msg_request.rb
|
264
265
|
- lib/appfuel/configuration.rb
|
@@ -267,19 +268,11 @@ files:
|
|
267
268
|
- lib/appfuel/configuration/populate.rb
|
268
269
|
- lib/appfuel/configuration/search.rb
|
269
270
|
- lib/appfuel/domain.rb
|
270
|
-
- lib/appfuel/domain/base_criteria.rb
|
271
271
|
- lib/appfuel/domain/criteria_builder.rb
|
272
|
-
- lib/appfuel/domain/criteria_settings.rb
|
273
272
|
- lib/appfuel/domain/domain_name_parser.rb
|
274
273
|
- lib/appfuel/domain/dsl.rb
|
275
274
|
- lib/appfuel/domain/entity.rb
|
276
275
|
- lib/appfuel/domain/entity_collection.rb
|
277
|
-
- lib/appfuel/domain/exists_criteria.rb
|
278
|
-
- lib/appfuel/domain/expr.rb
|
279
|
-
- lib/appfuel/domain/expr_conjunction.rb
|
280
|
-
- lib/appfuel/domain/expr_parser.rb
|
281
|
-
- lib/appfuel/domain/expr_transform.rb
|
282
|
-
- lib/appfuel/domain/search_criteria.rb
|
283
276
|
- lib/appfuel/domain/value_object.rb
|
284
277
|
- lib/appfuel/errors.rb
|
285
278
|
- lib/appfuel/feature.rb
|
@@ -297,13 +290,6 @@ files:
|
|
297
290
|
- lib/appfuel/predicates.rb
|
298
291
|
- lib/appfuel/presenter.rb
|
299
292
|
- lib/appfuel/presenter/base.rb
|
300
|
-
- lib/appfuel/repository.rb
|
301
|
-
- lib/appfuel/repository/base.rb
|
302
|
-
- lib/appfuel/repository/initializer.rb
|
303
|
-
- lib/appfuel/repository/mapper.rb
|
304
|
-
- lib/appfuel/repository/mapping_dsl.rb
|
305
|
-
- lib/appfuel/repository/mapping_entry.rb
|
306
|
-
- lib/appfuel/repository_runner.rb
|
307
293
|
- lib/appfuel/request.rb
|
308
294
|
- lib/appfuel/response.rb
|
309
295
|
- lib/appfuel/response_handler.rb
|
@@ -323,6 +309,22 @@ files:
|
|
323
309
|
- lib/appfuel/storage/memory.rb
|
324
310
|
- lib/appfuel/storage/memory/mapper.rb
|
325
311
|
- lib/appfuel/storage/memory/repository.rb
|
312
|
+
- lib/appfuel/storage/repository.rb
|
313
|
+
- lib/appfuel/storage/repository/base.rb
|
314
|
+
- lib/appfuel/storage/repository/criteria.rb
|
315
|
+
- lib/appfuel/storage/repository/expr.rb
|
316
|
+
- lib/appfuel/storage/repository/expr_conjunction.rb
|
317
|
+
- lib/appfuel/storage/repository/expr_parser.rb
|
318
|
+
- lib/appfuel/storage/repository/expr_transform.rb
|
319
|
+
- lib/appfuel/storage/repository/initializer.rb
|
320
|
+
- lib/appfuel/storage/repository/mapper.rb
|
321
|
+
- lib/appfuel/storage/repository/mapping_dsl.rb
|
322
|
+
- lib/appfuel/storage/repository/mapping_entry.rb
|
323
|
+
- lib/appfuel/storage/repository/order_expr.rb
|
324
|
+
- lib/appfuel/storage/repository/runner.rb
|
325
|
+
- lib/appfuel/storage/repository/search_parser.rb
|
326
|
+
- lib/appfuel/storage/repository/search_transform.rb
|
327
|
+
- lib/appfuel/storage/repository/settings.rb
|
326
328
|
- lib/appfuel/types.rb
|
327
329
|
- lib/appfuel/validation.rb
|
328
330
|
- lib/appfuel/validation/validator.rb
|
@@ -1,171 +0,0 @@
|
|
1
|
-
module Appfuel
|
2
|
-
module Domain
|
3
|
-
|
4
|
-
# The Criteria represents the interface between the repositories and actions
|
5
|
-
# or commands. The allow you to find entities in the application storage (
|
6
|
-
# a database) without knowledge of that storage system. The criteria will
|
7
|
-
# always refer to its queries in the domain language for which the repo is
|
8
|
-
# responsible for mapping that query to its persistence layer.
|
9
|
-
#
|
10
|
-
# global.user
|
11
|
-
# memberships.user
|
12
|
-
#
|
13
|
-
# exist: 'foo.bar exists id = 6'
|
14
|
-
# search: 'foo.bar filter id = 6 and bar = "foo" order id asc limit 6'
|
15
|
-
#
|
16
|
-
# search:
|
17
|
-
# domain: 'foo.bar',
|
18
|
-
#
|
19
|
-
# filters: 'id = 6 or id = 8 and id = 9'
|
20
|
-
# filters: [
|
21
|
-
# 'id = 6',
|
22
|
-
# {or: 'id = 8'}
|
23
|
-
# {and: id = 9'}
|
24
|
-
# ]
|
25
|
-
#
|
26
|
-
# order: 'foo.bar.id asc'
|
27
|
-
# order: 'foo.bar.id'
|
28
|
-
# order: [
|
29
|
-
# 'foo.bar.id',
|
30
|
-
# {desc: 'foo.bar.id'},
|
31
|
-
# {asc: 'foo.bar.id'}
|
32
|
-
# ]
|
33
|
-
# limit: 1
|
34
|
-
#
|
35
|
-
# settings:
|
36
|
-
# page: 1
|
37
|
-
# per_page: 2
|
38
|
-
# disable_pagination
|
39
|
-
# first
|
40
|
-
# all
|
41
|
-
# last
|
42
|
-
# error_on_empty
|
43
|
-
# parser
|
44
|
-
# transform
|
45
|
-
#
|
46
|
-
# exists:
|
47
|
-
# domain:
|
48
|
-
# expr:
|
49
|
-
#
|
50
|
-
#
|
51
|
-
class BaseCriteria
|
52
|
-
include DomainNameParser
|
53
|
-
|
54
|
-
|
55
|
-
attr_reader :domain_basename, :domain_name, :feature, :settings, :filters
|
56
|
-
|
57
|
-
# Parse out the domain into feature, domain, determine the name of the
|
58
|
-
# repo this criteria is for and initailize basic settings.
|
59
|
-
# global.user
|
60
|
-
#
|
61
|
-
# membership.user
|
62
|
-
# foo.id filter name like "foo" order foo.bar.id asc limit 2
|
63
|
-
# foo.id exists foo.id = 5
|
64
|
-
#
|
65
|
-
# @example
|
66
|
-
# SpCore::Domain::Criteria('foo', single: true)
|
67
|
-
# Types.Criteria('foo.bar', single: true)
|
68
|
-
#
|
69
|
-
# === Options
|
70
|
-
# error_on_empty: will cause the repo to fail when query returns an
|
71
|
-
# an empty dataset. The failure will have the message
|
72
|
-
# with key as domain and text is "<domain> not found"
|
73
|
-
#
|
74
|
-
# single: will cause the repo to return only one, the first,
|
75
|
-
# entity in the dataset
|
76
|
-
#
|
77
|
-
# @param domain [String] fully qualified domain name
|
78
|
-
# @param opts [Hash] options for initializing criteria
|
79
|
-
# @return [Criteria]
|
80
|
-
def initialize(domain_name, data = {})
|
81
|
-
@feature, @domain_basename, @domain_name = parse_domain_name(domain_name)
|
82
|
-
@settings = data[:settings] || CriteriaSettings.new(data)
|
83
|
-
@filters = nil
|
84
|
-
@params = {}
|
85
|
-
end
|
86
|
-
|
87
|
-
def clear_filters
|
88
|
-
@filters = nil
|
89
|
-
end
|
90
|
-
|
91
|
-
def filters?
|
92
|
-
!filters.nil?
|
93
|
-
end
|
94
|
-
|
95
|
-
def global?
|
96
|
-
!feature?
|
97
|
-
end
|
98
|
-
|
99
|
-
def feature?
|
100
|
-
@feature != 'global'
|
101
|
-
end
|
102
|
-
|
103
|
-
# @example
|
104
|
-
# criteria.add_param('foo', 100)
|
105
|
-
#
|
106
|
-
# @param key [Symbol, String] The key name where we want to keep the value
|
107
|
-
# @param value [String, Integer] The value that belongs to the key param
|
108
|
-
# @return [String, Integer] The saved value
|
109
|
-
def add_param(key, value)
|
110
|
-
fail 'key should not be nil' if key.nil?
|
111
|
-
|
112
|
-
@params[key.to_sym] = value
|
113
|
-
end
|
114
|
-
|
115
|
-
# @param key [String, Symbol]
|
116
|
-
# @return [String, Integer, Boolean] the found value
|
117
|
-
def param(key)
|
118
|
-
@params[key.to_sym]
|
119
|
-
end
|
120
|
-
|
121
|
-
# @param key [String, Symbol]
|
122
|
-
# @return [Boolean]
|
123
|
-
def param?(key)
|
124
|
-
@params.key?(key.to_sym)
|
125
|
-
end
|
126
|
-
|
127
|
-
# @return [Boolean] if the @params variable has values
|
128
|
-
def params?
|
129
|
-
!@params.empty?
|
130
|
-
end
|
131
|
-
|
132
|
-
private
|
133
|
-
def parse_expr(str)
|
134
|
-
if !(settings.parser && settings.parser.respond_to?(:parse))
|
135
|
-
fail "expression parser must implement to :parse"
|
136
|
-
end
|
137
|
-
|
138
|
-
if !(settings.transform && settings.transform.respond_to?(:apply))
|
139
|
-
fail "expression transform must implement :apply"
|
140
|
-
end
|
141
|
-
|
142
|
-
begin
|
143
|
-
tree = settings.parser.parse(str)
|
144
|
-
rescue Parslet::ParseFailed => e
|
145
|
-
msg = "The expression (#{str}) failed to parse"
|
146
|
-
err = RuntimeError.new(msg)
|
147
|
-
err.set_backtrace(e.backtrace)
|
148
|
-
raise err
|
149
|
-
end
|
150
|
-
|
151
|
-
result = settings.transform.apply(tree)
|
152
|
-
result = result[:domain_expr] || result[:root]
|
153
|
-
unless result
|
154
|
-
fail "unable to parse (#{str}) correctly"
|
155
|
-
end
|
156
|
-
result
|
157
|
-
end
|
158
|
-
|
159
|
-
def qualify_expr(domain_expr)
|
160
|
-
return domain_expr if domain_expr.qualified?
|
161
|
-
if global?
|
162
|
-
domain_expr.qualify_global(domain_basename)
|
163
|
-
return domain_expr
|
164
|
-
end
|
165
|
-
|
166
|
-
domain_expr.qualify_feature(feature, domain_basename)
|
167
|
-
domain_expr
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|