serega 0.41.0 → 0.50.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 +4 -4
- data/README.md +123 -1205
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +186 -131
- data/lib/serega/attribute_normalizer.rb +164 -345
- data/lib/serega/attribute_normalizers/batch.rb +45 -0
- data/lib/serega/attribute_normalizers/block.rb +81 -0
- data/lib/serega/attribute_normalizers/delegate.rb +34 -0
- data/lib/serega/attribute_normalizers/hash_access.rb +37 -0
- data/lib/serega/attribute_normalizers/preloads.rb +46 -0
- data/lib/serega/attribute_value_resolvers/batch.rb +5 -54
- data/lib/serega/attribute_value_resolvers/const.rb +2 -24
- data/lib/serega/attribute_value_resolvers/delegate.rb +17 -28
- data/lib/serega/attribute_value_resolvers/hash_access.rb +34 -52
- data/lib/serega/attribute_value_resolvers/keyword.rb +10 -25
- data/lib/serega/attribute_values.rb +46 -0
- data/lib/serega/batch_loader.rb +37 -0
- data/lib/serega/conditions.rb +56 -0
- data/lib/serega/config.rb +106 -145
- data/lib/serega/engine/pulls.rb +64 -0
- data/lib/serega/engine/run.rb +106 -0
- data/lib/serega/errors.rb +36 -8
- data/lib/serega/formatter.rb +98 -0
- data/lib/serega/plan/cache.rb +96 -0
- data/lib/serega/plan/per_ractor_cache.rb +23 -0
- data/lib/serega/plan.rb +101 -128
- data/lib/serega/plan_point.rb +44 -145
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +14 -100
- data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +8 -75
- data/lib/serega/plugins/context_metadata/context_metadata.rb +40 -123
- data/lib/serega/plugins/metadata/check_params.rb +80 -0
- data/lib/serega/plugins/metadata/meta_attribute.rb +40 -79
- data/lib/serega/plugins/metadata/metadata.rb +40 -168
- data/lib/serega/plugins/root/root.rb +91 -235
- data/lib/serega/plugins.rb +16 -49
- data/lib/serega/presenter.rb +108 -0
- data/lib/serega/result_builder.rb +68 -0
- data/lib/serega/result_code.rb +203 -0
- data/lib/serega/shareable.rb +52 -0
- data/lib/serega/source_group.rb +187 -0
- data/lib/serega/utils/collection_detector.rb +19 -13
- data/lib/serega/utils/deep_merge.rb +26 -0
- data/lib/serega/utils/enum_deep_dup.rb +10 -16
- data/lib/serega/utils/enum_deep_freeze.rb +8 -14
- data/lib/serega/utils/method_signature.rb +6 -15
- data/lib/serega/utils/parse_string_modifiers.rb +63 -0
- data/lib/serega/utils/ractor_local.rb +36 -0
- data/lib/serega/utils/serialized_attribute_error.rb +60 -20
- data/lib/serega/utils/to_hash.rb +5 -26
- data/lib/serega/validations/attribute/check_block.rb +3 -15
- data/lib/serega/validations/attribute/check_name.rb +4 -15
- data/lib/serega/validations/attribute/check_opt_base_serializer.rb +3 -15
- data/lib/serega/validations/attribute/check_opt_batch.rb +26 -56
- data/lib/serega/validations/attribute/check_opt_condition.rb +55 -0
- data/lib/serega/validations/attribute/check_opt_delegate.rb +10 -63
- data/lib/serega/validations/attribute/check_opt_format.rb +36 -0
- data/lib/serega/validations/attribute/check_opt_hash_access.rb +11 -59
- data/lib/serega/validations/attribute/check_opt_hide.rb +3 -14
- data/lib/serega/validations/attribute/check_opt_many.rb +18 -20
- data/lib/serega/validations/attribute/check_opt_method.rb +3 -25
- data/lib/serega/validations/attribute/check_opt_preload.rb +3 -26
- data/lib/serega/validations/attribute/check_opt_serializer.rb +4 -18
- data/lib/serega/validations/attribute/check_opt_value.rb +3 -21
- data/lib/serega/validations/check_attribute_params.rb +92 -71
- data/lib/serega/validations/check_batch_loader_params.rb +43 -62
- data/lib/serega/validations/check_formatter.rb +80 -0
- data/lib/serega/validations/check_serialize_keywords.rb +23 -0
- data/lib/serega/validations/initiate/check_modifiers.rb +14 -20
- data/lib/serega/validations/utils/check_allowed_keys.rb +3 -13
- data/lib/serega/validations/utils/check_hash_access_value.rb +44 -0
- data/lib/serega/validations/utils/check_opt_is_bool.rb +3 -13
- data/lib/serega/validations/utils/check_opt_is_hash.rb +3 -13
- data/lib/serega/validations/utils/check_opt_is_string_or_symbol.rb +3 -13
- data/lib/serega/version.rb +1 -4
- data/lib/serega.rb +480 -372
- data/sig/serega/plugins.rbs +49 -0
- data/sig/serega.rbs +142 -0
- metadata +43 -39
- data/lib/serega/data_builder.rb +0 -54
- data/lib/serega/engine/level.rb +0 -59
- data/lib/serega/engine/level_queue.rb +0 -56
- data/lib/serega/engine/loader.rb +0 -70
- data/lib/serega/helpers/serializer_class_helper.rb +0 -19
- data/lib/serega/object_serializer.rb +0 -118
- data/lib/serega/plugins/camel_case/camel_case.rb +0 -213
- data/lib/serega/plugins/depth_limit/depth_limit.rb +0 -206
- data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +0 -69
- data/lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb +0 -38
- data/lib/serega/plugins/formatters/formatters.rb +0 -322
- data/lib/serega/plugins/if/if.rb +0 -287
- data/lib/serega/plugins/if/validations/check_opt_if.rb +0 -72
- data/lib/serega/plugins/if/validations/check_opt_if_value.rb +0 -77
- data/lib/serega/plugins/if/validations/check_opt_unless.rb +0 -72
- data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +0 -77
- data/lib/serega/plugins/metadata/validations/check_block.rb +0 -47
- data/lib/serega/plugins/metadata/validations/check_opt_const.rb +0 -39
- data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +0 -35
- data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +0 -35
- data/lib/serega/plugins/metadata/validations/check_opt_value.rb +0 -61
- data/lib/serega/plugins/metadata/validations/check_opts.rb +0 -59
- data/lib/serega/plugins/metadata/validations/check_path.rb +0 -50
- data/lib/serega/plugins/presenter/presenter.rb +0 -242
- data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +0 -111
- data/lib/serega/plugins/string_modifiers/string_modifiers.rb +0 -63
- data/lib/serega/utils/symbol_name.rb +0 -39
- data/lib/serega/validations/attribute/check_opt_const.rb +0 -40
- data/lib/serega/validations/check_initiate_params.rb +0 -57
- data/lib/serega/validations/check_serialize_params.rb +0 -54
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
module SeregaUtils
|
|
4
|
+
module SeregaUtils # :nodoc:
|
|
6
5
|
#
|
|
7
6
|
# Utility to check if an object should be serialized as a collection.
|
|
8
7
|
#
|
|
9
8
|
# Hashes and Structs are Enumerable, but enumerate their own member
|
|
10
9
|
# values, so they are treated as single objects.
|
|
11
10
|
#
|
|
12
|
-
#
|
|
13
|
-
|
|
11
|
+
class CollectionDetector # :nodoc:
|
|
12
|
+
# Number of classes a Hash of known classes keeps
|
|
13
|
+
MAX_KNOWN_CLASSES = 8
|
|
14
|
+
|
|
14
15
|
class << self
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
# Checks if provided object is a collection of objects. Remembers the
|
|
17
|
+
# answer for the class of the object in `known_classes`, up to
|
|
18
|
+
# MAX_KNOWN_CLASSES classes: `is_a?(Enumerable)` checks all
|
|
19
|
+
# ancestors of a class that does not include Enumerable.
|
|
20
|
+
def call(object, known_classes)
|
|
21
|
+
return true if object.instance_of?(Array)
|
|
22
|
+
|
|
23
|
+
object_class = object.class
|
|
24
|
+
collection = known_classes[object_class]
|
|
25
|
+
return collection unless collection.nil?
|
|
26
|
+
|
|
27
|
+
collection = object.is_a?(Enumerable) && !object.is_a?(Hash) && !object.is_a?(Struct)
|
|
28
|
+
known_classes[object_class] = collection if known_classes.size < MAX_KNOWN_CLASSES
|
|
29
|
+
collection
|
|
24
30
|
end
|
|
25
31
|
end
|
|
26
32
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
module SeregaUtils # :nodoc:
|
|
5
|
+
#
|
|
6
|
+
# Merges nested hashes
|
|
7
|
+
#
|
|
8
|
+
class DeepMerge # :nodoc:
|
|
9
|
+
class << self
|
|
10
|
+
# Merges `other` into `hash` in place. Nested hashes merge into new
|
|
11
|
+
# hashes, thus `other` stays unchanged. Other values of `other` win.
|
|
12
|
+
def call(hash, other)
|
|
13
|
+
hash.merge!(other) { |_key, this_val, other_val| merge_values(this_val, other_val) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def merge_values(this_val, other_val)
|
|
19
|
+
return other_val unless this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
|
20
|
+
|
|
21
|
+
this_val.merge(other_val) { |_key, this_nested, other_nested| merge_values(this_nested, other_nested) }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -4,23 +4,15 @@ class Serega
|
|
|
4
4
|
#
|
|
5
5
|
# Utilities
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
module SeregaUtils
|
|
7
|
+
module SeregaUtils # :nodoc:
|
|
9
8
|
#
|
|
10
|
-
# Duplicates nested hashes and
|
|
11
|
-
# It does not duplicate any
|
|
9
|
+
# Duplicates nested hashes, arrays and strings
|
|
10
|
+
# It does not duplicate any other values
|
|
12
11
|
#
|
|
13
|
-
#
|
|
14
|
-
class EnumDeepDup
|
|
12
|
+
class EnumDeepDup # :nodoc:
|
|
15
13
|
class << self
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# It does not duplicate any non-Array and non-Hash values
|
|
19
|
-
#
|
|
20
|
-
# @param data [Hash, Array] Data to duplicate
|
|
21
|
-
#
|
|
22
|
-
# @return [Hash, Array] Duplicated data
|
|
23
|
-
#
|
|
14
|
+
# Deeply duplicate provided Array, Hash or String data
|
|
15
|
+
# It does not duplicate any other values
|
|
24
16
|
def call(data)
|
|
25
17
|
case data
|
|
26
18
|
when Hash
|
|
@@ -30,9 +22,11 @@ class Serega
|
|
|
30
22
|
when Array
|
|
31
23
|
data = data.dup
|
|
32
24
|
dup_array_values(data)
|
|
25
|
+
when String
|
|
26
|
+
data.dup
|
|
27
|
+
else
|
|
28
|
+
data
|
|
33
29
|
end
|
|
34
|
-
|
|
35
|
-
data
|
|
36
30
|
end
|
|
37
31
|
|
|
38
32
|
private
|
|
@@ -4,21 +4,13 @@ class Serega
|
|
|
4
4
|
#
|
|
5
5
|
# Utilities
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
module SeregaUtils
|
|
7
|
+
module SeregaUtils # :nodoc:
|
|
9
8
|
#
|
|
10
|
-
# Utility to freeze nested hashes and
|
|
9
|
+
# Utility to freeze nested hashes, arrays and strings
|
|
11
10
|
#
|
|
12
|
-
#
|
|
13
|
-
class EnumDeepFreeze
|
|
11
|
+
class EnumDeepFreeze # :nodoc:
|
|
14
12
|
class << self
|
|
15
|
-
#
|
|
16
|
-
# Freezes nested hashes and arrays
|
|
17
|
-
#
|
|
18
|
-
# @param data[Hash, Array] data to freeze
|
|
19
|
-
#
|
|
20
|
-
# @return [Hash, Array] same deeply frozen data
|
|
21
|
-
#
|
|
13
|
+
# Freezes nested hashes and arrays, replaces strings with frozen copies
|
|
22
14
|
def call(data)
|
|
23
15
|
case data
|
|
24
16
|
when Hash
|
|
@@ -27,9 +19,11 @@ class Serega
|
|
|
27
19
|
when Array
|
|
28
20
|
data.map! { |value| call(value) }
|
|
29
21
|
data.freeze
|
|
22
|
+
when String
|
|
23
|
+
-data
|
|
24
|
+
else
|
|
25
|
+
data
|
|
30
26
|
end
|
|
31
|
-
|
|
32
|
-
data
|
|
33
27
|
end
|
|
34
28
|
end
|
|
35
29
|
end
|
|
@@ -4,31 +4,22 @@ class Serega
|
|
|
4
4
|
#
|
|
5
5
|
# Utilities
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
module SeregaUtils
|
|
7
|
+
module SeregaUtils # :nodoc:
|
|
9
8
|
#
|
|
10
9
|
# Utility to make method arguments signature
|
|
11
10
|
#
|
|
12
|
-
#
|
|
13
|
-
class MethodSignature
|
|
11
|
+
class MethodSignature # :nodoc:
|
|
14
12
|
SYMBOL_TO_PROC_SIGNATURE_RUBY2 = [[:rest]]
|
|
15
13
|
SYMBOL_TO_PROC_SIGNATURE_RUBY3 = [[:req], [:rest]]
|
|
16
14
|
private_constant :SYMBOL_TO_PROC_SIGNATURE_RUBY2
|
|
17
15
|
private_constant :SYMBOL_TO_PROC_SIGNATURE_RUBY3
|
|
18
16
|
|
|
19
17
|
class << self
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# @param callable [#call] callable object
|
|
24
|
-
# @param pos_limit [Integer] Max count of positional parameters
|
|
25
|
-
# @param keyword_args [Array<Symbol>] List of accepted keyword argument names
|
|
26
|
-
#
|
|
27
|
-
# @return [String] Method signature which consist of number of positional parameters and
|
|
28
|
-
# keyword parameter names joined with undescrore.
|
|
29
|
-
#
|
|
18
|
+
# Generates method arguments signature: the count of positional
|
|
19
|
+
# parameters and the keyword parameter names, joined with underscores,
|
|
20
|
+
# like "1_ctx"
|
|
30
21
|
def call(callable, pos_limit:, keyword_args: [])
|
|
31
|
-
params = callable.is_a?(Proc) ? callable.parameters : callable.method(:call).parameters
|
|
22
|
+
params = (callable.is_a?(Proc) || callable.is_a?(Method)) ? callable.parameters : callable.method(:call).parameters
|
|
32
23
|
|
|
33
24
|
# Procs (but not lambdas) can accept all provided parameters
|
|
34
25
|
return full_signature(pos_limit, keyword_args) if params.empty? && callable.is_a?(Proc) && !callable.lambda?
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
module SeregaUtils # :nodoc:
|
|
5
|
+
#
|
|
6
|
+
# Parses fields listed in one String, with nested fields in parentheses
|
|
7
|
+
#
|
|
8
|
+
class ParseStringModifiers # :nodoc:
|
|
9
|
+
SEPARATOR = /[,()]/
|
|
10
|
+
COMMA = ",".ord
|
|
11
|
+
LPAREN = "(".ord
|
|
12
|
+
private_constant :SEPARATOR, :COMMA, :LPAREN
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
# Parses string modifiers
|
|
16
|
+
#
|
|
17
|
+
# parse("user") => { user: {} }
|
|
18
|
+
# parse("user(id)") => { user: { id: {} } }
|
|
19
|
+
# parse("user(id,name)") => { user: { id: {}, name: {} } }
|
|
20
|
+
# parse("user,comments") => { user: {}, comments: {} }
|
|
21
|
+
# parse("user(comments(text))") => { user: { comments: { text: {} } } }
|
|
22
|
+
def parse(fields)
|
|
23
|
+
result = {}
|
|
24
|
+
storage = result
|
|
25
|
+
parent_storages = nil
|
|
26
|
+
start = 0
|
|
27
|
+
|
|
28
|
+
while (index = fields.byteindex(SEPARATOR, start))
|
|
29
|
+
name = extract_name(fields, start, index)
|
|
30
|
+
separator = fields.getbyte(index)
|
|
31
|
+
|
|
32
|
+
if separator == COMMA
|
|
33
|
+
storage[name] = FROZEN_EMPTY_HASH if name
|
|
34
|
+
elsif separator == LPAREN
|
|
35
|
+
if name
|
|
36
|
+
(parent_storages ||= []) << storage
|
|
37
|
+
storage = storage[name] = {}
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
storage[name] = FROZEN_EMPTY_HASH if name
|
|
41
|
+
storage = parent_storages&.pop || result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
start = index + 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
name = extract_name(fields, start, fields.bytesize)
|
|
48
|
+
storage[name] = FROZEN_EMPTY_HASH if name
|
|
49
|
+
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def extract_name(fields, start, finish)
|
|
56
|
+
name = fields.byteslice(start, finish - start)
|
|
57
|
+
name.strip!
|
|
58
|
+
name.empty? ? nil : name.to_sym
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
#
|
|
5
|
+
# Utilities
|
|
6
|
+
#
|
|
7
|
+
module SeregaUtils # :nodoc:
|
|
8
|
+
#
|
|
9
|
+
# Values stored in the current Ractor by key
|
|
10
|
+
#
|
|
11
|
+
class RactorLocal # :nodoc:
|
|
12
|
+
class << self
|
|
13
|
+
# Returns the key of the owner value with the name
|
|
14
|
+
def key(owner, name)
|
|
15
|
+
:"serega_#{name}_#{owner.object_id}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# :nocov:
|
|
19
|
+
if defined?(Ractor.[])
|
|
20
|
+
# Returns the value of the key in the current Ractor, stores the
|
|
21
|
+
# block result when there is none
|
|
22
|
+
def fetch(key)
|
|
23
|
+
Ractor[key] ||= yield
|
|
24
|
+
end
|
|
25
|
+
else
|
|
26
|
+
# Returns the value of the key in the current Ractor, stores the
|
|
27
|
+
# block result when there is none
|
|
28
|
+
def fetch(key)
|
|
29
|
+
Ractor.current[key] ||= yield
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
# :nocov:
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -1,29 +1,69 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
module SeregaUtils
|
|
4
|
+
module SeregaUtils # :nodoc:
|
|
6
5
|
#
|
|
7
6
|
# Reraises an error, adding which attribute and serializer were being
|
|
8
|
-
# serialized when it happened
|
|
9
|
-
#
|
|
10
|
-
# attribute, whether its value is resolved inline or during batch loading.
|
|
7
|
+
# serialized when it happened, and where the attribute is defined. Value
|
|
8
|
+
# reads, preloads, batch loaders and attribute conditions use it.
|
|
11
9
|
#
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
module SerializedAttributeError # :nodoc:
|
|
11
|
+
# File name of the generated code in backtraces
|
|
12
|
+
GENERATED_CODE_FILE = "(serega generated code)"
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
# Reraises an error raised while a value is read, preloaded or batch
|
|
16
|
+
# loaded. An error raised in the generated code gets a backtrace line at
|
|
17
|
+
# the attribute, above the line of the generated code:
|
|
18
|
+
#
|
|
19
|
+
# app/serializers/user_serializer.rb:3:in 'attribute :full_name of UserSerializer'
|
|
20
|
+
# (serega generated code):20:in 'call'
|
|
21
|
+
def call(error, point)
|
|
22
|
+
location = point.attribute.location
|
|
23
|
+
serializer_class = point.plan.serializer_class
|
|
24
|
+
defined_at = location ? ", #{location}" : ""
|
|
25
|
+
|
|
26
|
+
exception = error.exception(<<~MESSAGE.strip)
|
|
27
|
+
#{error.message}
|
|
28
|
+
(when serializing the '#{point.name}' attribute in #{serializer_class}#{defined_at})
|
|
29
|
+
MESSAGE
|
|
30
|
+
add_attribute_line(exception, "#{location}:in 'attribute :#{point.name} of #{serializer_class}'") if location
|
|
31
|
+
raise exception
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Reraises an error raised by an :if, :unless, :if_value or
|
|
35
|
+
# :unless_value condition
|
|
36
|
+
def condition(error, attribute, condition_name)
|
|
37
|
+
location = attribute.location
|
|
38
|
+
defined_at = location ? ", #{location}" : ""
|
|
39
|
+
|
|
40
|
+
raise error.exception(<<~MESSAGE.strip)
|
|
41
|
+
#{error.message}
|
|
42
|
+
(when checking the :#{condition_name} condition of the '#{attribute.name}' attribute in #{attribute.serializer_class}#{defined_at})
|
|
43
|
+
MESSAGE
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# Puts the line above the line of the generated code that called this
|
|
49
|
+
# module. The backtrace and the call stack share the outer lines, up
|
|
50
|
+
# to that line: the backtrace has the line of the read there, the
|
|
51
|
+
# call stack the line of the rescue.
|
|
52
|
+
def add_attribute_line(exception, line)
|
|
53
|
+
backtrace = exception.backtrace
|
|
54
|
+
return unless backtrace
|
|
55
|
+
|
|
56
|
+
index = backtrace.size - shared_outer_lines_count(backtrace, caller(2)) - 1
|
|
57
|
+
return if index.negative? || !backtrace[index].start_with?(GENERATED_CODE_FILE)
|
|
58
|
+
|
|
59
|
+
exception.set_backtrace(backtrace.dup.insert(index, line))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def shared_outer_lines_count(backtrace, stack)
|
|
63
|
+
count = 0
|
|
64
|
+
count += 1 while count < backtrace.size && count < stack.size && backtrace[-1 - count] == stack[-1 - count]
|
|
65
|
+
count
|
|
66
|
+
end
|
|
27
67
|
end
|
|
28
68
|
end
|
|
29
69
|
end
|
data/lib/serega/utils/to_hash.rb
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
module SeregaUtils
|
|
4
|
+
module SeregaUtils # :nodoc:
|
|
6
5
|
#
|
|
7
6
|
# Utility to transform almost anything to Hash
|
|
8
7
|
#
|
|
9
|
-
#
|
|
10
|
-
class ToHash
|
|
8
|
+
class ToHash # :nodoc:
|
|
11
9
|
class << self
|
|
12
|
-
#
|
|
13
10
|
# Constructs deep hashes from provided data
|
|
14
11
|
#
|
|
15
|
-
# @param value [Array, Hash, String, Symbol, NilClass, FalseClass, TrueClass] Value to transform
|
|
16
|
-
#
|
|
17
|
-
# @example
|
|
18
12
|
# Serega::SeregaUtils::ToHash.(nil) # => {}
|
|
19
13
|
# Serega::SeregaUtils::ToHash.(true) # => {}
|
|
20
14
|
# Serega::SeregaUtils::ToHash.(false) # => {}
|
|
@@ -24,16 +18,13 @@ class Serega
|
|
|
24
18
|
# Serega::SeregaUtils::ToHash.({ foo: nil, bar: true }) # => {:foo=>{}, :bar=>{}}
|
|
25
19
|
# Serega::SeregaUtils::ToHash.({ foo: :bar }) # => {:foo=>{:bar=>{}}}
|
|
26
20
|
# Serega::SeregaUtils::ToHash.({ foo: [:bar] }) # => {:foo=>{:bar=>{}}}
|
|
27
|
-
#
|
|
28
|
-
# @return [Hash] Transformed data
|
|
29
|
-
#
|
|
30
21
|
def call(value)
|
|
31
22
|
case value
|
|
32
23
|
when Array then array_to_hash(value)
|
|
33
24
|
when Hash then hash_to_hash(value)
|
|
34
|
-
when NilClass, FalseClass, TrueClass then
|
|
35
|
-
when String then
|
|
36
|
-
when Symbol then
|
|
25
|
+
when NilClass, FalseClass, TrueClass then FROZEN_EMPTY_HASH
|
|
26
|
+
when String then {value.to_sym => FROZEN_EMPTY_HASH}
|
|
27
|
+
when Symbol then {value => FROZEN_EMPTY_HASH}
|
|
37
28
|
else raise SeregaError, "Can't convert #{value.class} class object to hash"
|
|
38
29
|
end
|
|
39
30
|
end
|
|
@@ -63,18 +54,6 @@ class Serega
|
|
|
63
54
|
end
|
|
64
55
|
res
|
|
65
56
|
end
|
|
66
|
-
|
|
67
|
-
def nil_to_hash(_value)
|
|
68
|
-
FROZEN_EMPTY_HASH
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def string_to_hash(value)
|
|
72
|
-
{value.to_sym => FROZEN_EMPTY_HASH}
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def symbol_to_hash(value)
|
|
76
|
-
{value => FROZEN_EMPTY_HASH}
|
|
77
|
-
end
|
|
78
57
|
end
|
|
79
58
|
end
|
|
80
59
|
end
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
module SeregaValidations
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
6
5
|
#
|
|
7
6
|
# Attribute parameters validators
|
|
8
7
|
#
|
|
9
|
-
#
|
|
10
|
-
module Attribute
|
|
8
|
+
module Attribute # :nodoc:
|
|
11
9
|
#
|
|
12
10
|
# Attribute `block` parameter validator
|
|
13
11
|
#
|
|
14
|
-
#
|
|
15
|
-
class CheckBlock
|
|
12
|
+
class CheckBlock # :nodoc:
|
|
16
13
|
# Explains the changed attribute block behavior. Shown when the block
|
|
17
14
|
# looks like an old-style value block — it accepts parameters or
|
|
18
15
|
# defines no attributes.
|
|
@@ -23,25 +20,16 @@ class Serega
|
|
|
23
20
|
" use the `value: <callable>` option instead."
|
|
24
21
|
|
|
25
22
|
class << self
|
|
26
|
-
#
|
|
27
23
|
# Checks block parameter provided with attribute.
|
|
28
24
|
#
|
|
29
25
|
# The block defines attributes of a nested anonymous serializer, so
|
|
30
26
|
# it is executed in the context of that serializer and must accept
|
|
31
27
|
# no parameters.
|
|
32
28
|
#
|
|
33
|
-
# @example
|
|
34
29
|
# attribute :statistics, method: :itself do
|
|
35
30
|
# attribute :likes_count
|
|
36
31
|
# attribute :comments_count
|
|
37
32
|
# end
|
|
38
|
-
#
|
|
39
|
-
# @param block [Proc] Block that defines nested serializer attributes
|
|
40
|
-
#
|
|
41
|
-
# @raise [SeregaError] SeregaError that block has invalid arguments
|
|
42
|
-
#
|
|
43
|
-
# @return [void]
|
|
44
|
-
#
|
|
45
33
|
def call(block)
|
|
46
34
|
return unless block
|
|
47
35
|
|
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# @private
|
|
7
|
-
module Attribute
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
module Attribute # :nodoc:
|
|
8
6
|
#
|
|
9
7
|
# Attribute `name` parameter validator
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
class CheckName
|
|
9
|
+
class CheckName # :nodoc:
|
|
13
10
|
# Regexp for valid attribute name
|
|
14
11
|
FORMAT = /\A[\w~-]+\z/
|
|
15
12
|
|
|
16
13
|
class << self
|
|
17
|
-
#
|
|
18
14
|
# Checks allowed characters.
|
|
19
15
|
# Allowed characters: "a-z", "A-Z", "0-9", "_", "-", "~".
|
|
20
|
-
#
|
|
21
|
-
# @param name [String, Symbol] Attribute name
|
|
22
|
-
#
|
|
23
|
-
# @raise [SeregaError] when name has invalid format
|
|
24
|
-
# @return [void]
|
|
25
|
-
#
|
|
26
16
|
def call(name)
|
|
27
|
-
name
|
|
28
|
-
raise SeregaError, message(name) unless FORMAT.match?(name)
|
|
17
|
+
raise SeregaError, message(name.to_s) unless FORMAT.match?(name)
|
|
29
18
|
end
|
|
30
19
|
|
|
31
20
|
private
|
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# @private
|
|
7
|
-
module Attribute
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
module Attribute # :nodoc:
|
|
8
6
|
#
|
|
9
7
|
# Attribute `:base_serializer` option validator
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
class CheckOptBaseSerializer
|
|
9
|
+
class CheckOptBaseSerializer # :nodoc:
|
|
13
10
|
class << self
|
|
14
|
-
#
|
|
15
11
|
# Checks attribute :base_serializer option. It specifies the parent
|
|
16
12
|
# class for the nested serializer defined with the attribute block,
|
|
17
13
|
# so it makes sense only when a block is provided.
|
|
18
|
-
#
|
|
19
|
-
# @param opts [Hash] Attribute options
|
|
20
|
-
# @param block [nil, Proc] Attribute block (defines a nested serializer)
|
|
21
|
-
#
|
|
22
|
-
# @raise [SeregaError] SeregaError that option has invalid value
|
|
23
|
-
#
|
|
24
|
-
# @return [void]
|
|
25
|
-
#
|
|
26
14
|
def call(opts, block = nil)
|
|
27
15
|
return unless opts.key?(:base_serializer)
|
|
28
16
|
|