appfuel 0.2.5 → 0.2.6
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/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
|