cloud-templates 0.1.0
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 +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
|