cloud-templates 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +29 -0
  3. data/.simplecov +6 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +201 -0
  6. data/NOTICE +13 -0
  7. data/README.md +124 -0
  8. data/Rakefile +27 -0
  9. data/cloud-templates.gemspec +27 -0
  10. data/examples/lib/user_directory/artifacts/catalogized.rb +11 -0
  11. data/examples/lib/user_directory/artifacts/group.rb +37 -0
  12. data/examples/lib/user_directory/artifacts/ided.rb +11 -0
  13. data/examples/lib/user_directory/artifacts/organization.rb +17 -0
  14. data/examples/lib/user_directory/artifacts/pathed.rb +22 -0
  15. data/examples/lib/user_directory/artifacts/person.rb +20 -0
  16. data/examples/lib/user_directory/artifacts/team.rb +31 -0
  17. data/examples/lib/user_directory/artifacts/unit.rb +24 -0
  18. data/examples/lib/user_directory/artifacts/user.rb +29 -0
  19. data/examples/lib/user_directory/render/etc/artifact_view.rb +15 -0
  20. data/examples/lib/user_directory/render/etc/composite_view.rb +26 -0
  21. data/examples/lib/user_directory/render/etc/group_view.rb +23 -0
  22. data/examples/lib/user_directory/render/etc/person_view.rb +19 -0
  23. data/examples/lib/user_directory/render/etc/registry.rb +33 -0
  24. data/examples/lib/user_directory/render/etc/user_view.rb +35 -0
  25. data/examples/lib/user_directory/render/etc.rb +3 -0
  26. data/examples/lib/user_directory/render/ldap/artifact_view.rb +27 -0
  27. data/examples/lib/user_directory/render/ldap/composite_view.rb +32 -0
  28. data/examples/lib/user_directory/render/ldap/group_view.rb +28 -0
  29. data/examples/lib/user_directory/render/ldap/organization_view.rb +26 -0
  30. data/examples/lib/user_directory/render/ldap/person_view.rb +39 -0
  31. data/examples/lib/user_directory/render/ldap/registry.rb +16 -0
  32. data/examples/lib/user_directory/render/ldap/unit_view.rb +26 -0
  33. data/examples/lib/user_directory/render/ldap/user_view.rb +39 -0
  34. data/examples/lib/user_directory/render/ldap.rb +3 -0
  35. data/examples/lib/user_directory/utils.rb +18 -0
  36. data/examples/lib/user_directory.rb +23 -0
  37. data/examples/lib_path.rb +2 -0
  38. data/examples/spec/spec_helper.rb +1 -0
  39. data/examples/spec/user_directory_spec.rb +568 -0
  40. data/lib/aws/templates/artifact.rb +140 -0
  41. data/lib/aws/templates/composite.rb +178 -0
  42. data/lib/aws/templates/exceptions.rb +221 -0
  43. data/lib/aws/templates/render/registry.rb +60 -0
  44. data/lib/aws/templates/render/utils/base_type_views.rb +131 -0
  45. data/lib/aws/templates/render/view.rb +127 -0
  46. data/lib/aws/templates/render.rb +72 -0
  47. data/lib/aws/templates/utils/artifact_storage.rb +141 -0
  48. data/lib/aws/templates/utils/contextualized/filters.rb +437 -0
  49. data/lib/aws/templates/utils/contextualized/hash.rb +13 -0
  50. data/lib/aws/templates/utils/contextualized/nil.rb +13 -0
  51. data/lib/aws/templates/utils/contextualized/proc.rb +13 -0
  52. data/lib/aws/templates/utils/contextualized.rb +113 -0
  53. data/lib/aws/templates/utils/default.rb +185 -0
  54. data/lib/aws/templates/utils/dependency/enumerable.rb +13 -0
  55. data/lib/aws/templates/utils/dependency/object.rb +46 -0
  56. data/lib/aws/templates/utils/dependency.rb +121 -0
  57. data/lib/aws/templates/utils/dependent.rb +28 -0
  58. data/lib/aws/templates/utils/inheritable.rb +52 -0
  59. data/lib/aws/templates/utils/late_bound.rb +89 -0
  60. data/lib/aws/templates/utils/memoized.rb +27 -0
  61. data/lib/aws/templates/utils/named.rb +19 -0
  62. data/lib/aws/templates/utils/options.rb +279 -0
  63. data/lib/aws/templates/utils/parametrized/constraints.rb +423 -0
  64. data/lib/aws/templates/utils/parametrized/getters.rb +293 -0
  65. data/lib/aws/templates/utils/parametrized/guarded.rb +32 -0
  66. data/lib/aws/templates/utils/parametrized/mapper.rb +73 -0
  67. data/lib/aws/templates/utils/parametrized/nested.rb +72 -0
  68. data/lib/aws/templates/utils/parametrized/transformations.rb +660 -0
  69. data/lib/aws/templates/utils/parametrized.rb +240 -0
  70. data/lib/aws/templates/utils.rb +219 -0
  71. data/lib/aws/templates.rb +16 -0
  72. data/spec/aws/templates/artifact_spec.rb +161 -0
  73. data/spec/aws/templates/composite_spec.rb +361 -0
  74. data/spec/aws/templates/render/utils/base_type_views_spec.rb +104 -0
  75. data/spec/aws/templates/render_spec.rb +62 -0
  76. data/spec/aws/templates/utils/as_named_spec.rb +31 -0
  77. data/spec/aws/templates/utils/contextualized/filters_spec.rb +108 -0
  78. data/spec/aws/templates/utils/contextualized_spec.rb +115 -0
  79. data/spec/aws/templates/utils/late_bound_spec.rb +52 -0
  80. data/spec/aws/templates/utils/options_spec.rb +67 -0
  81. data/spec/aws/templates/utils/parametrized/constraint_spec.rb +175 -0
  82. data/spec/aws/templates/utils/parametrized/getters_spec.rb +139 -0
  83. data/spec/aws/templates/utils/parametrized/transformation_spec.rb +314 -0
  84. data/spec/aws/templates/utils/parametrized_spec.rb +241 -0
  85. data/spec/spec_helper.rb +6 -0
  86. 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