serega 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/serega/attribute.rb +2 -2
  4. data/lib/serega/config.rb +23 -1
  5. data/lib/serega/errors.rb +8 -5
  6. data/lib/serega/helpers/serializer_class_helper.rb +5 -0
  7. data/lib/serega/json/adapter.rb +6 -0
  8. data/lib/serega/json/json.rb +20 -0
  9. data/lib/serega/json/oj.rb +20 -0
  10. data/lib/serega/map.rb +17 -0
  11. data/lib/serega/map_point.rb +36 -1
  12. data/lib/serega/object_serializer.rb +17 -4
  13. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +66 -18
  14. data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +100 -40
  15. data/lib/serega/plugins/batch/batch.rb +136 -10
  16. data/lib/serega/plugins/batch/lib/loader.rb +33 -1
  17. data/lib/serega/plugins/batch/lib/loaders.rb +15 -2
  18. data/lib/serega/plugins/batch/lib/plugins_extensions.rb +23 -1
  19. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +12 -0
  20. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +12 -0
  21. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +12 -0
  22. data/lib/serega/plugins/context_metadata/context_metadata.rb +95 -13
  23. data/lib/serega/plugins/formatters/formatters.rb +94 -8
  24. data/lib/serega/plugins/hide_nil/hide_nil.rb +33 -7
  25. data/lib/serega/plugins/metadata/metadata.rb +108 -35
  26. data/lib/serega/plugins/metadata/validations/check_block.rb +3 -0
  27. data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +4 -1
  28. data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +4 -1
  29. data/lib/serega/plugins/metadata/validations/check_opts.rb +3 -0
  30. data/lib/serega/plugins/metadata/validations/check_path.rb +3 -0
  31. data/lib/serega/plugins/preloads/lib/enum_deep_freeze.rb +10 -1
  32. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +13 -4
  33. data/lib/serega/plugins/preloads/lib/main_preload_path.rb +16 -3
  34. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +4 -6
  35. data/lib/serega/plugins/preloads/preloads.rb +145 -12
  36. data/lib/serega/plugins/preloads/validations/check_opt_preload.rb +11 -0
  37. data/lib/serega/plugins/preloads/validations/check_opt_preload_path.rb +12 -0
  38. data/lib/serega/plugins/presenter/presenter.rb +20 -11
  39. data/lib/serega/plugins/root/root.rb +131 -19
  40. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +42 -12
  41. data/lib/serega/plugins/string_modifiers/string_modifiers.rb +14 -0
  42. data/lib/serega/plugins.rb +7 -1
  43. data/lib/serega/utils/enum_deep_dup.rb +5 -0
  44. data/lib/serega/utils/to_hash.rb +21 -3
  45. data/lib/serega/validations/attribute/check_block.rb +7 -2
  46. data/lib/serega/validations/attribute/check_name.rb +6 -0
  47. data/lib/serega/validations/attribute/check_opt_const.rb +12 -9
  48. data/lib/serega/validations/attribute/check_opt_delegate.rb +13 -10
  49. data/lib/serega/validations/attribute/check_opt_hide.rb +3 -0
  50. data/lib/serega/validations/attribute/check_opt_key.rb +12 -9
  51. data/lib/serega/validations/attribute/check_opt_many.rb +3 -0
  52. data/lib/serega/validations/attribute/check_opt_serializer.rb +3 -0
  53. data/lib/serega/validations/attribute/check_opt_value.rb +12 -9
  54. data/lib/serega/validations/check_attribute_params.rb +29 -1
  55. data/lib/serega/validations/check_initiate_params.rb +17 -0
  56. data/lib/serega/validations/check_serialize_params.rb +16 -0
  57. data/lib/serega/validations/initiate/check_modifiers.rb +15 -0
  58. data/lib/serega/validations/utils/check_allowed_keys.rb +14 -0
  59. data/lib/serega/validations/utils/check_opt_is_bool.rb +11 -0
  60. data/lib/serega/validations/utils/check_opt_is_hash.rb +11 -0
  61. data/lib/serega/validations/utils/check_opt_is_string_or_symbol.rb +11 -0
  62. data/lib/serega/version.rb +4 -0
  63. data/lib/serega.rb +83 -24
  64. metadata +2 -3
  65. data/lib/serega/serializer.rb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b72ad9dbc7d5e4c23d627b78d898b5eea3df96eb1b5925b34c99905362727151
4
- data.tar.gz: c99f2d871340151b6631c659bd6b49687f4b0f1230ca8966b8031d34372a99cb
3
+ metadata.gz: '001918eece6824f604d5bdd2a43321346d474355fd629759eaa894dabf94d7f4'
4
+ data.tar.gz: e68ee37be935e8c4a9bc2eca849a6d56b68feb56155519a612976cddd2013871
5
5
  SHA512:
6
- metadata.gz: 7a5a3a16c35e75266c4a6f86459605965e237dd7b98ae0c2faa9929bccfee070a7fc62a3aac956a26223236af152990e4e6224b494c0f22cbafefbc35b5812f9
7
- data.tar.gz: 19bd1265a2c1160b715a457c274ac58f679a201664c31bb0ec3f5316a6b1b480ac6618b2bbcbc56493462a82f9e45059f11fb8618157dd0ed9c36c8574c81a57
6
+ metadata.gz: 4683bc9c753fbbcfc40c31e7a2833bddfe3a01711067a718ad9a01f263d8e7e576b768432e33dcaad688ddd2d3017e60ebfc3111d22a661d6f670ae465f9210c
7
+ data.tar.gz: ad5fd5c647484d9b68053e28a0e8a728e285727b7419e797cf89f73ea00abaf226c6c59d137afb7ba41b47fc1490b4948e11128d059257ed431da0574657998e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
@@ -2,11 +2,11 @@
2
2
 
3
3
  class Serega
4
4
  #
5
- # Stores Attribute data
5
+ # Stores serialized attribute data
6
6
  #
7
7
  class SeregaAttribute
8
8
  #
9
- # Stores Attribute instance methods
9
+ # Attribute instance methods
10
10
  #
11
11
  module AttributeInstanceMethods
12
12
  # @return [Symbol] Attribute name
data/lib/serega/config.rb CHANGED
@@ -4,7 +4,7 @@ require "forwardable"
4
4
 
5
5
  class Serega
6
6
  #
7
- # Core class that stores serializer configuration
7
+ # Stores serialization config
8
8
  #
9
9
  class SeregaConfig
10
10
  # :nocov: We can't use both :oj and :json adapters together
@@ -20,7 +20,11 @@ class Serega
20
20
  }.freeze
21
21
  # :nocov:
22
22
 
23
+ # SeregaConfig Instance methods
23
24
  module SeregaConfigInstanceMethods
25
+ #
26
+ # @return [Hash] All config options
27
+ #
24
28
  attr_reader :opts
25
29
 
26
30
  #
@@ -33,52 +37,70 @@ class Serega
33
37
  @opts = SeregaUtils::EnumDeepDup.call(opts)
34
38
  end
35
39
 
40
+ # @return [Array] Used plugins
36
41
  def plugins
37
42
  opts.fetch(:plugins)
38
43
  end
39
44
 
45
+ # @return [Array<Symbol>] Allowed options keys for serializer initialization
40
46
  def initiate_keys
41
47
  opts.fetch(:initiate_keys)
42
48
  end
43
49
 
50
+ # @return [Array<Symbol>] Allowed options keys for attribute initialization
44
51
  def attribute_keys
45
52
  opts.fetch(:attribute_keys)
46
53
  end
47
54
 
55
+ # @return [Array<Symbol>] Allowed options keys for serialization
48
56
  def serialize_keys
49
57
  opts.fetch(:serialize_keys)
50
58
  end
51
59
 
60
+ # @return [Boolean] Current :check_initiate_params config option
52
61
  def check_initiate_params
53
62
  opts.fetch(:check_initiate_params)
54
63
  end
55
64
 
65
+ # @param value [Boolean] Set :check_initiate_params config option
66
+ #
67
+ # @return [Boolean] :check_initiate_params config option
56
68
  def check_initiate_params=(value)
57
69
  raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
58
70
  opts[:check_initiate_params] = value
59
71
  end
60
72
 
73
+ # @return [Boolean] Current :max_cached_map_per_serializer_count config option
61
74
  def max_cached_map_per_serializer_count
62
75
  opts.fetch(:max_cached_map_per_serializer_count)
63
76
  end
64
77
 
78
+ # @param value [Boolean] Set :check_initiate_params config option
79
+ #
80
+ # @return [Boolean] New :max_cached_map_per_serializer_count config option
65
81
  def max_cached_map_per_serializer_count=(value)
66
82
  raise SeregaError, "Must have Integer value, #{value.inspect} provided" unless value.is_a?(Integer)
67
83
  opts[:max_cached_map_per_serializer_count] = value
68
84
  end
69
85
 
86
+ # @return [#call] Callable that used to construct JSON
70
87
  def to_json
71
88
  opts.fetch(:to_json)
72
89
  end
73
90
 
91
+ # @param value [#call] Callable that used to construct JSON
92
+ # @return [#call] Provided callable object
74
93
  def to_json=(value)
75
94
  opts[:to_json] = value
76
95
  end
77
96
 
97
+ # @return [#call] Callable that used to parse JSON
78
98
  def from_json
79
99
  opts.fetch(:from_json)
80
100
  end
81
101
 
102
+ # @param value [#call] Callable that used to parse JSON
103
+ # @return [#call] Provided callable object
82
104
  def from_json=(value)
83
105
  opts[:from_json] = value
84
106
  end
data/lib/serega/errors.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Serega
4
- # A generic exception Serega uses.
4
+ #
5
+ # Base exception class
6
+ #
5
7
  class SeregaError < StandardError; end
6
8
 
7
- # AttributeNotExist is raised when serializer is initiated using not existing attribute
8
- # Example:
9
- # UserSerializer.new(only: 'FOO', except: 'FOO', with: 'FOO')
10
- # UserSerializer.to_h(user, only: 'FOO', except: 'FOO', with: 'FOO' )
9
+ # Raised when serializer is initiated using not existing attribute
10
+ #
11
+ # @example
12
+ # Serega.new(only: 'FOO')
13
+ # # => Attribute 'FOO' not exists (Serega::AttributeNotExist)
11
14
  class AttributeNotExist < SeregaError; end
12
15
  end
@@ -1,8 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Serega
4
+ #
5
+ # Helpers
6
+ #
4
7
  module SeregaHelpers
8
+ #
5
9
  # Stores link to current serializer class
10
+ #
6
11
  module SerializerClassHelper
7
12
  # @return [Class<Serega>] Serializer class that current class is namespaced under.
8
13
  attr_accessor :serializer_class
@@ -1,7 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Serega
4
+ #
5
+ # JSON adapters
6
+ #
4
7
  module SeregaJSON
8
+ # Current JSON adapter
9
+ #
10
+ # @return [Symbol] Current JSON adapter name - :oj or :json
5
11
  def self.adapter
6
12
  @adapter ||=
7
13
  if defined?(::Oj)
@@ -2,13 +2,33 @@
2
2
 
3
3
  class Serega
4
4
  module SeregaJSON
5
+ #
6
+ # JSON dump adapter for ::JSON
7
+ #
5
8
  class JSONDump
9
+ #
10
+ # Dumps data to JSON string
11
+ #
12
+ # @param data [Object] Anything
13
+ #
14
+ # @return [String] Data serialized to JSON
15
+ #
6
16
  def self.call(data)
7
17
  ::JSON.dump(data)
8
18
  end
9
19
  end
10
20
 
21
+ #
22
+ # JSON parse adapter for ::JSON
23
+ #
11
24
  class JSONLoad
25
+ #
26
+ # Loads object from JSON string
27
+ #
28
+ # @param json_string [String] JSON String
29
+ #
30
+ # @return [Object] Deserialized data
31
+ #
12
32
  def self.call(json_string)
13
33
  ::JSON.parse(json_string)
14
34
  end
@@ -2,13 +2,33 @@
2
2
 
3
3
  class Serega
4
4
  module SeregaJSON
5
+ #
6
+ # JSON dump adapter for ::Oj
7
+ #
5
8
  class OjDump
9
+ #
10
+ # Dumps data to JSON string
11
+ #
12
+ # @param data [Object] Anything
13
+ #
14
+ # @return [String] Data serialized to JSON
15
+ #
6
16
  def self.call(data)
7
17
  ::Oj.dump(data, mode: :compat)
8
18
  end
9
19
  end
10
20
 
21
+ #
22
+ # JSON parse adapter for ::Oj
23
+ #
11
24
  class OjLoad
25
+ #
26
+ # Loads object from JSON string
27
+ #
28
+ # @param json_string [String] JSON String
29
+ #
30
+ # @return [Object] Deserialized data
31
+ #
12
32
  def self.call(json_string)
13
33
  ::Oj.load(json_string)
14
34
  end
data/lib/serega/map.rb CHANGED
@@ -1,8 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Serega
4
+ #
5
+ # Constructs map of attributes that should be serialized.
6
+ # We will traverse this map to construct serialized response.
7
+ #
4
8
  class SeregaMap
9
+ #
10
+ # SeregaMap class methods
11
+ #
5
12
  module ClassMethods
13
+ #
14
+ # Constructs map of attributes that should be serialized.
15
+ #
16
+ # @param opts Serialization parameters
17
+ # @option opts [Hash] :only The only attributes to serialize
18
+ # @option opts [Hash] :except Attributes to hide
19
+ # @option opts [Hash] :with Attributes (usually hidden) to serialize additionally
20
+ #
21
+ # @return [Array<Serega::SeregaMapPoint>] map
22
+ #
6
23
  def call(opts)
7
24
  max_cache_size = serializer_class.config.max_cached_map_per_serializer_count
8
25
  return map_for(opts) if max_cache_size.zero?
@@ -1,23 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Serega
4
+ #
5
+ # Combines attribute and nested attributes
6
+ #
4
7
  class SeregaMapPoint
8
+ #
9
+ # SeregaMapPoint instance methods
10
+ #
5
11
  module InstanceMethods
6
12
  extend Forwardable
7
13
 
8
- attr_reader :attribute, :nested_points
14
+ # @return [Serega::SeregaAttribute] Current attribute
15
+ attr_reader :attribute
9
16
 
17
+ # @return [NilClass, Array<Serega::SeregaMapPoint>] Nested points or nil
18
+ attr_reader :nested_points
19
+
20
+ # @!method name
21
+ # Attribute `name`
22
+ # @see Serega::SeregaAttribute::AttributeInstanceMethods#name
23
+ # @!method value
24
+ # Attribute `value` block
25
+ # @see Serega::SeregaAttribute::AttributeInstanceMethods#value
26
+ # @!method many
27
+ # Attribute `many` option
28
+ # @see Serega::SeregaAttribute::AttributeInstanceMethods#many
10
29
  def_delegators :@attribute, :name, :value, :many
11
30
 
31
+ #
32
+ # Initializes map point
33
+ #
34
+ # @param attribute [Serega::SeregaAttribute] Attribute to construct map point
35
+ # @param nested_points [NilClass, Array<Serega::SeregaMapPoint>] Nested map points for provided attribute
36
+ #
37
+ # @return [Serega::SeregaMapPoint] New map point
38
+ #
12
39
  def initialize(attribute, nested_points)
13
40
  @attribute = attribute
14
41
  @nested_points = nested_points
15
42
  end
16
43
 
44
+ #
45
+ # Checks if attribute has nested points (is a link to another serializer)
46
+ #
47
+ # @return [Boolean] whether attribute has nested points
48
+ #
17
49
  def has_nested_points?
18
50
  !nested_points.nil?
19
51
  end
20
52
 
53
+ #
54
+ # @return [Serega::SeregaObjectSerializer] object serializer for nested points
55
+ #
21
56
  def nested_object_serializer
22
57
  attribute.serializer::SeregaObjectSerializer
23
58
  end
@@ -1,13 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Serega
4
+ #
5
+ # Low-level class that is used by more high-level SeregaSerializer
6
+ # to construct serialized to hash response
7
+ #
4
8
  class SeregaObjectSerializer
5
- module SeregaObjectSerializerInstanceMethods
9
+ #
10
+ # SeregaObjectSerializer instance methods
11
+ #
12
+ module InstanceMethods
6
13
  attr_reader :context, :points, :many, :opts
7
14
 
8
15
  # @param context [Hash] Serialization context
9
16
  # @param many [TrueClass|FalseClass] is object is enumerable
10
17
  # @param points [Array<MapPoint>] Serialization points (attributes)
18
+ #
19
+ # @return [SeregaObjectSerializer] New SeregaObjectSerializer
11
20
  def initialize(context:, points:, many: nil, **opts)
12
21
  @context = context
13
22
  @points = points
@@ -15,8 +24,14 @@ class Serega
15
24
  @opts = opts
16
25
  end
17
26
 
27
+ # Serializes object(s)
28
+ #
18
29
  # @param object [Object] Serialized object
30
+ #
31
+ # @return [Hash, Array<Hash>] Serialized object(s)
19
32
  def serialize(object)
33
+ return if object.nil?
34
+
20
35
  array?(object, many) ? serialize_array(object) : serialize_object(object)
21
36
  end
22
37
 
@@ -27,8 +42,6 @@ class Serega
27
42
  end
28
43
 
29
44
  def serialize_object(object)
30
- return unless object
31
-
32
45
  points.each_with_object({}) do |point, container|
33
46
  attach_value(object, point, container)
34
47
  end
@@ -62,6 +75,6 @@ class Serega
62
75
  end
63
76
 
64
77
  extend Serega::SeregaHelpers::SerializerClassHelper
65
- include SeregaObjectSerializerInstanceMethods
78
+ include InstanceMethods
66
79
  end
67
80
  end
@@ -3,56 +3,104 @@
3
3
  class Serega
4
4
  module SeregaPlugins
5
5
  #
6
- # Plugin that checks used plugins and loads correct Preloader for selected response type
7
- # @see Serega::SeregaPlugins::JsonApiActiverecordPreloader
8
- # @see Serega::SeregaPlugins::SimpleApiActiverecordPreloader
6
+ # Plugin :activerecord_preloads
7
+ # (depends on :preloads plugin, that must be loaded first)
8
+ #
9
+ # Automatically preloads associations to serialized objects
10
+ #
11
+ # It takes all defined preloads from serialized attributes (including attributes from serialized relations),
12
+ # merges them into single associations hash and then uses ActiveRecord::Associations::Preloader
13
+ # to preload all associations.
14
+ #
15
+ # @example
16
+ # class AppSerializer < Serega
17
+ # plugin :preloads,
18
+ # auto_preload_attributes_with_delegate: true,
19
+ # auto_preload_attributes_with_serializer: true,
20
+ # auto_hide_attributes_with_preload: true
21
+ #
22
+ # plugin :activerecord_preloads
23
+ # end
24
+ #
25
+ # class UserSerializer < AppSerializer
26
+ # # no preloads
27
+ # attribute :username
28
+ #
29
+ # # preloads `:user_stats` as auto_preload_attributes_with_delegate option is true
30
+ # attribute :comments_count, delegate: { to: :user_stats }
31
+ #
32
+ # # preloads `:albums` as auto_preload_attributes_with_serializer option is true
33
+ # attribute :albums, serializer: AlbumSerializer, hide: false
34
+ # end
35
+ #
36
+ # class AlbumSerializer < AppSerializer
37
+ # # no preloads
38
+ # attribute :title
39
+ #
40
+ # # preloads :downloads_count as manually specified
41
+ # attribute :downloads_count, preload: :downloads, value: proc { |album| album.downloads.count }
42
+ # end
43
+ #
44
+ # UserSerializer.to_h(user) # => preloads {users_stats: {}, albums: { downloads: {} }}
9
45
  #
10
46
  module ActiverecordPreloads
11
- # @return [Symbol] plugin name
47
+ #
48
+ # @return [Symbol] Plugin name
49
+ #
12
50
  def self.plugin_name
13
51
  :activerecord_preloads
14
52
  end
15
53
 
16
- def self.before_load_plugin(serializer_class, **opts)
54
+ # Checks requirements to load plugin
55
+ #
56
+ # @param serializer_class [Class<Serega>] Current serializer class
57
+ # @param _opts [Hash] plugins options
58
+ #
59
+ # @return [void]
60
+ #
61
+ def self.before_load_plugin(serializer_class, **_opts)
62
+ unless serializer_class.plugin_used?(:preloads)
63
+ raise SeregaError, "Please load `plugin :preloads` first"
64
+ end
65
+
17
66
  if serializer_class.plugin_used?(:batch)
18
67
  raise SeregaError, "Plugin `activerecord_preloads` must be loaded before `batch`"
19
68
  end
20
-
21
- serializer_class.plugin(:preloads, **opts) unless serializer_class.plugin_used?(:preloads)
22
69
  end
23
70
 
24
71
  #
25
- # Loads plugin code and additional plugins
72
+ # Applies plugin code to specific serializer
26
73
  #
27
74
  # @param serializer_class [Class<Serega>] Current serializer class
28
- # @param opts [Hash] loaded plugins opts
75
+ # @param _opts [Hash] Loaded plugins options
29
76
  #
30
77
  # @return [void]
31
78
  #
32
- def self.load_plugin(serializer_class, **opts)
79
+ def self.load_plugin(serializer_class, **_opts)
33
80
  require_relative "./lib/preloader"
34
81
 
35
82
  serializer_class.include(InstanceMethods)
36
83
  end
37
84
 
38
- # Overrides Serega classes instance methods
85
+ #
86
+ # Overrides Serega class instance methods
87
+ #
39
88
  module InstanceMethods
89
+ private
90
+
40
91
  #
41
- # Override original #to_h method
42
- # @see Serega#to_h
92
+ # Override original #serialize method
93
+ # Preloads associations to object before serialization
43
94
  #
44
- def to_h(object, *)
95
+ def serialize(object, _opts)
45
96
  object = add_preloads(object)
46
97
  super
47
98
  end
48
99
 
49
- private
50
-
51
100
  def add_preloads(obj)
52
101
  return obj if obj.nil? || (obj.is_a?(Array) && obj.empty?)
53
102
 
54
- # preloads() method comes from :preloads plugin
55
- preloads = preloads()
103
+ preloads = preloads() # `preloads()` method comes from :preloads plugin
56
104
  return obj if preloads.empty?
57
105
 
58
106
  Preloader.preload(obj, preloads)