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