serega 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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/VERSION +1 -0
  3. data/lib/serega/attribute.rb +130 -0
  4. data/lib/serega/config.rb +48 -0
  5. data/lib/serega/convert.rb +45 -0
  6. data/lib/serega/convert_item.rb +37 -0
  7. data/lib/serega/helpers/serializer_class_helper.rb +11 -0
  8. data/lib/serega/map.rb +49 -0
  9. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +61 -0
  10. data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +95 -0
  11. data/lib/serega/plugins/context_metadata/context_metadata.rb +74 -0
  12. data/lib/serega/plugins/formatters/formatters.rb +49 -0
  13. data/lib/serega/plugins/hide_nil/hide_nil.rb +80 -0
  14. data/lib/serega/plugins/metadata/meta_attribute.rb +74 -0
  15. data/lib/serega/plugins/metadata/metadata.rb +123 -0
  16. data/lib/serega/plugins/metadata/validations/check_block.rb +45 -0
  17. data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +31 -0
  18. data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +31 -0
  19. data/lib/serega/plugins/metadata/validations/check_opts.rb +41 -0
  20. data/lib/serega/plugins/metadata/validations/check_path.rb +53 -0
  21. data/lib/serega/plugins/preloads/lib/enum_deep_freeze.rb +17 -0
  22. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +53 -0
  23. data/lib/serega/plugins/preloads/lib/main_preload_path.rb +40 -0
  24. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +60 -0
  25. data/lib/serega/plugins/preloads/preloads.rb +100 -0
  26. data/lib/serega/plugins/preloads/validations/check_opt_preload_path.rb +38 -0
  27. data/lib/serega/plugins/presenter/presenter.rb +111 -0
  28. data/lib/serega/plugins/root/root.rb +65 -0
  29. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +64 -0
  30. data/lib/serega/plugins/string_modifiers/string_modifiers.rb +32 -0
  31. data/lib/serega/plugins/validate_modifiers/validate.rb +51 -0
  32. data/lib/serega/plugins/validate_modifiers/validate_modifiers.rb +44 -0
  33. data/lib/serega/plugins.rb +51 -0
  34. data/lib/serega/utils/as_json.rb +35 -0
  35. data/lib/serega/utils/enum_deep_dup.rb +43 -0
  36. data/lib/serega/utils/to_hash.rb +52 -0
  37. data/lib/serega/utils/to_json.rb +22 -0
  38. data/lib/serega/validations/attribute/check_block.rb +81 -0
  39. data/lib/serega/validations/attribute/check_name.rb +45 -0
  40. data/lib/serega/validations/attribute/check_opt_hide.rb +20 -0
  41. data/lib/serega/validations/attribute/check_opt_many.rb +20 -0
  42. data/lib/serega/validations/attribute/check_opt_method.rb +25 -0
  43. data/lib/serega/validations/attribute/check_opt_serializer.rb +36 -0
  44. data/lib/serega/validations/attribute/check_opts.rb +36 -0
  45. data/lib/serega/validations/check_allowed_keys.rb +13 -0
  46. data/lib/serega/validations/check_opt_is_bool.rb +14 -0
  47. data/lib/serega/validations/check_opt_is_hash.rb +14 -0
  48. data/lib/serega/version.rb +5 -0
  49. data/lib/serega.rb +265 -0
  50. metadata +94 -0
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Metadata
6
+ #
7
+ # Stores Attribute data
8
+ #
9
+ class MetaAttribute
10
+ #
11
+ # Stores Attribute instance methods
12
+ #
13
+ module InstanceMethods
14
+ # @return [Symbol] Meta attribute name
15
+ attr_reader :name
16
+
17
+ # @return [Symbol] Meta attribute full path
18
+ attr_reader :path
19
+
20
+ # @return [Proc] Meta attribute options
21
+ attr_reader :opts
22
+
23
+ # @return [Proc] Meta attribute originally added block
24
+ attr_reader :block
25
+
26
+ #
27
+ # Initializes new meta attribute
28
+ #
29
+ # @param path [Array<Symbol, String>] Path for metadata of attribute
30
+ #
31
+ # @param opts [Hash] metadata attribute options
32
+ #
33
+ # @param block [Proc] Proc that receives object(s) and context and finds value
34
+ #
35
+ def initialize(path:, opts:, block:)
36
+ check(path, opts, block)
37
+
38
+ @name = path.join(".").to_sym
39
+ @path = Utils::EnumDeepDup.call(path)
40
+ @opts = Utils::EnumDeepDup.call(opts)
41
+ @block = block
42
+ end
43
+
44
+ #
45
+ # Finds attribute value
46
+ #
47
+ # @param object [Object] Serialized object(s)
48
+ # @param context [Hash, nil] Serialization context
49
+ #
50
+ # @return [Object] Serialized meta attribute value
51
+ #
52
+ def value(object, context)
53
+ block.call(object, context)
54
+ end
55
+
56
+ def hide?(value)
57
+ (opts[:hide_nil] && value.nil?) || (opts[:hide_empty] && value.empty?)
58
+ end
59
+
60
+ private
61
+
62
+ def check(path, opts, block)
63
+ CheckPath.call(path)
64
+ CheckOpts.call(opts, self.class.serializer_class.config[:metadata][:attribute_keys])
65
+ CheckBlock.call(block)
66
+ end
67
+ end
68
+
69
+ extend Serega::Helpers::SerializerClassHelper
70
+ include InstanceMethods
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Metadata
6
+ def self.plugin_name
7
+ :metadata
8
+ end
9
+
10
+ def self.before_load_plugin(serializer_class, **opts)
11
+ serializer_class.plugin(:root, **opts) unless serializer_class.plugin_used?(:root)
12
+ end
13
+
14
+ def self.load_plugin(serializer_class, **_opts)
15
+ serializer_class.extend(ClassMethods)
16
+ serializer_class::Convert.include(ConvertInstanceMethods)
17
+
18
+ require_relative "./meta_attribute"
19
+ require_relative "./validations/check_block"
20
+ require_relative "./validations/check_opt_hide_nil"
21
+ require_relative "./validations/check_opt_hide_empty"
22
+ require_relative "./validations/check_opts"
23
+ require_relative "./validations/check_path"
24
+
25
+ meta_attribute_class = Class.new(MetaAttribute)
26
+ meta_attribute_class.serializer_class = serializer_class
27
+ serializer_class.const_set(:MetaAttribute, meta_attribute_class)
28
+ end
29
+
30
+ def self.after_load_plugin(serializer_class, **_opts)
31
+ serializer_class.config[plugin_name] = {attribute_keys: %i[path hide_nil hide_empty]}
32
+ end
33
+
34
+ module ClassMethods
35
+ private def inherited(subclass)
36
+ super
37
+
38
+ meta_attribute_class = Class.new(self::MetaAttribute)
39
+ meta_attribute_class.serializer_class = subclass
40
+ subclass.const_set(:MetaAttribute, meta_attribute_class)
41
+
42
+ # Assign same metadata attributes
43
+ meta_attributes.each_value do |attr|
44
+ subclass.meta_attribute(*attr.path, **attr.opts, &attr.block)
45
+ end
46
+ end
47
+
48
+ #
49
+ # List of added metadata attributes
50
+ #
51
+ # @return [Array] Added metadata attributes
52
+ #
53
+ def meta_attributes
54
+ @meta_attributes ||= {}
55
+ end
56
+
57
+ #
58
+ # Adds metadata to response
59
+ #
60
+ # @example
61
+ # class AppSerializer < Serega
62
+ #
63
+ # meta_attribute(:version) { '1.2.3' }
64
+ #
65
+ # meta_attribute(:meta, :paging, hide_nil: true, hide_empty: true) do |scope, ctx|
66
+ # { page: scope.page, per_page: scope.per_page, total_count: scope.total_count }
67
+ # end
68
+ # end
69
+ #
70
+ # @param *path [Array<String, Symbol>] Metadata attribute path keys
71
+ # @param **opts [Hash] Metadata attribute options
72
+ # @param &block [Proc] Metadata attribute value
73
+ #
74
+ # @return [MetadataAttribute] Added metadata attribute
75
+ #
76
+ def meta_attribute(*path, **opts, &block)
77
+ attribute = self::MetaAttribute.new(path: path, opts: opts, block: block)
78
+ meta_attributes[attribute.name] = attribute
79
+ end
80
+ end
81
+
82
+ module ConvertInstanceMethods
83
+ def to_h
84
+ hash = super
85
+ add_metadata(hash)
86
+ hash
87
+ end
88
+
89
+ private
90
+
91
+ def add_metadata(hash)
92
+ self.class.serializer_class.meta_attributes.each_value do |meta_attribute|
93
+ metadata = meta_attribute_value(meta_attribute)
94
+ next unless metadata
95
+
96
+ deep_merge_metadata(hash, metadata)
97
+ end
98
+ end
99
+
100
+ def meta_attribute_value(meta_attribute)
101
+ value = meta_attribute.value(object, opts[:context])
102
+ return if meta_attribute.hide?(value)
103
+
104
+ # Example:
105
+ # [:foo, :bar].reverse_each.inject(:bazz) { |val, key| { key => val } } # => { foo: { bar: :bazz } }
106
+ meta_attribute.path.reverse_each.inject(value) { |val, key| {key => val} }
107
+ end
108
+
109
+ def deep_merge_metadata(hash, metadata)
110
+ hash.merge!(metadata) do |_key, this_val, other_val|
111
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
112
+ deep_merge_metadata(this_val, other_val)
113
+ else
114
+ other_val
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ register_plugin(Metadata.plugin_name, Metadata)
122
+ end
123
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Metadata
6
+ class MetaAttribute
7
+ class CheckBlock
8
+ ALLOWED_PARAM_TYPES = %i[opt req]
9
+ private_constant :ALLOWED_PARAM_TYPES
10
+
11
+ class << self
12
+ #
13
+ # Checks block provided with attribute
14
+ # Block must have up to two arguments - object and context.
15
+ # It should not have any *rest or **key arguments
16
+ #
17
+ # @example without arguments
18
+ # metadata(:version) { CONSTANT_VERSION }
19
+ #
20
+ # @example with one argument
21
+ # metadata(:paging) { |scope| { { page: scope.page, per_page: scope.per_page, total_count: scope.total_count } }
22
+ #
23
+ # @example with two arguments
24
+ # metadata(:paging) { |scope, context| { { ... } if context[:with_paging] }
25
+ #
26
+ # @param block [Proc] Block that returns serialized meta attribute value
27
+ #
28
+ # @raise [Error] Error that block has invalid arguments
29
+ #
30
+ # @return [void]
31
+ #
32
+ def call(block)
33
+ raise Error, "Block must be provided when defining meta attribute" unless block
34
+
35
+ params = block.parameters
36
+ return if (params.count <= 2) && params.all? { |par| ALLOWED_PARAM_TYPES.include?(par[0]) }
37
+
38
+ raise Error, "Block can have maximum 2 regular parameters (no **keyword or *array args)"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Metadata
6
+ class MetaAttribute
7
+ class CheckOptHideEmpty
8
+ class << self
9
+ #
10
+ # Checks attribute :after_hide_if option
11
+ #
12
+ # @param opts [Hash] Attribute options
13
+ #
14
+ # @raise [Error] Error that option has invalid value
15
+ #
16
+ # @return [void]
17
+ #
18
+ def call(opts)
19
+ return unless opts.key?(:hide_empty)
20
+
21
+ value = opts[:hide_empty]
22
+ return if value == true
23
+
24
+ raise Error, "Invalid option :hide_empty => #{value.inspect}. Must be true"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Metadata
6
+ class MetaAttribute
7
+ class CheckOptHideNil
8
+ class << self
9
+ #
10
+ # Checks attribute :after_hide_if option
11
+ #
12
+ # @param opts [Hash] Attribute options
13
+ #
14
+ # @raise [Error] Error that option has invalid value
15
+ #
16
+ # @return [void]
17
+ #
18
+ def call(opts)
19
+ return unless opts.key?(:hide_nil)
20
+
21
+ value = opts[:hide_nil]
22
+ return if value == true
23
+
24
+ raise Error, "Invalid option :hide_nil => #{value.inspect}. Must be true"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Metadata
6
+ class MetaAttribute
7
+ class CheckOpts
8
+ class << self
9
+ #
10
+ # Validates attribute options
11
+ # Checks used options are allowed and then checks options values.
12
+ #
13
+ # @param opts [Hash] Attribute options
14
+ # @param attribute_keys [Array<Symbol>] Allowed options keys
15
+ #
16
+ # @raise [Error] when attribute has invalid options
17
+ #
18
+ # @return [void]
19
+ #
20
+ def call(opts, attribute_keys)
21
+ opts.each_key do |key|
22
+ next if attribute_keys.include?(key.to_sym)
23
+
24
+ raise Error, "Invalid option #{key.inspect}. Allowed options are: #{attribute_keys.map(&:inspect).join(", ")}"
25
+ end
26
+
27
+ check_each_opt(opts)
28
+ end
29
+
30
+ private
31
+
32
+ def check_each_opt(opts)
33
+ CheckOptHideEmpty.call(opts)
34
+ CheckOptHideNil.call(opts)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Metadata
6
+ class MetaAttribute
7
+ class CheckPath
8
+ FORMAT_ONE_CHAR = /\A[a-zA-Z0-9]\z/
9
+ FORMAT_MANY_CHARS = /\A[a-zA-Z0-9][a-zA-Z0-9_-]*?[a-zA-Z0-9]\z/ # allow '-' and '_' in the middle
10
+
11
+ private_constant :FORMAT_ONE_CHAR, :FORMAT_MANY_CHARS
12
+
13
+ class << self
14
+ #
15
+ # Checks allowed characters in specified metadata path parts.
16
+ # Globally allowed characters: "a-z", "A-Z", "0-9".
17
+ # Minus and low line "-", "_" also allowed except as the first or last character.
18
+ #
19
+ # @param path [Array<String, Symbol>] Metadata attribute path names
20
+ #
21
+ # @raise [Error] when metadata attribute name has invalid format
22
+ # @return [void]
23
+ #
24
+ def call(path)
25
+ path.each { |attr_name| check_name(attr_name) }
26
+ end
27
+
28
+ private
29
+
30
+ def check_name(name)
31
+ name = name.to_s
32
+
33
+ valid =
34
+ case name.size
35
+ when 0 then false
36
+ when 1 then name.match?(FORMAT_ONE_CHAR)
37
+ else name.match?(FORMAT_MANY_CHARS)
38
+ end
39
+
40
+ return if valid
41
+
42
+ raise Error, message(name)
43
+ end
44
+
45
+ def message(name)
46
+ %(Invalid metadata path #{name.inspect}, globally allowed characters: "a-z", "A-Z", "0-9". Minus and low line "-", "_" also allowed except as the first or last character)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Preloads
6
+ # Freezes nested enumerable data
7
+ class EnumDeepFreeze
8
+ class << self
9
+ def call(data)
10
+ data.each_entry { |entry| call(entry) } if data.is_a?(Enumerable)
11
+ data.freeze
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Preloads
6
+ # Transforms user provided preloads to hash
7
+ class FormatUserPreloads
8
+ METHODS = {
9
+ Array => :array_to_hash,
10
+ FalseClass => :nil_to_hash,
11
+ Hash => :hash_to_hash,
12
+ NilClass => :nil_to_hash,
13
+ String => :string_to_hash,
14
+ Symbol => :symbol_to_hash
15
+ }.freeze
16
+
17
+ module ClassMethods
18
+ def call(value)
19
+ send(METHODS.fetch(value.class), value)
20
+ end
21
+
22
+ private
23
+
24
+ def array_to_hash(values)
25
+ values.each_with_object({}) do |value, obj|
26
+ obj.merge!(call(value))
27
+ end
28
+ end
29
+
30
+ def hash_to_hash(values)
31
+ values.each_with_object({}) do |(key, value), obj|
32
+ obj[key.to_sym] = call(value)
33
+ end
34
+ end
35
+
36
+ def nil_to_hash(_value)
37
+ {}
38
+ end
39
+
40
+ def string_to_hash(value)
41
+ symbol_to_hash(value.to_sym)
42
+ end
43
+
44
+ def symbol_to_hash(value)
45
+ {value => {}}
46
+ end
47
+ end
48
+
49
+ extend ClassMethods
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Preloads
6
+ class MainPreloadPath
7
+ module ClassMethods
8
+ # @param preloads [Hash] Formatted user provided preloads hash
9
+ def call(preloads)
10
+ return FROZEN_EMPTY_ARRAY if preloads.empty?
11
+
12
+ main_path(preloads)
13
+ end
14
+
15
+ private
16
+
17
+ # Generates path (Array) to the last included resource.
18
+ # We need to know this path to include nested associations.
19
+ #
20
+ # main_path(a: { b: { c: {} }, d: {} }) # => [:a, :d]
21
+ #
22
+ def main_path(hash, path = [])
23
+ current_level = path.size
24
+
25
+ hash.each do |key, data|
26
+ path.pop(path.size - current_level)
27
+ path << key
28
+
29
+ main_path(data, path)
30
+ end
31
+
32
+ path
33
+ end
34
+ end
35
+
36
+ extend ClassMethods
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ module Preloads
6
+ #
7
+ # Finds relations to preload for provided serializer
8
+ #
9
+ class PreloadsConstructor
10
+ module ClassMethods
11
+ #
12
+ # Constructs preloads hash for given serializer
13
+ #
14
+ # @param serializer [Serega] Instance of Serega serializer
15
+ #
16
+ # @return [Hash]
17
+ #
18
+ def call(map)
19
+ preloads = {}
20
+ append_many(preloads, map)
21
+ preloads
22
+ end
23
+
24
+ private
25
+
26
+ def append_many(preloads, map)
27
+ map.each do |attribute, nested_map|
28
+ current_preloads = attribute.preloads
29
+ next unless current_preloads
30
+
31
+ has_nested = nested_map.any?
32
+ current_preloads = Utils::EnumDeepDup.call(current_preloads) if has_nested
33
+ append_current(preloads, current_preloads)
34
+ next unless has_nested
35
+
36
+ nested_preloads = nested(preloads, attribute.preloads_path)
37
+ append_many(nested_preloads, nested_map)
38
+ end
39
+ end
40
+
41
+ def append_current(preloads, current_preloads)
42
+ merge(preloads, current_preloads) unless current_preloads.empty?
43
+ end
44
+
45
+ def merge(preloads, current_preloads)
46
+ preloads.merge!(current_preloads) do |_key, value_one, value_two|
47
+ merge(value_one, value_two)
48
+ end
49
+ end
50
+
51
+ def nested(preloads, path)
52
+ !path || path.empty? ? preloads : preloads.dig(*path)
53
+ end
54
+ end
55
+
56
+ extend ClassMethods
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module Plugins
5
+ #
6
+ # Plugin adds `.preloads` method to find relations that must be preloaded
7
+ #
8
+ module Preloads
9
+ # @return [Symbol] plugin name
10
+ def self.plugin_name
11
+ :preloads
12
+ end
13
+
14
+ #
15
+ # Includes plugin modules to current serializer
16
+ #
17
+ # @param serializer_class [Class] current serializer class
18
+ # @param _opts [Hash] plugin opts
19
+ #
20
+ # @return [void]
21
+ #
22
+ def self.load_plugin(serializer_class, **_opts)
23
+ serializer_class.include(InstanceMethods)
24
+ serializer_class::Attribute.include(AttributeMethods)
25
+
26
+ serializer_class::Attribute::CheckOpts.extend(CheckOptsClassMethods)
27
+
28
+ require_relative "./lib/enum_deep_freeze"
29
+ require_relative "./lib/format_user_preloads"
30
+ require_relative "./lib/main_preload_path"
31
+ require_relative "./lib/preloads_constructor"
32
+ require_relative "./validations/check_opt_preload_path"
33
+ end
34
+
35
+ def self.after_load_plugin(serializer_class, **opts)
36
+ config = serializer_class.config
37
+ config[:attribute_keys] += [:preload, :preload_path]
38
+ config[:preloads] = {auto_preload_relations: opts.fetch(:auto_preload_relations, true)}
39
+ end
40
+
41
+ # Adds #preloads instance method
42
+ module InstanceMethods
43
+ # @return [Hash] relations that can be preloaded to omit N+1
44
+ def preloads
45
+ @preloads ||= PreloadsConstructor.call(map)
46
+ end
47
+ end
48
+
49
+ # Adds #preloads and #preloads_path Attribute instance method
50
+ module AttributeMethods
51
+ def preloads
52
+ return @preloads if defined?(@preloads)
53
+
54
+ @preloads = get_preloads
55
+ end
56
+
57
+ def preloads_path
58
+ return @preloads_path if defined?(@preloads_path)
59
+
60
+ @preloads_path = get_preloads_path
61
+ end
62
+
63
+ private
64
+
65
+ def get_preloads
66
+ preloads_provided = opts.key?(:preload)
67
+ preloads =
68
+ if preloads_provided
69
+ opts[:preload]
70
+ elsif relation? && self.class.serializer_class.config[:preloads][:auto_preload_relations]
71
+ key
72
+ end
73
+
74
+ # Nil and empty hash differs as we can preload nested results to
75
+ # empty hash, but we will skip nested preloading if nil or false provided
76
+ return if preloads_provided && !preloads
77
+
78
+ FormatUserPreloads.call(preloads)
79
+ end
80
+
81
+ def get_preloads_path
82
+ path = Array(opts[:preload_path]).map!(&:to_sym)
83
+ path = MainPreloadPath.call(preloads) if path.empty?
84
+ EnumDeepFreeze.call(path)
85
+ end
86
+ end
87
+
88
+ module CheckOptsClassMethods
89
+ private
90
+
91
+ def check_each_opt(opts)
92
+ super
93
+ CheckOptPreloadPath.call(opts)
94
+ end
95
+ end
96
+ end
97
+
98
+ register_plugin(Preloads.plugin_name, Preloads)
99
+ end
100
+ end