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,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
|