Module: Serega::ClassMethods

Included in:
Serega
Defined in:
lib/serega.rb

Overview

Serializers class methods

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#configSeregaConfig (readonly)

Returns current config

Returns:



107
108
109
# File 'lib/serega.rb', line 107

def config
  @config
end

Instance Method Details

#attribute(name, **opts, &block) ⇒ Serega::SeregaAttribute

Adds attribute

Examples:

attribute :statistics, method: :itself do
  attribute :likes_count
  attribute :comments_count
end

Parameters:

  • name (Symbol)

    Attribute name. Attribute value will be found by executing object.<name>

  • opts (Hash)

    Options to serialize attribute

  • block (Proc)

    Defines attributes of a nested anonymous serializer. The block is executed in the context of that serializer, so everything available in a serializer class body can be used inside. The attribute value (found by name/:method/:value/:delegate/:const/:batch as usual) is serialized with this nested serializer. The nested serializer inherits from the :base_serializer attribute option or config.base_serializer (one of them is required).

Returns:



205
206
207
208
# File 'lib/serega.rb', line 205

def attribute(name, **opts, &block)
  attribute = self::SeregaAttribute.new(name: name, opts: opts, block: block)
  attributes[attribute.name] = attribute
end

#attributesHash

Lists attributes

Returns:

  • (Hash)

    attributes list



160
161
162
# File 'lib/serega.rb', line 160

def attributes
  @attributes ||= {}
end

#batch(name, value = nil, &block) ⇒ #call

Defines a batch loader

Examples:

batch :tags, PostTagsLoader

with block

batch_loader(:tags) do |posts|
  Tags.where(post: posts).group(:post_id).pluck(:post_id, Arel.sql('ARRAY_AGG(tags.tag ORDER BY tag)')).to_h
end

attribute :tags, batch: :tags, value: { |post, batch:| batch[:tags][post.id] }

with context

batch_loader(:tags) do |posts, ctx:|
  next {} if ctx[:bot]

  Tags.where(post: posts).group(:post_id).pluck(:post_id, Arel.sql('ARRAY_AGG(tags.tag ORDER BY tag)')).to_h
end

attribute :tags, batch: :tags, value: { |post, batch:| batch[:tags][post.id] }

Parameters:

  • name (Symbol)

    A batch loader name

  • value (#call) (defaults to: nil)

    Batch loader

  • block (Proc)

    Batch loader

Returns:

  • (#call)

    Batch loader

Raises:



238
239
240
241
242
243
# File 'lib/serega.rb', line 238

def batch(name, value = nil, &block)
  raise SeregaError, "Batch loader must be defined with a callable value or block" if (value && block) || (!value && !block)

  batch_loader = self::SeregaEngineLoader.new(name: name, block: value || block)
  batch_loaders[batch_loader.name] = batch_loader
end

#batch_loadersHash

Lists defined batch loaders

Returns:

  • (Hash)

    batch loaders list



170
171
172
# File 'lib/serega.rb', line 170

def batch_loaders
  @batch_loaders ||= {}
end

#call(object, opts = nil) ⇒ Hash Also known as: to_h

Serializes provided object to Hash

Parameters:

  • object (Object)

    Serialized object

  • opts (Hash, nil) (defaults to: nil)

    Serializer modifiers and other instantiating options

Options Hash (opts):

  • :only (Array, Hash, String, Symbol)

    The only attributes to serialize

  • :except (Array, Hash, String, Symbol)

    Attributes to hide

  • :with (Array, Hash, String, Symbol)

    Attributes (usually hidden) to serialize additionally

  • :validate (Boolean)

    Validates provided modifiers (Default is true)

  • :context (Hash)

    Serialization context

  • :many (Boolean)

    Set true if provided multiple objects (Default object.is_a?(Enumerable) && !object.is_a?(Hash) && !object.is_a?(Struct))

Returns:

  • (Hash)

    Serialization result



372
373
374
375
376
377
# File 'lib/serega.rb', line 372

def call(object, opts = nil)
  opts = opts&.transform_keys(&:to_sym)
  modifiers_opts = init_modifier_opts(opts)
  serialize_opts = init_serialize_opts(opts)
  new(modifiers_opts).to_h(object, serialize_opts)
end

#plugin(name, **opts) ⇒ class<Module>

Enables plugin for current serializer

Parameters:

  • name (Symbol, Class<Module>)

    Plugin name or plugin module itself

  • opts (Hash)

    ] Plugin options

Returns:

  • (class<Module>)

    Loaded plugin module

Raises:



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/serega.rb', line 116

def plugin(name, **opts)
  raise SeregaError, "This plugin is already loaded" if plugin_used?(name)

  plugin = SeregaPlugins.find_plugin(name)

  # We split loading of plugin to three parts - before_load, load, after_load:
  #
  # - **before_load_plugin** usually used to check requirements and to load additional plugins
  # - **load_plugin** usually used to include plugin modules
  # - **after_load_plugin** usually used to add config options
  plugin.before_load_plugin(self, **opts) if plugin.respond_to?(:before_load_plugin)
  plugin.load_plugin(self, **opts) if plugin.respond_to?(:load_plugin)
  plugin.after_load_plugin(self, **opts) if plugin.respond_to?(:after_load_plugin)

  # Store attached plugins, so we can check it is loaded later
  config.plugins << (plugin.respond_to?(:plugin_name) ? plugin.plugin_name : plugin)

  plugin
end

#plugin_used?(name) ⇒ Boolean

Checks plugin is used

Parameters:

  • name (Symbol, Class<Module>)

    Plugin name or plugin module itself

Returns:

  • (Boolean)

    Is plugin used



144
145
146
147
148
149
150
151
152
# File 'lib/serega.rb', line 144

def plugin_used?(name)
  plugin_name =
    case name
    when Module then name.respond_to?(:plugin_name) ? name.plugin_name : name
    else name
    end

  config.plugins.include?(plugin_name)
end

#preload_with(value = nil, &block) ⇒ #call?

Registers (or returns) the handler used to preload an attribute's associations onto the records gathered during serialization.

The handler is called once per preloaded attribute with the gathered objects and that attribute's preloads. ORM plugins register a handler that performs the actual eager loading.

Examples:

with a block

preload_with { |objects, preloads| MyORM.eager_load(objects, preloads) }

with a callable value

preload_with MyPreloader

Parameters:

  • value (#call, nil) (defaults to: nil)

    Preload handler accepting two positional arguments

  • block (Proc)

    Preload handler accepting two positional arguments

Returns:

  • (#call, nil)

    The registered preload handler

Raises:



288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/serega.rb', line 288

def preload_with(value = nil, &block)
  return @preload_with if value.nil? && block.nil?
  raise SeregaError, "preload_with accepts a single callable or a block, not both" if value && block

  handler = value || block
  raise SeregaError, "preload_with value must be a Proc or respond to #call" if !handler.is_a?(Proc) && !handler.respond_to?(:call)

  signature = SeregaUtils::MethodSignature.call(handler, pos_limit: 2)
  raise SeregaError, "preload_with handler must accept two positional arguments: (objects, preloads)" unless signature == "2"

  @preload_with = handler
end

#prepare_initial_objects(value = nil, &block) ⇒ #call?

Registers (or returns) the handler that replaces the serialized objects before serialization starts.

The handler is called once per serialization with the objects provided to .call/.to_h/.to_data and the serialization context. Its result is serialized instead of the provided objects, which allows to accept ids or other references and load the records in one place.

The handler runs before the :many option is detected, so it may turn a single object into a collection and back. A provided :many serialization option is still used as is.

The handler runs only for the serialized objects, and not for objects of nested serializers.

Examples:

with a block

prepare_initial_objects { |user_ids| User.where(id: user_ids) }

with a context

prepare_initial_objects { |user_ids, ctx| User.where(id: user_ids, account: ctx[:account]) }

with a keyword context

prepare_initial_objects { |user_ids, ctx:| User.where(id: user_ids, account: ctx[:account]) }

with a callable value

prepare_initial_objects UsersLoader

Parameters:

  • value (#call, nil) (defaults to: nil)

    Handler accepting (objects), (objects, context) or (objects, ctx:)

  • block (Proc)

    Handler accepting (objects), (objects, context) or (objects, ctx:)

Returns:

  • (#call, nil)

    The registered handler

Raises:



334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/serega.rb', line 334

def prepare_initial_objects(value = nil, &block)
  return @prepare_initial_objects if value.nil? && block.nil?
  raise SeregaError, "prepare_initial_objects accepts a single callable or a block, not both" if value && block

  handler = value || block
  raise SeregaError, "prepare_initial_objects value must be a Proc or respond to #call" if !handler.is_a?(Proc) && !handler.respond_to?(:call)

  signature = SeregaUtils::MethodSignature.call(handler, pos_limit: 2, keyword_args: [:ctx])
  raise SeregaError, prepare_initial_objects_signature_error unless %w[1 2 1_ctx].include?(signature)

  @prepare_initial_objects_signature = signature
  @prepare_initial_objects = handler
end

#prepare_initial_objects_signatureString?

Signature of the registered prepare_initial_objects handler

Returns:

  • (String, nil)

    Handler signature



354
355
356
# File 'lib/serega.rb', line 354

def prepare_initial_objects_signature
  @prepare_initial_objects_signature
end

#presenter(&block) ⇒ Class<SeregaPresenter>?

Defines presenter methods — evaluates the block inside the serializer's own presenter class. Multiple blocks accumulate.

presenter do
def name
  [first_name, last_name].compact.join(" ")
end
end

Parameters:

  • block (Proc)

    Presenter methods

Returns:

  • (Class<SeregaPresenter>, nil)

    the serializer presenter class, nil when the serializer has none



260
261
262
263
264
265
266
267
# File 'lib/serega.rb', line 260

def presenter(&block)
  return @presenter_class unless block

  @presenter_class ||= Class.new(SeregaPresenter)
  @presenter_class.class_exec(&block)
  presenter_blocks << block
  @presenter_class
end

#presenter_blocksArray<Proc>

Lists blocks given to presenter, in definition order

Returns:

  • (Array<Proc>)

    presenter blocks list



180
181
182
# File 'lib/serega.rb', line 180

def presenter_blocks
  @presenter_blocks ||= []
end

#to_data(object, opts = nil) ⇒ Data, ...

Serializes provided object to a tree of Ruby Data objects

Parameters:

  • object (Object)

    Serialized object

  • opts (Hash, nil) (defaults to: nil)

    Serializer modifiers and other instantiating options

Options Hash (opts):

  • :only (Array, Hash, String, Symbol)

    The only attributes to serialize

  • :except (Array, Hash, String, Symbol)

    Attributes to hide

  • :with (Array, Hash, String, Symbol)

    Attributes (usually hidden) to serialize additionally

  • :validate (Boolean)

    Validates provided modifiers (Default is true)

  • :context (Hash)

    Serialization context

  • :many (Boolean)

    Set true if provided multiple objects (Default object.is_a?(Enumerable) && !object.is_a?(Hash) && !object.is_a?(Struct))

Returns:

  • (Data, Array<Data>, nil)

    Serialization result as Data object(s)



393
394
395
396
397
398
# File 'lib/serega.rb', line 393

def to_data(object, opts = nil)
  opts = opts&.transform_keys(&:to_sym)
  modifiers_opts = init_modifier_opts(opts)
  serialize_opts = init_serialize_opts(opts)
  new(modifiers_opts).to_data(object, serialize_opts)
end