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,57 +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
|
-
#
|
15
|
-
# exits:
|
16
|
-
# domain: 'foo.bar'
|
17
|
-
# expr: 'id = 6'
|
18
|
-
#
|
19
|
-
# settings:
|
20
|
-
# error_on_empty
|
21
|
-
# parser
|
22
|
-
# transform
|
23
|
-
#
|
24
|
-
#
|
25
|
-
class ExistsCriteria < BaseCriteria
|
26
|
-
#
|
27
|
-
# @param domain [String] fully qualified domain name
|
28
|
-
# @param opts [Hash] options for initializing criteria
|
29
|
-
# @return [Criteria]
|
30
|
-
def initialize(domain_name, data = {})
|
31
|
-
super
|
32
|
-
expr(data[:expr]) if data[:expr]
|
33
|
-
end
|
34
|
-
|
35
|
-
def filter(str)
|
36
|
-
domain_expr = parse_expr(str)
|
37
|
-
if filters?
|
38
|
-
fail "A filter expression has already been assigned"
|
39
|
-
end
|
40
|
-
|
41
|
-
if domain_expr.conjunction?
|
42
|
-
fail "Only simple domain expressions are allowed for exists criteria"
|
43
|
-
end
|
44
|
-
|
45
|
-
if domain_expr.qualified?
|
46
|
-
fail "Only allows relative domain attributes"
|
47
|
-
end
|
48
|
-
|
49
|
-
@filters = qualify_expr(domain_expr)
|
50
|
-
self
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module Appfuel
|
2
|
-
module Domain
|
3
|
-
class ExprConjunction
|
4
|
-
attr_reader :op, :left, :right
|
5
|
-
|
6
|
-
def initialize(type, left, right)
|
7
|
-
@op = type.to_s.downcase
|
8
|
-
@left = left
|
9
|
-
@right = right
|
10
|
-
end
|
11
|
-
|
12
|
-
def conjunction?
|
13
|
-
true
|
14
|
-
end
|
15
|
-
|
16
|
-
def qualify_feature(feature, domain)
|
17
|
-
left.qualify_feature(feature, domain)
|
18
|
-
right.qualify_feature(feature, domain)
|
19
|
-
end
|
20
|
-
|
21
|
-
def qualify_global(domain)
|
22
|
-
left.qualify_global(domain)
|
23
|
-
right.qualify_global(domain)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,137 +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
|
-
# search: 'foo.bar filter id = 6 and bar = "foo" order id asc limit 6'
|
11
|
-
#
|
12
|
-
# filters: 'id = 6 or id = 8 and id = 9'
|
13
|
-
# filters: [
|
14
|
-
# 'id = 6',
|
15
|
-
# {or: 'id = 8'}
|
16
|
-
# {and: id = 9'}
|
17
|
-
# ]
|
18
|
-
#
|
19
|
-
# order: 'foo.bar.id asc'
|
20
|
-
# order: 'foo.bar.id'
|
21
|
-
# order: [
|
22
|
-
# 'foo.bar.id',
|
23
|
-
# {desc: 'foo.bar.id'},
|
24
|
-
# {asc: 'foo.bar.id'}
|
25
|
-
# ]
|
26
|
-
# limit: 1
|
27
|
-
#
|
28
|
-
# settings:
|
29
|
-
# page: 1
|
30
|
-
# per_page: 2
|
31
|
-
# disable_pagination
|
32
|
-
# first
|
33
|
-
# all
|
34
|
-
# last
|
35
|
-
# error_on_empty
|
36
|
-
# parser
|
37
|
-
# transform
|
38
|
-
#
|
39
|
-
class SearchCriteria < BaseCriteria
|
40
|
-
attr_reader :order_exprs
|
41
|
-
# Parse out the domain into feature, domain, determine the name of the
|
42
|
-
# repo this criteria is for and initailize basic settings.
|
43
|
-
# global.user
|
44
|
-
#
|
45
|
-
# membership.user
|
46
|
-
# foo.id filter name like "foo" order foo.bar.id asc limit 2
|
47
|
-
# foo.id exists foo.id = 5
|
48
|
-
#
|
49
|
-
# @example
|
50
|
-
# SpCore::Domain::Criteria('foo', single: true)
|
51
|
-
# Types.Criteria('foo.bar', single: true)
|
52
|
-
#
|
53
|
-
# === Options
|
54
|
-
# error_on_empty: will cause the repo to fail when query returns an
|
55
|
-
# an empty dataset. The failure will have the message
|
56
|
-
# with key as domain and text is "<domain> not found"
|
57
|
-
#
|
58
|
-
# single: will cause the repo to return only one, the first,
|
59
|
-
# entity in the dataset
|
60
|
-
#
|
61
|
-
# @param domain [String] fully qualified domain name
|
62
|
-
# @param opts [Hash] options for initializing criteria
|
63
|
-
# @return [Criteria]
|
64
|
-
#
|
65
|
-
# @param domain [String] fully qualified domain name
|
66
|
-
# @param opts [Hash] options for initializing criteria
|
67
|
-
# @return [Criteria]
|
68
|
-
def initialize(domain_name, data = {})
|
69
|
-
super
|
70
|
-
@limit = nil
|
71
|
-
@order_exprs = []
|
72
|
-
filter(data[:filter]) if data[:filter]
|
73
|
-
end
|
74
|
-
|
75
|
-
def filter(str, op: 'and')
|
76
|
-
expr = parse_expr(str)
|
77
|
-
return false unless expr
|
78
|
-
|
79
|
-
expr = qualify_expr(expr)
|
80
|
-
if filters?
|
81
|
-
expr = ExprConjunction.new(op, filters, expr)
|
82
|
-
end
|
83
|
-
|
84
|
-
@filters = expr
|
85
|
-
self
|
86
|
-
end
|
87
|
-
|
88
|
-
def limit(nbr = nil)
|
89
|
-
return @limit if nbr.nil?
|
90
|
-
|
91
|
-
@limit = Integer(nbr)
|
92
|
-
fail "limit must be an integer greater than 0" unless nbr > 0
|
93
|
-
self
|
94
|
-
end
|
95
|
-
|
96
|
-
# order first_name asc, last_name
|
97
|
-
# order: 'foo.bar.id asc'
|
98
|
-
# order: 'foo.bar.id'
|
99
|
-
# order: [
|
100
|
-
# 'foo.bar.id',
|
101
|
-
# {desc: 'foo.bar.id'},
|
102
|
-
# {asc: 'foo.bar.id'}
|
103
|
-
# ]
|
104
|
-
#
|
105
|
-
# membership.user.id
|
106
|
-
def order(data)
|
107
|
-
data = [data] if data.is_a?(String)
|
108
|
-
unless data.respond_to?(:each)
|
109
|
-
fail "order must be a string or implement :each"
|
110
|
-
end
|
111
|
-
data.each do |item|
|
112
|
-
item = transform_order_string(item) if item.is_a?(String)
|
113
|
-
|
114
|
-
if !item.is_a?(Hash)
|
115
|
-
fail "order array must be a list of strings or hashes"
|
116
|
-
end
|
117
|
-
|
118
|
-
dir, domain_attr = item.first
|
119
|
-
dir = dir.to_s.downcase
|
120
|
-
unless ['desc', 'asc'].include(dir)
|
121
|
-
fail "order array item must have a hash key of desc or asc"
|
122
|
-
end
|
123
|
-
@order_exprs << {dir => domain_attr}
|
124
|
-
end
|
125
|
-
self
|
126
|
-
end
|
127
|
-
|
128
|
-
private
|
129
|
-
|
130
|
-
def transform_order_string(str)
|
131
|
-
str, dir = item.split(' ')
|
132
|
-
dir = 'asc' if dir.nil?
|
133
|
-
{dir.downcase => str}
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
module Appfuel
|
2
|
-
module Repository
|
3
|
-
class Base
|
4
|
-
include Appfuel::Application::AppContainer
|
5
|
-
|
6
|
-
class << self
|
7
|
-
attr_writer :mapper
|
8
|
-
|
9
|
-
def container_class_type
|
10
|
-
'repositories'
|
11
|
-
end
|
12
|
-
|
13
|
-
def inherited(klass)
|
14
|
-
stage_class_for_registration(klass)
|
15
|
-
end
|
16
|
-
|
17
|
-
def mapper
|
18
|
-
@mapper ||= create_mapper
|
19
|
-
end
|
20
|
-
|
21
|
-
def create_mapper(maps = nil)
|
22
|
-
Mapper.new(container_root_name, maps)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def mapper
|
27
|
-
self.class.mapper
|
28
|
-
end
|
29
|
-
|
30
|
-
def search(criteria)
|
31
|
-
mapper.search(criteria)
|
32
|
-
end
|
33
|
-
|
34
|
-
def exists?(criteria)
|
35
|
-
expr = criteria.fiilters
|
36
|
-
mapper.exists?(expr)
|
37
|
-
end
|
38
|
-
|
39
|
-
def to_storage(entity, exclude = [])
|
40
|
-
mapper.to_storage(entity, exclude)
|
41
|
-
end
|
42
|
-
|
43
|
-
def to_entity(domain_name, storage)
|
44
|
-
key = qualify_container_key(domain_name, "domains")
|
45
|
-
hash = mapper.to_entity_hash(domain_name, storage)
|
46
|
-
app_container[key].new(hash)
|
47
|
-
end
|
48
|
-
|
49
|
-
def build(type:, name:, storage:, **inputs)
|
50
|
-
builder = find_entity_builder(type: type, domain_name: name)
|
51
|
-
builder.call(storage, inputs)
|
52
|
-
end
|
53
|
-
|
54
|
-
# features.membership.presenters.hash.user
|
55
|
-
# global.presenters.user
|
56
|
-
#
|
57
|
-
# key => db_model
|
58
|
-
# key => db_model
|
59
|
-
def find_entity_builder(domain_name:, type:)
|
60
|
-
key = qualify_container_key(domain_name, "domain_builders.#{type}")
|
61
|
-
|
62
|
-
container = app_container
|
63
|
-
unless container.key?(key)
|
64
|
-
return ->(data, inputs = {}) {
|
65
|
-
build_default_entity(domain_name: domain_name, storage: data)
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
container[key]
|
70
|
-
end
|
71
|
-
|
72
|
-
def build_default_entity(domain_name:, storage:)
|
73
|
-
storage = [storage] unless storage.is_a?(Array)
|
74
|
-
|
75
|
-
storage_attrs = {}
|
76
|
-
storage.each do |model|
|
77
|
-
storage_attrs.merge!(mapper.model_attributes(model))
|
78
|
-
end
|
79
|
-
|
80
|
-
hash = mapper.to_entity_hash(domain_name, storage_attrs)
|
81
|
-
key = qualify_container_key(domain_name, "domains")
|
82
|
-
app_container[key].new(hash)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
@@ -1,60 +0,0 @@
|
|
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
|
-
class RepositoryRunner
|
5
|
-
attr_reader :repo_namespace, :criteria_class
|
6
|
-
|
7
|
-
# The call relies on the fact that we can build a criteria find the
|
8
|
-
# correct repo and call the exists? interface on that repo. The identity
|
9
|
-
# of any given repo requires its namespace + its class name.
|
10
|
-
#
|
11
|
-
# @param namespace [String] fully qualified namespace string fro repos
|
12
|
-
# @param criteria_class [Class] class used to represent the criteria
|
13
|
-
# @returns [ExistsInDbRunner]
|
14
|
-
def initialize(namespace, criteria_class)
|
15
|
-
@repo_namespace = namespace
|
16
|
-
@criteria_class = criteria_class
|
17
|
-
end
|
18
|
-
|
19
|
-
def create_criteria(entity_key, opts = {})
|
20
|
-
criteria_class.new(entity_key, opts)
|
21
|
-
end
|
22
|
-
|
23
|
-
def query(criteria)
|
24
|
-
load_repo(criteria).query(criteria)
|
25
|
-
end
|
26
|
-
|
27
|
-
# @param entity_key [String] the type identifier for an entity
|
28
|
-
# @param opts [Hash] one attr => value pair is required
|
29
|
-
# repo => name is optional
|
30
|
-
#
|
31
|
-
# @return [Bool]
|
32
|
-
def exists?(entity_key, opts = {})
|
33
|
-
fail "opts must be a hash" unless opts.is_a?(Hash)
|
34
|
-
|
35
|
-
criteria_opts = {}
|
36
|
-
if opts.key?(:repo)
|
37
|
-
criteria_opts[:repo] = opts.delete(:repo)
|
38
|
-
end
|
39
|
-
fail "opts hash must have one attr => value pair" if opts.empty?
|
40
|
-
|
41
|
-
property, value = opts.first
|
42
|
-
criteria = create_criteria(entity_key, criteria_opts)
|
43
|
-
criteria.exists(property, value)
|
44
|
-
|
45
|
-
load_repo(criteria).exists?(criteria)
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def load_repo(criteria)
|
52
|
-
klass = "#{repo_namespace}::#{criteria.repo_name}"
|
53
|
-
unless Kernel.const_defined?(klass)
|
54
|
-
fail "RepositoryRunner: failed - repo #{klass} not defined"
|
55
|
-
end
|
56
|
-
|
57
|
-
Kernel.const_get(klass).new
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
File without changes
|
File without changes
|
File without changes
|