appfuel 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +78 -1
- data/appfuel.gemspec +1 -0
- data/docs/images/appfuel_basic_flow.png +0 -0
- data/lib/appfuel/application/app_container.rb +24 -1
- data/lib/appfuel/application/container_class_registration.rb +29 -4
- data/lib/appfuel/application/root.rb +1 -0
- data/lib/appfuel/application.rb +0 -1
- data/lib/appfuel/domain/base_criteria.rb +171 -0
- data/lib/appfuel/domain/criteria_builder.rb +248 -0
- data/lib/appfuel/domain/criteria_settings.rb +156 -0
- data/lib/appfuel/domain/dsl.rb +5 -1
- data/lib/appfuel/domain/entity.rb +1 -2
- data/lib/appfuel/domain/exists_criteria.rb +57 -0
- data/lib/appfuel/domain/expr.rb +66 -97
- data/lib/appfuel/domain/expr_conjunction.rb +27 -0
- data/lib/appfuel/domain/expr_parser.rb +199 -0
- data/lib/appfuel/domain/expr_transform.rb +68 -0
- data/lib/appfuel/domain/search_criteria.rb +137 -0
- data/lib/appfuel/domain.rb +6 -1
- data/lib/appfuel/feature/initializer.rb +5 -0
- data/lib/appfuel/handler/action.rb +3 -0
- data/lib/appfuel/handler/base.rb +11 -1
- data/lib/appfuel/handler/command.rb +4 -0
- data/lib/appfuel/repository/base.rb +16 -2
- data/lib/appfuel/repository/mapper.rb +41 -7
- data/lib/appfuel/repository/mapping_dsl.rb +4 -4
- data/lib/appfuel/repository/mapping_entry.rb +2 -2
- data/lib/appfuel/repository.rb +0 -1
- data/lib/appfuel/storage/db/active_record_model.rb +32 -28
- data/lib/appfuel/storage/db/mapper.rb +38 -125
- data/lib/appfuel/storage/db/repository.rb +6 -10
- data/lib/appfuel/storage/memory/repository.rb +4 -0
- data/lib/appfuel/types.rb +0 -1
- data/lib/appfuel/version.rb +1 -1
- data/lib/appfuel.rb +6 -10
- metadata +26 -7
- data/lib/appfuel/application/container_key.rb +0 -201
- data/lib/appfuel/application/qualify_container_key.rb +0 -76
- data/lib/appfuel/db_model.rb +0 -16
- data/lib/appfuel/domain/criteria.rb +0 -436
- data/lib/appfuel/repository/mapping_registry.rb +0 -121
@@ -1,436 +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
|
-
class Criteria
|
10
|
-
include DomainNameParser
|
11
|
-
|
12
|
-
DEFAULT_PAGE = 1
|
13
|
-
DEFAULT_PER_PAGE = 20
|
14
|
-
|
15
|
-
attr_reader :domain, :domain_name, :feature, :repo_name, :exprs, :order,
|
16
|
-
:exists, :exec, :all
|
17
|
-
|
18
|
-
# Parse out the domain into feature, domain, determine the name of the
|
19
|
-
# repo this criteria is for and initailize basic settings.
|
20
|
-
#
|
21
|
-
# @example
|
22
|
-
# SpCore::Domain::Criteria('foo', single: true)
|
23
|
-
# Types.Criteria('foo.bar', single: true)
|
24
|
-
#
|
25
|
-
# === Options
|
26
|
-
# error_on_empty: will cause the repo to fail when query returns an
|
27
|
-
# an empty dataset. The failure will have the message
|
28
|
-
# with key as domain and text is "<domain> not found"
|
29
|
-
#
|
30
|
-
# single: will cause the repo to return only one, the first,
|
31
|
-
# entity in the dataset
|
32
|
-
#
|
33
|
-
# @param domain [String] fully qualified domain name
|
34
|
-
# @param opts [Hash] options for initializing criteria
|
35
|
-
# @return [Criteria]
|
36
|
-
def initialize(domain, opts = {})
|
37
|
-
@feature, @domain, @domain_name = parse_domain_name(domain)
|
38
|
-
@exists = nil
|
39
|
-
@exprs = []
|
40
|
-
@order = []
|
41
|
-
@limit = nil
|
42
|
-
@exec = nil
|
43
|
-
@all = false
|
44
|
-
@first = false
|
45
|
-
@last = false
|
46
|
-
@params = {}
|
47
|
-
@page = DEFAULT_PAGE
|
48
|
-
@per_page = DEFAULT_PER_PAGE
|
49
|
-
@disable_pagination = opts[:disable_pagination] == true
|
50
|
-
@repo_name = "#{(opts[:repo] || @domain).classify}Repository"
|
51
|
-
|
52
|
-
empty_dataset_is_valid!
|
53
|
-
if opts[:error_on_empty] == true
|
54
|
-
error_on_empty_dataset!
|
55
|
-
end
|
56
|
-
|
57
|
-
# default is to expect a collection for this critria unless you declare
|
58
|
-
# you want a single
|
59
|
-
collection
|
60
|
-
public_send(:first) if opts[:single] == true || opts[:first] == true
|
61
|
-
public_send(:last) if opts[:last] == true
|
62
|
-
end
|
63
|
-
|
64
|
-
# Add param to the instantiated criteria
|
65
|
-
#
|
66
|
-
# @example
|
67
|
-
# criteria.add_param('foo', 100)
|
68
|
-
#
|
69
|
-
# @param key [Symbol, String] The key name where we want to keep the value
|
70
|
-
# @param value [String, Integer] The value that belongs to the key param
|
71
|
-
# @return [String, Integer] The saved value
|
72
|
-
def add_param(key, value)
|
73
|
-
fail 'key should not be nil' if key.nil?
|
74
|
-
|
75
|
-
@params[key.to_sym] = value
|
76
|
-
end
|
77
|
-
|
78
|
-
# @param key [String, Symbol]
|
79
|
-
# @return [String, Integer, Boolean] the found value
|
80
|
-
def param(key)
|
81
|
-
@params[key.to_sym]
|
82
|
-
end
|
83
|
-
|
84
|
-
# @param key [String, Symbol]
|
85
|
-
# @return [Boolean]
|
86
|
-
def param?(key)
|
87
|
-
@params.key?(key.to_sym)
|
88
|
-
end
|
89
|
-
|
90
|
-
# @return [Boolean] if the @params variable has values
|
91
|
-
def params?
|
92
|
-
!@params.empty?
|
93
|
-
end
|
94
|
-
|
95
|
-
# Remove operators and keep key filters. It returns a cleaned
|
96
|
-
# list of filters.
|
97
|
-
#
|
98
|
-
# @example
|
99
|
-
# criteria.filter([
|
100
|
-
# {projects.offer.id: 6},
|
101
|
-
# {last_name: 'SirFooish', op: 'like'},
|
102
|
-
# {first_name: Bob, op: 'like', or: false}
|
103
|
-
# ])
|
104
|
-
#
|
105
|
-
# @raise [RuntimeError] when the attribute is not an array
|
106
|
-
# @raise [RuntimeError] when the filter is not a Hash
|
107
|
-
#
|
108
|
-
# @param list [Array<Hash>] The list of filters to implement in criteria
|
109
|
-
# @return [Array<Hash>, nil] List of filters with values or nil in case of array empty
|
110
|
-
def filter(list)
|
111
|
-
fail 'the attribute must be an Array' unless list.is_a? Array
|
112
|
-
|
113
|
-
list.each do |item|
|
114
|
-
fail 'filters must be a Hash' unless item.is_a? Hash
|
115
|
-
|
116
|
-
operator = extract_relational_operator(item)
|
117
|
-
relational_or = extract_relational_condition(item)
|
118
|
-
key, value = item.first
|
119
|
-
|
120
|
-
where(key, operator => value, or: relational_or)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def where?
|
125
|
-
!exprs.empty? || all?
|
126
|
-
end
|
127
|
-
|
128
|
-
# Adds an expression to the list that will be joined to
|
129
|
-
# the next expression with an `:and` operator
|
130
|
-
#
|
131
|
-
# @param domain_attr [String]
|
132
|
-
# @param data [Hash]
|
133
|
-
# @option <operator> the key is the operator like :eq and value is value
|
134
|
-
# @option :or with a value of true will join this expression with the
|
135
|
-
# previous expression using a relation or. relation and
|
136
|
-
# is by default
|
137
|
-
#
|
138
|
-
# @return [Criteria]
|
139
|
-
def where(domain_attr, data)
|
140
|
-
domain_attr = domain_attr.to_s
|
141
|
-
|
142
|
-
relational_op = :and
|
143
|
-
if data.key?(:or)
|
144
|
-
value = data.delete(:or)
|
145
|
-
relational_op = :or if value == true
|
146
|
-
end
|
147
|
-
|
148
|
-
domain_entity = domain_name
|
149
|
-
if domain_attr.count('.') == 2
|
150
|
-
domain_entity, domain_attr = parse_domain_attr(domain_attr)
|
151
|
-
end
|
152
|
-
|
153
|
-
expr = {
|
154
|
-
expr: create_expr(domain_entity, domain_attr, data),
|
155
|
-
relational_op: relational_op
|
156
|
-
}
|
157
|
-
exprs << expr
|
158
|
-
self
|
159
|
-
end
|
160
|
-
|
161
|
-
alias_method :and, :where
|
162
|
-
|
163
|
-
# Adds an expression to the list that will be joined to
|
164
|
-
# the next expression with an `:or` operator
|
165
|
-
#
|
166
|
-
# @param attr [String]
|
167
|
-
# @param value [Hash]
|
168
|
-
# @return [Criteria]
|
169
|
-
def or(domain_attr, data)
|
170
|
-
data[:or] = true
|
171
|
-
where(domain_attr, data)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Adds an expression to order list.
|
175
|
-
#
|
176
|
-
# @param list [list]
|
177
|
-
# @return [Criteria]
|
178
|
-
def order_by(dict, order_dir = 'ASC')
|
179
|
-
if dict.is_a?(String) || dict.is_a?(Symbol)
|
180
|
-
dict = {dict.to_s => order_dir}
|
181
|
-
end
|
182
|
-
|
183
|
-
dict.each do |domain_attr, dir|
|
184
|
-
domain_entity = domain_name
|
185
|
-
|
186
|
-
if domain_attr.count('.') == 2
|
187
|
-
domain_entity, domain_attr = parse_domain_attr(domain_attr)
|
188
|
-
end
|
189
|
-
|
190
|
-
@order << create_expr(domain_entity, domain_attr, eq: dir.to_s.upcase)
|
191
|
-
end
|
192
|
-
|
193
|
-
self
|
194
|
-
end
|
195
|
-
|
196
|
-
def order?
|
197
|
-
!@order.empty?
|
198
|
-
end
|
199
|
-
|
200
|
-
def limit?
|
201
|
-
!@limit.nil?
|
202
|
-
end
|
203
|
-
|
204
|
-
# @param limit [Integer]
|
205
|
-
# @return [Criteria]
|
206
|
-
def limit(value = nil)
|
207
|
-
return @limit if value.nil?
|
208
|
-
|
209
|
-
value = Integer(value)
|
210
|
-
fail "limit must be an integer gt 0" unless value > 0
|
211
|
-
@limit = value
|
212
|
-
self
|
213
|
-
end
|
214
|
-
|
215
|
-
# @param page [Integer]
|
216
|
-
# @return [Criteria]
|
217
|
-
def page(value = nil)
|
218
|
-
return @page if value.nil?
|
219
|
-
|
220
|
-
@page = Integer(value)
|
221
|
-
self
|
222
|
-
end
|
223
|
-
|
224
|
-
# @param per_page [Integer]
|
225
|
-
# @return [Criteria]
|
226
|
-
def per_page(value=nil)
|
227
|
-
return @per_page if value.nil?
|
228
|
-
|
229
|
-
@per_page = Integer(value)
|
230
|
-
self
|
231
|
-
end
|
232
|
-
|
233
|
-
# The repo uses this to determine what kind of dataset to return
|
234
|
-
#
|
235
|
-
# @return [Boolean]
|
236
|
-
def single?
|
237
|
-
first? || last?
|
238
|
-
end
|
239
|
-
|
240
|
-
# Tell the repo to only return a single entity for this criteria
|
241
|
-
#
|
242
|
-
# @return [Criteria]
|
243
|
-
def single
|
244
|
-
first
|
245
|
-
self
|
246
|
-
end
|
247
|
-
|
248
|
-
# Set false @first and @last instance variables
|
249
|
-
def clear_single
|
250
|
-
clear_first
|
251
|
-
clear_last
|
252
|
-
end
|
253
|
-
|
254
|
-
def first
|
255
|
-
@first = true
|
256
|
-
clear_last
|
257
|
-
self
|
258
|
-
end
|
259
|
-
|
260
|
-
def first?
|
261
|
-
@first
|
262
|
-
end
|
263
|
-
|
264
|
-
def clear_first
|
265
|
-
@first = false
|
266
|
-
end
|
267
|
-
|
268
|
-
def last?
|
269
|
-
@last
|
270
|
-
end
|
271
|
-
|
272
|
-
def last
|
273
|
-
clear_first
|
274
|
-
@last = true
|
275
|
-
self
|
276
|
-
end
|
277
|
-
|
278
|
-
def clear_last
|
279
|
-
@last = false
|
280
|
-
end
|
281
|
-
|
282
|
-
def disable_pagination?
|
283
|
-
@disable_pagination
|
284
|
-
end
|
285
|
-
|
286
|
-
def disable_pagination
|
287
|
-
@disable_pagination = true
|
288
|
-
end
|
289
|
-
|
290
|
-
# Tell the repo to return a collection for this criteria. This is the
|
291
|
-
# default setting
|
292
|
-
#
|
293
|
-
# @return Criteria
|
294
|
-
def collection
|
295
|
-
clear_single
|
296
|
-
self
|
297
|
-
end
|
298
|
-
|
299
|
-
def all?
|
300
|
-
@all
|
301
|
-
end
|
302
|
-
|
303
|
-
# Tell the repo to return all records for this criteria. It is import
|
304
|
-
# to understand that for database queries you are calling all on the
|
305
|
-
# map for the specified domain.
|
306
|
-
#
|
307
|
-
# @example
|
308
|
-
# Criteria.new('projects.offer').all
|
309
|
-
#
|
310
|
-
# UserRepository has a mapper with a map for 'offer' in this case
|
311
|
-
# all will be called on the db class for this map.
|
312
|
-
#
|
313
|
-
# @return [Criteria]
|
314
|
-
def all
|
315
|
-
@all = true
|
316
|
-
collection
|
317
|
-
self
|
318
|
-
end
|
319
|
-
|
320
|
-
# Used to determin if this criteria belongs to a feature
|
321
|
-
#
|
322
|
-
# @return [Boolean]
|
323
|
-
def feature?
|
324
|
-
!@feature.nil?
|
325
|
-
end
|
326
|
-
|
327
|
-
# Used to determin if this criteria belongs to a global domain
|
328
|
-
#
|
329
|
-
# @return [Boolean]
|
330
|
-
def global_domain?
|
331
|
-
!feature?
|
332
|
-
end
|
333
|
-
|
334
|
-
# Determines if a domain exists in this repo
|
335
|
-
#
|
336
|
-
# @param attr [String]
|
337
|
-
# @param value [Mixed]
|
338
|
-
# @return [Criteria]
|
339
|
-
def exists(domain_attr, value)
|
340
|
-
domain_attr = domain_attr.to_s
|
341
|
-
domain_entity = domain_name
|
342
|
-
if domain_attr.count('.') == 3
|
343
|
-
domain_entity, domain_attr = parse_domain_attr(domain_attr)
|
344
|
-
end
|
345
|
-
@exists = create_expr(domain_entity, domain_attr, eq: value)
|
346
|
-
self
|
347
|
-
end
|
348
|
-
|
349
|
-
# @return [DbEntityExpr]
|
350
|
-
def exists_expr
|
351
|
-
@exists
|
352
|
-
end
|
353
|
-
|
354
|
-
# exec is used to indicate we want a custom method on the repo
|
355
|
-
# to used with this criteria
|
356
|
-
#
|
357
|
-
# @param name [String]
|
358
|
-
# @return [String, Criteria] when used as a dsl it returns the criteria
|
359
|
-
def exec(name = nil)
|
360
|
-
return @exec if name.nil?
|
361
|
-
|
362
|
-
@exec = name.to_sym
|
363
|
-
self
|
364
|
-
end
|
365
|
-
|
366
|
-
def exec?
|
367
|
-
!@exec.nil?
|
368
|
-
end
|
369
|
-
|
370
|
-
# @yield expression and operator
|
371
|
-
# @return [Enumerator] when no block is given
|
372
|
-
def each
|
373
|
-
return exprs.each unless block_given?
|
374
|
-
|
375
|
-
exprs.each do |expr|
|
376
|
-
yield expr[:expr], expr[:relational_op]
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
def error_on_empty_dataset?
|
381
|
-
@error_on_empty
|
382
|
-
end
|
383
|
-
|
384
|
-
# Tells the repo to return an error when entity is not found
|
385
|
-
#
|
386
|
-
# @return Criteria
|
387
|
-
def error_on_empty_dataset!
|
388
|
-
@error_on_empty = true
|
389
|
-
self
|
390
|
-
end
|
391
|
-
|
392
|
-
# Tells the repo to return and empty collection, or nil if single is
|
393
|
-
# invoked, if the entity is not found
|
394
|
-
#
|
395
|
-
# @return Criteria
|
396
|
-
def empty_dataset_is_valid!
|
397
|
-
@error_on_empty = false
|
398
|
-
self
|
399
|
-
end
|
400
|
-
|
401
|
-
private
|
402
|
-
def parse_domain_name(name)
|
403
|
-
if !name.is_a?(String) && !name.respond_to?(:domain_name)
|
404
|
-
fail "domain name must be a string or implement method :domain_name"
|
405
|
-
end
|
406
|
-
|
407
|
-
name = name.domain_name if name.respond_to?(:domain_name)
|
408
|
-
feature, domain = name.split('.', 2)
|
409
|
-
if domain.nil?
|
410
|
-
domain = feature
|
411
|
-
feature = nil
|
412
|
-
end
|
413
|
-
[feature, domain, name]
|
414
|
-
end
|
415
|
-
|
416
|
-
def create_expr(domain_name, domain_attr, value)
|
417
|
-
Expr.new(domain_name, domain_attr, value)
|
418
|
-
end
|
419
|
-
|
420
|
-
def extract_relational_condition(filter_item)
|
421
|
-
relational_or = (filter_item.delete(:or) == true) if filter_item.key?(:or)
|
422
|
-
relational_or
|
423
|
-
end
|
424
|
-
|
425
|
-
def extract_relational_operator(filter_item)
|
426
|
-
operator = "eq"
|
427
|
-
operator = filter_item.delete(:op) if filter_item.key?(:op)
|
428
|
-
if filter_item[:insensitive]
|
429
|
-
operator = "ilike"
|
430
|
-
filter_item.delete(:insensitive)
|
431
|
-
end
|
432
|
-
operator
|
433
|
-
end
|
434
|
-
end
|
435
|
-
end
|
436
|
-
end
|
@@ -1,121 +0,0 @@
|
|
1
|
-
module Appfuel
|
2
|
-
module Repository
|
3
|
-
# The mapping registry holds all entity to db mappings. Mappings are
|
4
|
-
# contained within a DbEntityMapEntry object and are arranged by
|
5
|
-
# entity name. Each entity will hold a hash where the keys are the
|
6
|
-
# attribute names and the value is the entry
|
7
|
-
class MappingRegistry
|
8
|
-
attr_reader :map, :container_root_name
|
9
|
-
|
10
|
-
def initialize(app_name, map)
|
11
|
-
@container_root_name = app_name
|
12
|
-
@map = map
|
13
|
-
end
|
14
|
-
|
15
|
-
# Determine if an entity has been added
|
16
|
-
#
|
17
|
-
# @param entity [String]
|
18
|
-
# @return [Boolean]
|
19
|
-
def domain?(domain_name)
|
20
|
-
map.key?(domain_name)
|
21
|
-
end
|
22
|
-
|
23
|
-
# Determine if an attribute is mapped for a given entity
|
24
|
-
#
|
25
|
-
# @param entity [String] name of the entity
|
26
|
-
# @param attr [String] name of the attribute
|
27
|
-
# @return [Boolean]
|
28
|
-
def domain_attr?(domain_name, domain_attr)
|
29
|
-
return false unless entity?(domain_name)
|
30
|
-
map[domain_name].key?(domain_attr)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns a mapping entry for a given entity
|
34
|
-
#
|
35
|
-
# @raise [RuntimeError] when entity not found
|
36
|
-
# @raise [RuntimeError] when attr not found
|
37
|
-
#
|
38
|
-
# @param entity [String] name of the entity
|
39
|
-
# @param attr [String] name of the attribute
|
40
|
-
# @return [Boolean]
|
41
|
-
def find(domain_name, domain_attr)
|
42
|
-
validate_entity(domain_name)
|
43
|
-
|
44
|
-
unless map[domain_name].key?(domain_attr)
|
45
|
-
fail "Entity (#{domain_name}) attr (#{domain_attr}) not registered"
|
46
|
-
end
|
47
|
-
map[domain_name][domain_attr]
|
48
|
-
end
|
49
|
-
|
50
|
-
# Iterates over all entries for a given entity
|
51
|
-
#
|
52
|
-
# @yield [attr, entry] expose the entity attr name and entry
|
53
|
-
#
|
54
|
-
# @param entity [String] name of the entity
|
55
|
-
# @return [void]
|
56
|
-
def each_domain_attr(domain_name)
|
57
|
-
validate_domain(domain_name)
|
58
|
-
|
59
|
-
map[domain_name].each do |attr, entry|
|
60
|
-
yield attr, entry
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Determine if an column is mapped for a given entity
|
65
|
-
#
|
66
|
-
# @param entity [String] name of the entity
|
67
|
-
# @param attr [String] name of the attribute
|
68
|
-
# @return [Boolean]
|
69
|
-
def persistence_attr_mapped?(domain_name, persistence_attr)
|
70
|
-
result = false
|
71
|
-
each_domain_attr(entity) do |_attr, entry|
|
72
|
-
result = true if persistence_attr == entry.persistence_attr
|
73
|
-
end
|
74
|
-
result
|
75
|
-
end
|
76
|
-
|
77
|
-
# Returns a column name for an entity's attribute
|
78
|
-
#
|
79
|
-
# @raise [RuntimeError] when entity not found
|
80
|
-
# @raise [RuntimeError] when attr not found
|
81
|
-
#
|
82
|
-
# @param entity [String] name of the entity
|
83
|
-
# @param attr [String] name of the attribute
|
84
|
-
# @return [String]
|
85
|
-
def persistence_attr(domain_name, attr)
|
86
|
-
find(entity, attr).persistence_attr
|
87
|
-
end
|
88
|
-
|
89
|
-
# Returns the db model for a given entity attr
|
90
|
-
# container:
|
91
|
-
# domains:
|
92
|
-
# domain_name -> domain
|
93
|
-
# persistence
|
94
|
-
# db:
|
95
|
-
# persistence_name: -> class
|
96
|
-
#
|
97
|
-
# container[persistence.db.name]
|
98
|
-
#
|
99
|
-
# @raise [RuntimeError] when entity not found
|
100
|
-
# @raise [RuntimeError] when attr not found
|
101
|
-
# @raise [Dry::Contriner::Error] when db_class is not registered
|
102
|
-
#
|
103
|
-
# @param entity [String] name of the entity
|
104
|
-
# @param attr [String] name of the attribute
|
105
|
-
# @return [Object]
|
106
|
-
def persistence_class(type, domain_name, attr)
|
107
|
-
entry = find(domain_name, attr)
|
108
|
-
name = entry.persistence[type]
|
109
|
-
key = "persistence.#{type}.#{name}"
|
110
|
-
Appfuel.app_container(root_name)[key]
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
def validate_entity(entity)
|
115
|
-
unless entity?(entity)
|
116
|
-
fail "Entity (#{entity}) is not registered"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|