jat 0.0.1 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jat/attribute.rb +107 -0
  3. data/lib/jat/config.rb +35 -0
  4. data/lib/jat/plugins/activerecord/activerecord.rb +23 -0
  5. data/lib/jat/plugins/cache/cache.rb +47 -0
  6. data/lib/jat/plugins/common/_activerecord_preloads/_activerecord_preloads.rb +38 -0
  7. data/lib/jat/plugins/common/_activerecord_preloads/lib/preloader.rb +93 -0
  8. data/lib/jat/plugins/common/_lower_camel_case/_lower_camel_case.rb +36 -0
  9. data/lib/jat/plugins/common/_preloads/_preloads.rb +63 -0
  10. data/lib/jat/plugins/common/_preloads/lib/format_user_preloads.rb +52 -0
  11. data/lib/jat/plugins/common/_preloads/lib/preloads_with_path.rb +78 -0
  12. data/lib/jat/plugins/json_api/json_api.rb +251 -0
  13. data/lib/jat/plugins/json_api/lib/fields_param_parser.rb +40 -0
  14. data/lib/jat/plugins/json_api/lib/include_param_parser.rb +84 -0
  15. data/lib/jat/plugins/json_api/lib/map.rb +119 -0
  16. data/lib/jat/plugins/json_api/lib/params/fields/parse.rb +27 -0
  17. data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +58 -0
  18. data/lib/jat/plugins/json_api/lib/params/fields.rb +23 -0
  19. data/lib/jat/plugins/json_api/lib/params/include/parse.rb +55 -0
  20. data/lib/jat/plugins/json_api/lib/params/include/validate.rb +29 -0
  21. data/lib/jat/plugins/json_api/lib/params/include.rb +49 -0
  22. data/lib/jat/plugins/json_api/lib/response.rb +123 -0
  23. data/lib/jat/plugins/json_api/lib/response_piece.rb +175 -0
  24. data/lib/jat/plugins/json_api/plugins/json_api_activerecord/json_api_activerecord.rb +23 -0
  25. data/lib/jat/plugins/json_api/plugins/json_api_lower_camel_case/json_api_lower_camel_case.rb +34 -0
  26. data/lib/jat/plugins/json_api/plugins/json_api_maps_cache/json_api_maps_cache.rb +58 -0
  27. data/lib/jat/plugins/json_api/plugins/json_api_preloads/json_api_preloads.rb +38 -0
  28. data/lib/jat/plugins/json_api/plugins/json_api_preloads/lib/preloads.rb +76 -0
  29. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/json_api_validate_params.rb +61 -0
  30. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/lib/params_error.rb +6 -0
  31. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/lib/validate_fields_param.rb +59 -0
  32. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/lib/validate_include_param.rb +33 -0
  33. data/lib/jat/plugins/lower_camel_case/lower_camel_case.rb +23 -0
  34. data/lib/jat/plugins/maps_cache/maps_cache.rb +23 -0
  35. data/lib/jat/plugins/preloads/preloads.rb +23 -0
  36. data/lib/jat/plugins/simple_api/lib/fields_param_parser.rb +97 -0
  37. data/lib/jat/plugins/simple_api/lib/map.rb +99 -0
  38. data/lib/jat/plugins/simple_api/lib/response.rb +119 -0
  39. data/lib/jat/plugins/simple_api/lib/response_piece.rb +80 -0
  40. data/lib/jat/plugins/simple_api/plugins/simple_api_activerecord/simple_api_activerecord.rb +23 -0
  41. data/lib/jat/plugins/simple_api/plugins/simple_api_lower_camel_case/simple_api_lower_camel_case.rb +34 -0
  42. data/lib/jat/plugins/simple_api/plugins/simple_api_maps_cache/simple_api_maps_cache.rb +50 -0
  43. data/lib/jat/plugins/simple_api/plugins/simple_api_preloads/lib/preloads.rb +55 -0
  44. data/lib/jat/plugins/simple_api/plugins/simple_api_preloads/simple_api_preloads.rb +38 -0
  45. data/lib/jat/plugins/simple_api/plugins/simple_api_validate_params/lib/fields_error.rb +6 -0
  46. data/lib/jat/plugins/simple_api/plugins/simple_api_validate_params/lib/validate_fields_param.rb +45 -0
  47. data/lib/jat/plugins/simple_api/plugins/simple_api_validate_params/simple_api_validate_params.rb +49 -0
  48. data/lib/jat/plugins/simple_api/simple_api.rb +125 -0
  49. data/lib/jat/plugins/to_str/to_str.rb +54 -0
  50. data/lib/jat/plugins/types/types.rb +54 -0
  51. data/lib/jat/plugins/validate_params/validate_params.rb +23 -0
  52. data/lib/jat/plugins.rb +39 -0
  53. data/lib/jat/utils/enum_deep_dup.rb +29 -0
  54. data/lib/jat/utils/enum_deep_freeze.rb +19 -0
  55. data/lib/jat.rb +66 -141
  56. data/test/lib/jat/attribute_test.rb +152 -0
  57. data/test/lib/jat/config_test.rb +57 -0
  58. data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +59 -0
  59. data/test/lib/jat/plugins/_activerecord_preloads/lib/preloader_test.rb +84 -0
  60. data/test/lib/jat/plugins/_camel_lower/_camel_lower_test.rb +26 -0
  61. data/test/lib/jat/plugins/_preloads/_preloads_test.rb +68 -0
  62. data/test/lib/jat/plugins/_preloads/lib/format_user_preloads_test.rb +47 -0
  63. data/test/lib/jat/plugins/_preloads/lib/preloads_with_path_test.rb +33 -0
  64. data/test/lib/jat/plugins/cache/cache_test.rb +82 -0
  65. data/test/lib/jat/plugins/json_api/json_api_test.rb +162 -0
  66. data/test/lib/jat/plugins/json_api/lib/fields_param_parser_test.rb +38 -0
  67. data/test/lib/jat/plugins/json_api/lib/include_param_parser_test.rb +41 -0
  68. data/test/lib/jat/plugins/json_api/lib/map_test.rb +188 -0
  69. data/test/lib/jat/plugins/json_api/lib/response_test.rb +489 -0
  70. data/test/lib/jat/plugins/json_api_activerecord/json_api_activerecord_test.rb +24 -0
  71. data/test/lib/jat/plugins/json_api_camel_lower/json_api_camel_lower_test.rb +79 -0
  72. data/test/lib/jat/plugins/json_api_maps_cache/json_api_maps_cache_test.rb +107 -0
  73. data/test/lib/jat/plugins/json_api_preloads/json_api_preloads_test.rb +37 -0
  74. data/test/lib/jat/plugins/json_api_preloads/lib/preloads_test.rb +197 -0
  75. data/test/lib/jat/plugins/json_api_validate_params/json_api_validate_params_test.rb +84 -0
  76. data/test/lib/jat/plugins/simple_api/lib/fields_param_parser_test.rb +77 -0
  77. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +133 -0
  78. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +348 -0
  79. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +139 -0
  80. data/test/lib/jat/plugins/simple_api_activerecord/simple_api_activerecord_test.rb +24 -0
  81. data/test/lib/jat/plugins/simple_api_camel_lower/simple_api_camel_lower_test.rb +48 -0
  82. data/test/lib/jat/plugins/simple_api_maps_cache/simple_api_maps_cache_test.rb +95 -0
  83. data/test/lib/jat/plugins/simple_api_preloads/lib/preloads_test.rb +140 -0
  84. data/test/lib/jat/plugins/simple_api_preloads/simple_api_preloads_test.rb +37 -0
  85. data/test/lib/jat/plugins/simple_api_validate_params/simple_api_validate_params_test.rb +89 -0
  86. data/test/lib/jat/plugins/to_str/to_str_test.rb +52 -0
  87. data/test/lib/jat/plugins/types/types_test.rb +79 -0
  88. data/test/lib/jat/utils/enum_deep_dup_test.rb +31 -0
  89. data/test/lib/jat/utils/enum_deep_freeze_test.rb +28 -0
  90. data/test/lib/jat_test.rb +143 -0
  91. data/test/lib/plugin_test.rb +49 -0
  92. data/test/support/activerecord.rb +24 -0
  93. data/test/test_helper.rb +13 -0
  94. data/test/test_plugin.rb +56 -0
  95. metadata +243 -11
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiPreloads
6
+ class Preloads
7
+ module ClassMethods
8
+ def call(jat)
9
+ result = {}
10
+ append_many(result, jat.class, jat.map)
11
+ result
12
+ end
13
+
14
+ private
15
+
16
+ def append_many(result, jat_class, keys)
17
+ keys.each do |key, inner_keys|
18
+ attribute = jat_class.attributes.fetch(key)
19
+ preloads = attribute.preloads
20
+ next unless preloads
21
+
22
+ append_one(result, jat_class, preloads)
23
+ next if inner_keys.empty?
24
+
25
+ path = attribute.preloads_path
26
+ nested_result = nested(result, path)
27
+ nested_serializer = attribute.serializer.call
28
+
29
+ append_many(nested_result, nested_serializer, inner_keys)
30
+ end
31
+ end
32
+
33
+ def append_one(result, jat_class, preloads)
34
+ return if preloads.empty?
35
+
36
+ preloads = EnumDeepDup.call(preloads)
37
+ merge(result, preloads)
38
+ end
39
+
40
+ def merge(result, preloads)
41
+ result.merge!(preloads) do |_key, value_one, value_two|
42
+ merge(value_one, value_two)
43
+ end
44
+ end
45
+
46
+ def nested(result, path)
47
+ !path || path.empty? ? result : result.dig(*path)
48
+ end
49
+ end
50
+
51
+ extend ClassMethods
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/preloads"
4
+
5
+ class Jat
6
+ module Plugins
7
+ module SimpleApiPreloads
8
+ def self.plugin_name
9
+ :simple_api_preloads
10
+ end
11
+
12
+ def self.before_load(jat_class, **opts)
13
+ raise Error, "Please load :simple_api plugin first" unless jat_class.plugin_used?(:simple_api)
14
+
15
+ jat_class.plugin :_preloads, **opts
16
+ end
17
+
18
+ def self.load(jat_class, **_opts)
19
+ jat_class.extend(ClassMethods)
20
+ jat_class.include(InstanceMethods)
21
+ end
22
+
23
+ module ClassMethods
24
+ def preloads(context = {})
25
+ new(context).preloads
26
+ end
27
+ end
28
+
29
+ module InstanceMethods
30
+ def preloads
31
+ @preloads ||= Preloads.call(self)
32
+ end
33
+ end
34
+ end
35
+
36
+ register_plugin(SimpleApiPreloads.plugin_name, SimpleApiPreloads)
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ class SimpleApiFieldsError < Error
5
+ end
6
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiValidateParams
6
+ class ValidateFieldsParam
7
+ class << self
8
+ def call(jat_class, fields, prev_names = [])
9
+ fields.each do |name, nested_fields|
10
+ attribute = jat_class.attributes[name]
11
+
12
+ raise_error(name, prev_names) unless attribute
13
+ next if nested_fields.empty?
14
+
15
+ raise_nested_error(name, prev_names, nested_fields) unless attribute.relation?
16
+ nested_serializer = attribute.serializer.call
17
+ call(nested_serializer, nested_fields, prev_names + [name])
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def raise_error(name, prev_names)
24
+ field_name = field_name(name, prev_names)
25
+
26
+ raise SimpleApiFieldsError, "Field #{field_name} not exists"
27
+ end
28
+
29
+ def raise_nested_error(name, prev_names, nested_fields)
30
+ field_name = field_name(name, prev_names)
31
+ first_nested = nested_fields.keys.first
32
+
33
+ raise SimpleApiFieldsError, "Field #{field_name} is not a relationship to add '#{first_nested}' attribute"
34
+ end
35
+
36
+ def field_name(name, prev_names)
37
+ res = "'#{name}'"
38
+ res += " ('#{prev_names.join(".")}.#{name}')" if prev_names.any?
39
+ res
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/fields_error"
4
+ require_relative "./lib/validate_fields_param"
5
+
6
+ class Jat
7
+ module Plugins
8
+ module SimpleApiValidateParams
9
+ def self.plugin_name
10
+ :simple_api_validate_params
11
+ end
12
+
13
+ def self.before_load(jat_class, **_opts)
14
+ return if jat_class.plugin_used?(:simple_api)
15
+ raise Error, "Please load :simple_api plugin first"
16
+ end
17
+
18
+ def self.load(jat_class, **_opts)
19
+ jat_class.extend(ClassMethods)
20
+ jat_class.include(InstanceMethods)
21
+
22
+ jat_class::FieldsParamParser.include(FieldsParamParserMethods)
23
+ end
24
+
25
+ module InstanceMethods
26
+ def validate
27
+ @validate ||= self.class.validate(context)
28
+ end
29
+ end
30
+
31
+ module ClassMethods
32
+ def validate(context)
33
+ # Generate map for current context.
34
+ # Params are valid if no errors were raised
35
+ map(context)
36
+ true
37
+ end
38
+ end
39
+
40
+ module FieldsParamParserMethods
41
+ def parse
42
+ super.tap { |result| ValidateFieldsParam.call(self.class.jat_class, result) }
43
+ end
44
+ end
45
+ end
46
+
47
+ register_plugin(SimpleApiValidateParams.plugin_name, SimpleApiValidateParams)
48
+ end
49
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/fields_param_parser"
4
+ require_relative "./lib/map"
5
+ require_relative "./lib/response"
6
+ require_relative "./lib/response_piece"
7
+
8
+ class Jat
9
+ module Plugins
10
+ module SimpleApi
11
+ def self.plugin_name
12
+ :simple_api
13
+ end
14
+
15
+ def self.before_load(jat_class, **_opts)
16
+ response_plugin = jat_class.config[:response_plugin_loaded]
17
+ return unless response_plugin
18
+
19
+ raise Error, "Response plugin `#{response_plugin}` was already loaded before"
20
+ end
21
+
22
+ def self.load(jat_class, **_opts)
23
+ jat_class.include(InstanceMethods)
24
+ jat_class.extend(ClassMethods)
25
+ end
26
+
27
+ def self.after_load(jat_class, **_opts)
28
+ jat_class.config[:response_plugin_loaded] = plugin_name
29
+
30
+ jat_class.meta_key(:meta)
31
+
32
+ fields_parser_class = Class.new(FieldsParamParser)
33
+ fields_parser_class.jat_class = jat_class
34
+ jat_class.const_set(:FieldsParamParser, fields_parser_class)
35
+
36
+ map_class = Class.new(Map)
37
+ map_class.jat_class = jat_class
38
+ jat_class.const_set(:Map, map_class)
39
+
40
+ response_class = Class.new(Response)
41
+ response_class.jat_class = jat_class
42
+ jat_class.const_set(:Response, response_class)
43
+
44
+ response_piece_class = Class.new(ResponsePiece)
45
+ response_piece_class.jat_class = jat_class
46
+ jat_class.const_set(:ResponsePiece, response_piece_class)
47
+ end
48
+
49
+ module InstanceMethods
50
+ def to_h(object)
51
+ self.class::Response.call(object, context)
52
+ end
53
+
54
+ def map
55
+ @map ||= self.class.map(context)
56
+ end
57
+ end
58
+
59
+ module ClassMethods
60
+ def inherited(subclass)
61
+ super
62
+
63
+ fields_parser_class = Class.new(self::FieldsParamParser)
64
+ fields_parser_class.jat_class = subclass
65
+ subclass.const_set(:FieldsParamParser, fields_parser_class)
66
+
67
+ map_class = Class.new(self::Map)
68
+ map_class.jat_class = subclass
69
+ subclass.const_set(:Map, map_class)
70
+
71
+ response_class = Class.new(self::Response)
72
+ response_class.jat_class = subclass
73
+ subclass.const_set(:Response, response_class)
74
+
75
+ response_piece_class = Class.new(self::ResponsePiece)
76
+ response_piece_class.jat_class = subclass
77
+ subclass.const_set(:ResponsePiece, response_piece_class)
78
+
79
+ # Assign same meta
80
+ added_meta.each_value do |attribute|
81
+ params = attribute.params
82
+ subclass.attribute(params[:name], **params[:opts], &params[:block])
83
+ end
84
+ end
85
+
86
+ def root(default = nil, one: nil, many: nil)
87
+ root_one = one || default
88
+ root_many = many || default
89
+
90
+ config[:root_one] = root_one ? root_one.to_sym : nil
91
+ config[:root_many] = root_many ? root_many.to_sym : nil
92
+
93
+ {root_one: root_one, root_many: root_many}
94
+ end
95
+
96
+ def meta_key(new_meta_key)
97
+ config[:meta_key] = new_meta_key.to_sym
98
+ end
99
+
100
+ def added_meta
101
+ @added_meta ||= {}
102
+ end
103
+
104
+ def meta(name, **opts, &block)
105
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
106
+ added_meta[new_attr.name] = new_attr
107
+ end
108
+
109
+ def map(context)
110
+ self::Map.call(context)
111
+ end
112
+
113
+ def map_full
114
+ @map_full ||= self::Map.call(exposed: :all)
115
+ end
116
+
117
+ def map_exposed
118
+ @map_exposed ||= self::Map.call(exposed: :default)
119
+ end
120
+ end
121
+ end
122
+
123
+ register_plugin(SimpleApi.plugin_name, SimpleApi)
124
+ end
125
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module ToStr
6
+ def self.plugin_name
7
+ :to_str
8
+ end
9
+
10
+ def self.load(jat_class, **_opts)
11
+ jat_class.include(InstanceMethods)
12
+ jat_class.extend(ClassMethods)
13
+ end
14
+
15
+ def self.after_load(jat_class, **opts)
16
+ jat_class.config[:to_str] = opts[:to_str] || ->(data) { ToStrJSON.dump(data) }
17
+ end
18
+
19
+ module ClassMethods
20
+ def to_str(object, context = nil)
21
+ new(context || FROZEN_EMPTY_HASH).to_str(object)
22
+ end
23
+ end
24
+
25
+ module InstanceMethods
26
+ def to_str(object)
27
+ hash = to_h(object)
28
+ config[:to_str].call(hash)
29
+ end
30
+ end
31
+
32
+ class ToStrJSON
33
+ module ClassMethods
34
+ def dump(response)
35
+ json_adapter.dump(response)
36
+ end
37
+
38
+ private
39
+
40
+ def json_adapter
41
+ @json_adapter ||= begin
42
+ require "json"
43
+ ::JSON
44
+ end
45
+ end
46
+ end
47
+
48
+ extend ClassMethods
49
+ end
50
+ end
51
+
52
+ register_plugin(ToStr.plugin_name, ToStr)
53
+ end
54
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module Types
6
+ def self.plugin_name
7
+ :types
8
+ end
9
+
10
+ def self.load(jat_class, **_opts)
11
+ jat_class::Attribute.include(InstanceMethods)
12
+ end
13
+
14
+ def self.after_load(jat_class, **_opts)
15
+ jat_class.config[:types] = {
16
+ array: ->(value) { Array(value) },
17
+ bool: ->(value) { !!value },
18
+ float: ->(value) { Float(value) },
19
+ hash: ->(value) { Hash(value) },
20
+ int: ->(value) { Integer(value) },
21
+ str: ->(value) { String(value) }
22
+ }
23
+ end
24
+
25
+ module InstanceMethods
26
+ def block
27
+ return @block if instance_variable_defined?(:@block)
28
+
29
+ original_block = super
30
+ type = opts[:type]
31
+ return original_block unless type
32
+
33
+ @block = typed_block(type, original_block)
34
+ end
35
+
36
+ private
37
+
38
+ def typed_block(type, original_block)
39
+ proc do |object, context|
40
+ value = original_block.call(object, context)
41
+
42
+ # Type conversion
43
+ case type
44
+ when Symbol then self.class.jat_class.config.fetch(:types).fetch(type).call(value)
45
+ else type.call(value)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ register_plugin(Types.plugin_name, Types)
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module ValidateParams
6
+ def self.plugin_name
7
+ :validate_params
8
+ end
9
+
10
+ def self.load(jat_class, **opts)
11
+ if jat_class.plugin_used?(:json_api)
12
+ jat_class.plugin :json_api_validate_params, **opts
13
+ elsif jat_class.plugin_used?(:simple_api)
14
+ jat_class.plugin :simple_api_validate_params, **opts
15
+ else
16
+ raise Error, "Please load :json_api or :simple_api plugin first"
17
+ end
18
+ end
19
+ end
20
+
21
+ register_plugin(ValidateParams.plugin_name, ValidateParams)
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ @plugins = {}
6
+
7
+ # Register the given plugin with Jat, so that it can be loaded using
8
+ # `Jat.plugin` with a symbol. Should be used by plugin files. Example:
9
+ #
10
+ # Jat::Plugins.register_plugin(:plugin_name, PluginModule)
11
+ def self.register_plugin(name, mod)
12
+ @plugins[name] = mod
13
+ end
14
+
15
+ # If the registered plugin already exists, use it. Otherwise, require
16
+ # and return it. This raises a LoadError if such a plugin doesn't exist,
17
+ # or a Jat::Error if it exists but it does not register itself
18
+ # correctly.
19
+ def self.find_plugin(name)
20
+ return name if name.is_a?(Module)
21
+
22
+ begin
23
+ require "jat/plugins/#{name}/#{name}"
24
+ rescue LoadError
25
+ if name.start_with?("json_api")
26
+ require "jat/plugins/json_api/plugins/#{name}/#{name}"
27
+ elsif name.start_with?("simple_api")
28
+ require "jat/plugins/simple_api/plugins/#{name}/#{name}"
29
+ elsif name.start_with?("_")
30
+ require "jat/plugins/common/#{name}/#{name}"
31
+ else
32
+ raise
33
+ end
34
+ end
35
+
36
+ @plugins[name] || raise(Error, "plugin #{name} did not register itself correctly in Jat::Plugins")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ # Duplicates nested enumerable data
5
+ class EnumDeepDup
6
+ DUP = {
7
+ Hash => ->(data) { dup_hash_values(data) },
8
+ Array => ->(data) { dup_array_values(data) }
9
+ }.freeze
10
+
11
+ def self.call(data)
12
+ duplicate_data = data.dup
13
+ DUP.fetch(duplicate_data.class).call(duplicate_data)
14
+ duplicate_data
15
+ end
16
+
17
+ def self.dup_hash_values(duplicate_data)
18
+ duplicate_data.each do |key, value|
19
+ duplicate_data[key] = call(value) if value.is_a?(Enumerable)
20
+ end
21
+ end
22
+
23
+ def self.dup_array_values(duplicate_data)
24
+ duplicate_data.each_with_index do |value, index|
25
+ duplicate_data[index] = call(value) if value.is_a?(Enumerable)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ # Freezes nested enumerable data
5
+ class EnumDeepFreeze
6
+ FREEZES = {
7
+ Hash => ->(data) { data.each_value(&FREEZE_ENUMS) },
8
+ Array => ->(data) { data.each(&FREEZE_ENUMS) }
9
+ }.freeze
10
+
11
+ FREEZE_ENUMS = ->(value) { call(value) if value.is_a?(Enumerable) }
12
+
13
+ def self.call(data)
14
+ data.freeze
15
+ FREEZES.fetch(data.class).call(data)
16
+ data
17
+ end
18
+ end
19
+ end