cloud-templates 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +29 -0
- data/.simplecov +6 -0
- data/Gemfile +2 -0
- data/LICENSE +201 -0
- data/NOTICE +13 -0
- data/README.md +124 -0
- data/Rakefile +27 -0
- data/cloud-templates.gemspec +27 -0
- data/examples/lib/user_directory/artifacts/catalogized.rb +11 -0
- data/examples/lib/user_directory/artifacts/group.rb +37 -0
- data/examples/lib/user_directory/artifacts/ided.rb +11 -0
- data/examples/lib/user_directory/artifacts/organization.rb +17 -0
- data/examples/lib/user_directory/artifacts/pathed.rb +22 -0
- data/examples/lib/user_directory/artifacts/person.rb +20 -0
- data/examples/lib/user_directory/artifacts/team.rb +31 -0
- data/examples/lib/user_directory/artifacts/unit.rb +24 -0
- data/examples/lib/user_directory/artifacts/user.rb +29 -0
- data/examples/lib/user_directory/render/etc/artifact_view.rb +15 -0
- data/examples/lib/user_directory/render/etc/composite_view.rb +26 -0
- data/examples/lib/user_directory/render/etc/group_view.rb +23 -0
- data/examples/lib/user_directory/render/etc/person_view.rb +19 -0
- data/examples/lib/user_directory/render/etc/registry.rb +33 -0
- data/examples/lib/user_directory/render/etc/user_view.rb +35 -0
- data/examples/lib/user_directory/render/etc.rb +3 -0
- data/examples/lib/user_directory/render/ldap/artifact_view.rb +27 -0
- data/examples/lib/user_directory/render/ldap/composite_view.rb +32 -0
- data/examples/lib/user_directory/render/ldap/group_view.rb +28 -0
- data/examples/lib/user_directory/render/ldap/organization_view.rb +26 -0
- data/examples/lib/user_directory/render/ldap/person_view.rb +39 -0
- data/examples/lib/user_directory/render/ldap/registry.rb +16 -0
- data/examples/lib/user_directory/render/ldap/unit_view.rb +26 -0
- data/examples/lib/user_directory/render/ldap/user_view.rb +39 -0
- data/examples/lib/user_directory/render/ldap.rb +3 -0
- data/examples/lib/user_directory/utils.rb +18 -0
- data/examples/lib/user_directory.rb +23 -0
- data/examples/lib_path.rb +2 -0
- data/examples/spec/spec_helper.rb +1 -0
- data/examples/spec/user_directory_spec.rb +568 -0
- data/lib/aws/templates/artifact.rb +140 -0
- data/lib/aws/templates/composite.rb +178 -0
- data/lib/aws/templates/exceptions.rb +221 -0
- data/lib/aws/templates/render/registry.rb +60 -0
- data/lib/aws/templates/render/utils/base_type_views.rb +131 -0
- data/lib/aws/templates/render/view.rb +127 -0
- data/lib/aws/templates/render.rb +72 -0
- data/lib/aws/templates/utils/artifact_storage.rb +141 -0
- data/lib/aws/templates/utils/contextualized/filters.rb +437 -0
- data/lib/aws/templates/utils/contextualized/hash.rb +13 -0
- data/lib/aws/templates/utils/contextualized/nil.rb +13 -0
- data/lib/aws/templates/utils/contextualized/proc.rb +13 -0
- data/lib/aws/templates/utils/contextualized.rb +113 -0
- data/lib/aws/templates/utils/default.rb +185 -0
- data/lib/aws/templates/utils/dependency/enumerable.rb +13 -0
- data/lib/aws/templates/utils/dependency/object.rb +46 -0
- data/lib/aws/templates/utils/dependency.rb +121 -0
- data/lib/aws/templates/utils/dependent.rb +28 -0
- data/lib/aws/templates/utils/inheritable.rb +52 -0
- data/lib/aws/templates/utils/late_bound.rb +89 -0
- data/lib/aws/templates/utils/memoized.rb +27 -0
- data/lib/aws/templates/utils/named.rb +19 -0
- data/lib/aws/templates/utils/options.rb +279 -0
- data/lib/aws/templates/utils/parametrized/constraints.rb +423 -0
- data/lib/aws/templates/utils/parametrized/getters.rb +293 -0
- data/lib/aws/templates/utils/parametrized/guarded.rb +32 -0
- data/lib/aws/templates/utils/parametrized/mapper.rb +73 -0
- data/lib/aws/templates/utils/parametrized/nested.rb +72 -0
- data/lib/aws/templates/utils/parametrized/transformations.rb +660 -0
- data/lib/aws/templates/utils/parametrized.rb +240 -0
- data/lib/aws/templates/utils.rb +219 -0
- data/lib/aws/templates.rb +16 -0
- data/spec/aws/templates/artifact_spec.rb +161 -0
- data/spec/aws/templates/composite_spec.rb +361 -0
- data/spec/aws/templates/render/utils/base_type_views_spec.rb +104 -0
- data/spec/aws/templates/render_spec.rb +62 -0
- data/spec/aws/templates/utils/as_named_spec.rb +31 -0
- data/spec/aws/templates/utils/contextualized/filters_spec.rb +108 -0
- data/spec/aws/templates/utils/contextualized_spec.rb +115 -0
- data/spec/aws/templates/utils/late_bound_spec.rb +52 -0
- data/spec/aws/templates/utils/options_spec.rb +67 -0
- data/spec/aws/templates/utils/parametrized/constraint_spec.rb +175 -0
- data/spec/aws/templates/utils/parametrized/getters_spec.rb +139 -0
- data/spec/aws/templates/utils/parametrized/transformation_spec.rb +314 -0
- data/spec/aws/templates/utils/parametrized_spec.rb +241 -0
- data/spec/spec_helper.rb +6 -0
- metadata +244 -0
@@ -0,0 +1,437 @@
|
|
1
|
+
require 'aws/templates/exceptions'
|
2
|
+
require 'aws/templates/utils'
|
3
|
+
require 'aws/templates/utils/options'
|
4
|
+
require 'aws/templates/utils/inheritable'
|
5
|
+
|
6
|
+
module Aws
|
7
|
+
module Templates
|
8
|
+
module Utils
|
9
|
+
##
|
10
|
+
# Filtered mixin.
|
11
|
+
#
|
12
|
+
# It implements class instance-based definitions of option filters.
|
13
|
+
# Filters are options hash alterations and transformations
|
14
|
+
# which are defined per-class basis and applied according to class
|
15
|
+
# hierarchy when invoked. The target mixing entity should be either
|
16
|
+
# Module or Class. In the former case it's possible to model set of
|
17
|
+
# object which have common traits organized as an arbitrary graph
|
18
|
+
# with many-to-many relationship.
|
19
|
+
#
|
20
|
+
# Important difference from defaults is that the transformations
|
21
|
+
# are performed on a copy of options returned by a separate "filtered"
|
22
|
+
# accessor and not in place.
|
23
|
+
module Contextualized
|
24
|
+
include Inheritable
|
25
|
+
|
26
|
+
##
|
27
|
+
# Filter functor class
|
28
|
+
#
|
29
|
+
# A filter is a Proc accepting input hash and providing output
|
30
|
+
# hash which is expected to be a permutation of the input.
|
31
|
+
# The proc is executed in instance context so instance methods can
|
32
|
+
# be used for calculation.
|
33
|
+
#
|
34
|
+
# The class implements functor pattern through to_proc method and
|
35
|
+
# closure. Essentially, all filters can be used everywhere where
|
36
|
+
# a block is expected.
|
37
|
+
#
|
38
|
+
# It provides protected method filter which should be overriden in
|
39
|
+
# all concrete filter classes.
|
40
|
+
class Filter
|
41
|
+
##
|
42
|
+
# Proc proxy
|
43
|
+
#
|
44
|
+
# Just passes opts to the proc the filter was initialized with. It is used internaly.
|
45
|
+
class Proxy < Filter
|
46
|
+
attr_reader :proc
|
47
|
+
|
48
|
+
def initialize(prc, &blk)
|
49
|
+
@proc = prc || blk
|
50
|
+
end
|
51
|
+
|
52
|
+
def filter(opts, memo, instance)
|
53
|
+
instance.instance_exec(opts, memo, &proc)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# No-op filter
|
59
|
+
#
|
60
|
+
# No-op filter or identity filter doesn't perform any operations on passed options. The
|
61
|
+
# role of this filter is to play the role of identity function in par with lambda
|
62
|
+
# calculus.
|
63
|
+
#
|
64
|
+
# === Examples
|
65
|
+
#
|
66
|
+
# class Piece
|
67
|
+
# include Aws::Templates::Utils::Contextualized
|
68
|
+
#
|
69
|
+
# contextualize filter(:identity)
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# i = Piece.new
|
73
|
+
# opts = Options.new(a: { q: 1 }, b: 2, c: { d: { r: 5 }, e: 1 })
|
74
|
+
# opts.filter(i.filter) # => {}
|
75
|
+
class Identity < Filter
|
76
|
+
def self.new
|
77
|
+
@singleton ||= super()
|
78
|
+
end
|
79
|
+
|
80
|
+
def filter(_, memo, _)
|
81
|
+
memo
|
82
|
+
end
|
83
|
+
|
84
|
+
def &(other)
|
85
|
+
other.to_filter
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Statically scoped filter
|
91
|
+
#
|
92
|
+
# Scoped filter wraps whatever Proc obejct passed to it into specified scope for
|
93
|
+
# execution. So whatever the scope the filter is called in, it will always be evaluated
|
94
|
+
# in the same scope specified at creation.
|
95
|
+
#
|
96
|
+
# The filter is used by the internal mechanics of the framework.
|
97
|
+
class Scoped < Filter
|
98
|
+
attr_reader :scoped_filter
|
99
|
+
attr_reader :scope
|
100
|
+
|
101
|
+
def initialize(fltr, scp)
|
102
|
+
@scoped_filter = _check_filter(fltr)
|
103
|
+
@scope = scp
|
104
|
+
end
|
105
|
+
|
106
|
+
def filter(options, memo, _)
|
107
|
+
scope.instance_exec(options, memo, &scoped_filter)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def _check_filter(fltr)
|
113
|
+
raise "#{fltr} is not a filter" unless fltr.respond_to?(:to_proc)
|
114
|
+
fltr
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Add all options into the context
|
120
|
+
#
|
121
|
+
# The filter performs deep copy of entire options hash with consecutive merge into the
|
122
|
+
# resulting context
|
123
|
+
#
|
124
|
+
# === Example
|
125
|
+
#
|
126
|
+
# class Piece
|
127
|
+
# contextualize filter(:copy)
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# i = Piece.new()
|
131
|
+
# opts = Options.new(a: { q: 1 }, b: 2, c: { d: { r: 5 }, e: 1 })
|
132
|
+
# opts.filter(i.filter) # => { a: { q: 1 }, b: 2, c: { d: { r: 5 }, e: 1 } }
|
133
|
+
class Copy < Filter
|
134
|
+
PRE_FILTER = %i[label root parent].freeze
|
135
|
+
|
136
|
+
def filter(opts, memo, _)
|
137
|
+
result = Utils.deep_dup(opts.to_hash)
|
138
|
+
PRE_FILTER.each { |k| result.delete(k) }
|
139
|
+
Utils.merge(memo, result)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Base class for recursive operations
|
145
|
+
#
|
146
|
+
# Internally used by Add and Remove filters.
|
147
|
+
class RecursiveSchemaFilter < Filter
|
148
|
+
attr_reader :scheme
|
149
|
+
|
150
|
+
def initialize(*args)
|
151
|
+
schm = if args.last.respond_to?(:to_hash)
|
152
|
+
args.each_with_object(args.pop) do |field, hsh|
|
153
|
+
hsh[field] = nil
|
154
|
+
hsh
|
155
|
+
end
|
156
|
+
else
|
157
|
+
args
|
158
|
+
end
|
159
|
+
|
160
|
+
@scheme = _check_scheme(schm)
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def _check_scheme(schm)
|
166
|
+
if schm.respond_to?(:to_hash)
|
167
|
+
schm.to_hash.each_pair { |_, sub| _check_scheme(sub) unless sub.nil? }
|
168
|
+
elsif !schm.respond_to?(:to_a)
|
169
|
+
raise "#{schm.inspect} is not appropriate branch in the scheme"
|
170
|
+
end
|
171
|
+
|
172
|
+
schm
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Add specified keys into the hash
|
178
|
+
#
|
179
|
+
# Selective version of Copy filter. It adds key-value pairs or whole subtrees from
|
180
|
+
# options into the memo hash. It does this according to specified schema represented
|
181
|
+
# by combination of nested hashes and arrays. User can specify addition of values
|
182
|
+
# at arbitrary depth in options hash hierarchy with arbitrar granularity.
|
183
|
+
#
|
184
|
+
# === Example
|
185
|
+
#
|
186
|
+
# class Piece
|
187
|
+
# include Aws::Templates::Utils::Contextualized
|
188
|
+
#
|
189
|
+
# contextualize filter(:add, :a, :b, c: [:d])
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
# i = Piece.new()
|
193
|
+
# opts = Options.new(a: { q: 1 }, b: 2, c: { d: { r: 5 }, e: 1 })
|
194
|
+
# opts.filter(i.filter) # => { a: { q: 1 }, b: 2, c: { d: { r: 5 } } }
|
195
|
+
class Add < RecursiveSchemaFilter
|
196
|
+
def filter(options, memo, _)
|
197
|
+
_recurse_add(options, memo, scheme)
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def _recurse_add(opts, memo, schm)
|
203
|
+
return unless Utils.recursive?(opts)
|
204
|
+
|
205
|
+
if Utils.hashable?(schm)
|
206
|
+
_scheme_add(opts, memo, schm.to_hash)
|
207
|
+
elsif Utils.list?(schm)
|
208
|
+
_list_add(opts, memo, schm.to_ary)
|
209
|
+
end
|
210
|
+
|
211
|
+
memo
|
212
|
+
end
|
213
|
+
|
214
|
+
def _list_add(opts, memo, list)
|
215
|
+
list.each { |field| memo[field] = Utils.merge(memo[field], opts[field]) }
|
216
|
+
end
|
217
|
+
|
218
|
+
def _scheme_add(opts, memo, schm)
|
219
|
+
schm.each_pair do |field, sub_scheme|
|
220
|
+
next unless opts.include?(field)
|
221
|
+
memo[field] = if sub_scheme.nil?
|
222
|
+
Utils.merge(memo[field], opts[field])
|
223
|
+
else
|
224
|
+
_recurse_add(opts[field], memo[field] || {}, sub_scheme)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
##
|
231
|
+
# Remove specified keys from hash
|
232
|
+
#
|
233
|
+
# The filter performs removal of values from options hash
|
234
|
+
# according to specified schema represented by combination of
|
235
|
+
# nested hashes and arrays. User can specify removal of values
|
236
|
+
# at arbitrary depth in options hash hierarchy with arbitrary
|
237
|
+
# granularity.
|
238
|
+
#
|
239
|
+
# === Example
|
240
|
+
#
|
241
|
+
# class Piece
|
242
|
+
# include Aws::Templates::Utils::Contextualized
|
243
|
+
#
|
244
|
+
# contextualize filter(:copy) & filter(:remove, :a, :b, c: [:d])
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# i = Piece.new()
|
248
|
+
# opts = Options.new(a: { q: 1 }, b: 2, c: { d: { r: 5 }, e: 1 })
|
249
|
+
# opts.filter(i.filter) # => { c: { e: 1 } }
|
250
|
+
class Remove < RecursiveSchemaFilter
|
251
|
+
def filter(_, memo, _)
|
252
|
+
_recurse_remove(memo, scheme)
|
253
|
+
memo
|
254
|
+
end
|
255
|
+
|
256
|
+
private
|
257
|
+
|
258
|
+
def _recurse_remove(opts, schm)
|
259
|
+
return unless Utils.recursive?(opts)
|
260
|
+
|
261
|
+
if Utils.hashable?(schm)
|
262
|
+
_scheme_remove(opts, schm.to_hash)
|
263
|
+
elsif Utils.list?(schm)
|
264
|
+
_list_remove(opts, schm.to_ary)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def _list_remove(opts, list)
|
269
|
+
list.each { |field| opts.delete(field) }
|
270
|
+
end
|
271
|
+
|
272
|
+
def _scheme_remove(opts, schm)
|
273
|
+
schm.each_pair do |field, sub_scheme|
|
274
|
+
if sub_scheme.nil?
|
275
|
+
opts.delete(field)
|
276
|
+
elsif opts.include?(field)
|
277
|
+
_recurse_remove(opts[field], sub_scheme)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
##
|
284
|
+
# Override specified keys in options hash
|
285
|
+
#
|
286
|
+
# The filter performs merge the hash passed at initialization with
|
287
|
+
# options hash. Either hash itself or block returning a hash
|
288
|
+
# can be specified. The block will be evaluated in instance context
|
289
|
+
# so all instance methods are accessible.
|
290
|
+
#
|
291
|
+
# === Example
|
292
|
+
#
|
293
|
+
# class Piece
|
294
|
+
# include Aws::Templates::Utils::Contextualized
|
295
|
+
#
|
296
|
+
# contextualize filter(:copy) & filter(:override, a: 12, b: 15, c: { d: 30 })
|
297
|
+
# end
|
298
|
+
#
|
299
|
+
# i = Piece.new
|
300
|
+
# opts = Options.new(c: { e: 1 })
|
301
|
+
# opts.filter(i.filter) # => { a: 12, b: 15, c: { d: 30, e: 1 } }
|
302
|
+
class Override < Filter
|
303
|
+
attr_reader :override
|
304
|
+
|
305
|
+
def initialize(override = nil, &override_block)
|
306
|
+
@override = _check_override_type(override || override_block)
|
307
|
+
end
|
308
|
+
|
309
|
+
def filter(_, memo, instance)
|
310
|
+
Utils.merge(
|
311
|
+
memo,
|
312
|
+
if override.respond_to?(:to_hash)
|
313
|
+
override
|
314
|
+
elsif override.respond_to?(:to_proc)
|
315
|
+
instance.instance_eval(&override)
|
316
|
+
end
|
317
|
+
)
|
318
|
+
end
|
319
|
+
|
320
|
+
private
|
321
|
+
|
322
|
+
def _check_override_type(ovrr)
|
323
|
+
raise "Wrong override value: #{ovrr.inspect}" unless _proper_override_type?(ovrr)
|
324
|
+
ovrr
|
325
|
+
end
|
326
|
+
|
327
|
+
def _proper_override_type?(ovrr)
|
328
|
+
ovrr.respond_to?(:to_hash) || ovrr.respond_to?(:to_proc)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
##
|
333
|
+
# Chain filters
|
334
|
+
#
|
335
|
+
# The filter chains all passed filters to have chained
|
336
|
+
# filter semantics.
|
337
|
+
#
|
338
|
+
# === Example
|
339
|
+
#
|
340
|
+
# class Piece
|
341
|
+
# include Aws::Templates::Utils::Contextualized
|
342
|
+
#
|
343
|
+
# contextualize filter(:copy) & filter(:remove, :c) & filter(:override, a: 12, b: 15)
|
344
|
+
# end
|
345
|
+
#
|
346
|
+
# i = Piece.new
|
347
|
+
# opts = Options.new(c: { e: 1 })
|
348
|
+
# opts.filter(i.filter) # => { a: 12, b: 15 }
|
349
|
+
class Chain < Filter
|
350
|
+
attr_reader :filters
|
351
|
+
|
352
|
+
def initialize(*flts)
|
353
|
+
wrong_objects = flts.reject { |f| f.respond_to?(:to_proc) }
|
354
|
+
unless wrong_objects.empty?
|
355
|
+
raise(
|
356
|
+
"The following objects are not filters: #{wrong_objects.inspect}"
|
357
|
+
)
|
358
|
+
end
|
359
|
+
|
360
|
+
@filters = flts
|
361
|
+
end
|
362
|
+
|
363
|
+
def filter(options, memo, instance)
|
364
|
+
filters.inject(memo) { |acc, elem| instance.instance_exec(options, acc, &elem) }
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
##
|
369
|
+
# Chain filters
|
370
|
+
def &(other)
|
371
|
+
fltr = other.to_filter
|
372
|
+
return self if fltr.is_a?(Identity)
|
373
|
+
Chain.new(self, fltr)
|
374
|
+
end
|
375
|
+
|
376
|
+
##
|
377
|
+
# Creates closure with filter invocation
|
378
|
+
#
|
379
|
+
# It's an interface method required for Filter to expose
|
380
|
+
# functor properties. It encloses invocation of Filter
|
381
|
+
# filter method into a closure. The closure itself is
|
382
|
+
# executed in the context of Filtered instance which provides
|
383
|
+
# proper set "self" variable.
|
384
|
+
#
|
385
|
+
# The closure itself accepts just one parameter:
|
386
|
+
# * +opts+ - input hash to be filtered
|
387
|
+
# ...where instance is assumed from self
|
388
|
+
def to_proc
|
389
|
+
fltr = self
|
390
|
+
->(opts, memo = {}) { fltr.filter(opts, memo, self) }
|
391
|
+
end
|
392
|
+
|
393
|
+
##
|
394
|
+
# Filter method
|
395
|
+
#
|
396
|
+
# * +opts+ - input hash to be filtered
|
397
|
+
# * +instance+ - the instance filter is executed in
|
398
|
+
def filter(opts, memo, instance); end
|
399
|
+
|
400
|
+
def to_filter
|
401
|
+
self
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
##
|
406
|
+
# Mixin for filter factory method
|
407
|
+
#
|
408
|
+
# Adds filter factory method to the target
|
409
|
+
module FilterFactory
|
410
|
+
##
|
411
|
+
# Filter factory method
|
412
|
+
#
|
413
|
+
# It creates a filter based on type identifier and parameters with optional block which
|
414
|
+
# will be passed unchanged to the filter constructor
|
415
|
+
# * +type+ - type identifier; can by either symbol or string
|
416
|
+
# * +args+ - filter constructor arguments
|
417
|
+
# * +blk+ - optional block to be passed to filter constructor
|
418
|
+
def filter(type, *args, &blk)
|
419
|
+
Filter.const_get(type.to_s.capitalize).new(*args, &blk)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
##
|
424
|
+
# Class-level mixins
|
425
|
+
#
|
426
|
+
# It's a DSL extension to declaratively define context filters
|
427
|
+
class_scope do
|
428
|
+
include FilterFactory
|
429
|
+
end
|
430
|
+
|
431
|
+
instance_scope do
|
432
|
+
include FilterFactory
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'aws/templates/utils/contextualized'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Hash class patch
|
5
|
+
#
|
6
|
+
# Adds to_filter method converting a hash into an Override filter
|
7
|
+
class Hash
|
8
|
+
##
|
9
|
+
# Convert to Override filter
|
10
|
+
def to_filter
|
11
|
+
Aws::Templates::Utils::Contextualized::Filter::Override.new(self)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'aws/templates/utils/contextualized'
|
2
|
+
|
3
|
+
##
|
4
|
+
# NilClass class patch
|
5
|
+
#
|
6
|
+
# Adds to_filter method converting nil into the Identity filter
|
7
|
+
class NilClass
|
8
|
+
##
|
9
|
+
# Convert nil to Identity filter
|
10
|
+
def to_filter
|
11
|
+
Aws::Templates::Utils::Contextualized::Filter::Identity.new
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'aws/templates/utils/contextualized'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Proc class patch
|
5
|
+
#
|
6
|
+
# Adds to_filter method proxying a Proc through Filter interface object
|
7
|
+
class Proc
|
8
|
+
##
|
9
|
+
# Proxy the Proc through Proxy filter object
|
10
|
+
def to_filter
|
11
|
+
Aws::Templates::Utils::Contextualized::Filter::Proxy.new(self)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'aws/templates/utils/contextualized/filters'
|
2
|
+
require 'aws/templates/utils/contextualized/proc'
|
3
|
+
require 'aws/templates/utils/contextualized/hash'
|
4
|
+
require 'aws/templates/utils/contextualized/nil'
|
5
|
+
require 'aws/templates/utils/inheritable'
|
6
|
+
|
7
|
+
module Aws
|
8
|
+
module Templates
|
9
|
+
module Utils
|
10
|
+
##
|
11
|
+
# Contextualized mixin.
|
12
|
+
#
|
13
|
+
# It implements class instance-based definitions of context filters.
|
14
|
+
# Filters are options hash alterations and transformations
|
15
|
+
# which are defined per-class basis and combined according to class
|
16
|
+
# hierarchy when invoked. The target mixing entity should be either
|
17
|
+
# Module or Class. In the former case it's possible to model set of
|
18
|
+
# object which have common traits organized as an arbitrary graph
|
19
|
+
# with many-to-many relationship.
|
20
|
+
#
|
21
|
+
# Important difference from defaults is that the final result returned
|
22
|
+
# by "filter" mixed method is a functor. No operations are performed
|
23
|
+
# on options.
|
24
|
+
module Contextualized
|
25
|
+
include Inheritable
|
26
|
+
|
27
|
+
instance_scope do
|
28
|
+
##
|
29
|
+
# Context functor
|
30
|
+
#
|
31
|
+
# It's a mixin method returning resulting context filter functor with all
|
32
|
+
# contexts appropriatelly processed. The algorithm is to walk down
|
33
|
+
# the hierarchy of the class and aggregate all context filters from its
|
34
|
+
# ancestors prioritizing the ones defined earlier in the class hierarchy.
|
35
|
+
# The method is working correctly with both parent classes and all
|
36
|
+
# Contextualized mixins used in between.
|
37
|
+
def context
|
38
|
+
@context ||= filter(:scoped, self.class.context, self)
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Enclose block into local context
|
43
|
+
#
|
44
|
+
# You can apply additional filters for the block and make it
|
45
|
+
# the context of the block so only code in this closure will
|
46
|
+
# have defined filter alterations.
|
47
|
+
def contextualize(arg, &blk)
|
48
|
+
if blk
|
49
|
+
clone._set_context(context.scoped_filter & arg).instance_exec(&blk)
|
50
|
+
else
|
51
|
+
filter(:scoped, context.scoped_filter & arg, self)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def _set_context(new_context)
|
58
|
+
@context = filter(:scoped, new_context, self)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Class-level mixins
|
65
|
+
#
|
66
|
+
# It's a DSL extension to declaratively define context filters
|
67
|
+
class_scope do
|
68
|
+
##
|
69
|
+
# Context filter assigned to the module
|
70
|
+
#
|
71
|
+
# Class-level accessor of a filter to be a part of context.
|
72
|
+
# The method returns only the filter for the current class
|
73
|
+
# without consideration of the class hierarchy.
|
74
|
+
def module_context
|
75
|
+
@module_context ||= filter(:identity)
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Module's context filter
|
80
|
+
#
|
81
|
+
# Class-level accessor of a filter to be a part of context.
|
82
|
+
# The method returns aggregate filter include module's own filters
|
83
|
+
# concatenated with all ancestor's filters.
|
84
|
+
def context
|
85
|
+
@context ||= _contextualized_ancestors.inject(filter(:identity)) do |acc, mod|
|
86
|
+
acc & mod.module_context
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def _contextualized_ancestors
|
91
|
+
ancestors
|
92
|
+
.select { |mod| (mod != Contextualized) && mod.ancestors.include?(Contextualized) }
|
93
|
+
.reverse
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Add context filter
|
98
|
+
#
|
99
|
+
# The class method is used to build hierarchical context
|
100
|
+
# filtering pipeline using language-provided features such as
|
101
|
+
# class inheritance and introspection. You can
|
102
|
+
# specify either a lamda, a hash or a filter functor.
|
103
|
+
#
|
104
|
+
# If no parameters are passed at all, ArgumentError will be thrown.
|
105
|
+
def contextualize(fltr)
|
106
|
+
raise ArgumentError.new('Proc should be specified') unless fltr
|
107
|
+
@module_context = module_context & fltr
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|