Class: Barley::Serializer

Inherits:
Object
  • Object
show all
Defined in:
lib/barley/serializer.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object, cache: false, root: false, context: nil, only: nil, except: nil) ⇒ Serializer

Returns a new instance of Serializer.

Examples:

with cache

Barley::Serializer.new(object, cache: true)

with cache and expires_in

Barley::Serializer.new(object, cache: {expires_in: 1.hour})

Parameters:

  • object (Object)

    the object to serialize

  • cache (Boolean, Hash<Symbol, ActiveSupport::Duration>) (defaults to: false)

    a boolean to cache the result, or a hash with options for the cache

  • root (Boolean) (defaults to: false)

    whether to include the root key in the hash

  • context (Object) (defaults to: nil)

    an optional context object to pass additional data to the serializer

  • only (Array<Symbol>) (defaults to: nil)

    an array of attributes to include

  • except (Array<Symbol>) (defaults to: nil)

    an array of attributes to exclude



227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/barley/serializer.rb', line 227

def initialize(object, cache: false, root: false, context: nil, only: nil, except: nil)
  @object = object
  @context = context
  @root = root
  @only = only
  @except = except
  @cache, @expires_in = if cache.is_a?(Hash)
    [true, cache[:expires_in]]
  else
    [cache, nil]
  end
end

Class Attribute Details

.defined_attributesObject

Returns the value of attribute defined_attributes.



9
10
11
# File 'lib/barley/serializer.rb', line 9

def defined_attributes
  @defined_attributes
end

Instance Attribute Details

#contextObject

Returns the value of attribute context.



6
7
8
# File 'lib/barley/serializer.rb', line 6

def context
  @context
end

#objectObject

Returns the value of attribute object.



5
6
7
# File 'lib/barley/serializer.rb', line 5

def object
  @object
end

Class Method Details

.attribute(key, key_name: nil, type: nil, &block) ⇒ Object

Defines a single attribute for the serializer

Type checking is done with Dry::Types. If a type is not provided, the value is returned as is. Dry::Types can be used to coerce the value to the desired type and to check constraints.

Examples:

simple attribute

attribute :id
# => {id: 1234}

attribute with a different key name

attribute :name, key_name: :full_name
# => {full_name: "John Doe"}

attribute with a type

attribute :email, type: Types::Strict::String
# => {email: "john.doe@example"}

attribute with a type and a block

attribute :email, type: Types::Strict::String do
  object.email.upcase
end
# => {email: "JOHN.DOE@EXAMPLE"}

Parameters:

  • key (Symbol)

    the attribute name

  • key_name (Symbol) (defaults to: nil)

    the key name in the hash

  • type (Dry::Types) (defaults to: nil)

    the type to use, or coerce the value to

  • block (Proc)

    a block to use to compute the value

Raises:

  • (Dry::Types::ConstraintError)

    if the type does not match

  • (Barley::InvalidAttributeError)

    if the value does not match the type - when a type is provided

See Also:



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/barley/serializer.rb', line 77

def attribute(key, key_name: nil, type: nil, &block)
  key_name ||= key
  define_method(key_name) do
    value = block ? instance_eval(&block) : object.send(key)
    if type.nil?
      value
    else
      raise Barley::InvalidAttributeError, "Invalid value type found for attribute #{key_name}::#{type.name}: #{value}::#{value.class}" unless type.valid?(value)

      type[value]
    end
  end

  self.defined_attributes = (defined_attributes || []) << key_name
end

.attributes(*keys) ⇒ Object

Defines attributes for the serializer

Accepts either a list of symbols or a hash of symbols and Dry::Types, or a mix of both

Examples:

only symbols

attributes :id, :name, :email
# => {id: 1234, name: "John Doe", email: "john.doe@example"}

with types

attributes id: Types::Strict::Integer, name: Types::Strict::String, email: Types::Strict::String
# => {id: 1234, name: "John Doe", email: "john.doe@example"}

with types and symbols

attributes :id, name: Types::Strict::String, email: Types::Strict::String
# => {id: 1234, name: "John Doe", email: "john.doe@example"}

Parameters:

  • keys (Hash<Symbol, Dry::Types>, Array<Symbol>)

    mix of symbols and hashes of symbols and Dry::Types

See Also:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/barley/serializer.rb', line 30

def attributes(*keys)
  if keys.last.is_a?(Hash)
    keys.pop.each do |key, type|
      attribute(key, type: type)
    end
  end
  keys.each do |key|
    if key.is_a?(Hash)
      attribute(key.keys.first, type: key.values.first)
    else
      attribute(key)
    end
  end
end

.many(key, key_name: nil, serializer: nil, cache: false, scope: nil, &block) ⇒ Object

Defines a collection association for the serializer

Examples:

using the default serializer of the associated model

many :groups
# => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}

using a custom serializer

many :groups, serializer: MyCustomGroupSerializer
# => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}

using a block with an inline serializer definition

many :groups do
  attributes :id, :name
end
# => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}

using a different key name

many :groups, key_name: :my_groups
# => {my_groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}

using cache

many :groups, cache: true
# => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}

using cache and expires_in

many :groups, cache: {expires_in: 1.hour}
# => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}

using a named scope

many :groups, scope: :active # given the scope `active` is defined in the Group model
# => {groups: [{id: 5678, name: "Group 2"}]}

using a lambda scope

many :groups, scope: -> { order(id: :asc).limit(1) }
# => {groups: [{id: 1234, name: "Group 1"}]}

Parameters:

  • key (Symbol)

    the association name

  • key_name (Symbol) (defaults to: nil)

    the key name in the hash

  • serializer (Class) (defaults to: nil)

    the serializer to use

  • cache (Boolean, Hash<Symbol, ActiveSupport::Duration>) (defaults to: false)

    whether to cache the result, or a hash with options for the cache

  • scope (Symbol, Proc) (defaults to: nil)

    the scope to use to fetch the elements

  • block (Proc)

    a block to use to define the serializer inline



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/barley/serializer.rb', line 186

def many(key, key_name: nil, serializer: nil, cache: false, scope: nil, &block)
  key_name ||= key
  if block
    serializer = Class.new(Barley::Serializer) do
      instance_eval(&block)
    end
  end
  define_method(key_name) do
    elements = object.send(key)
    if scope.is_a?(Symbol)
      elements = elements.send(scope)
    elsif scope.is_a?(Proc)
      elements = if scope.arity == 1
        elements.instance_exec(@context, &scope)
      else
        elements.instance_exec(&scope)
      end
    end
    return [] if elements.empty?

    el_serializer = serializer || elements.first.serializer.class
    only = @only.find { |k| k.is_a?(Hash) && k.key?(key) }.slice(key).values.first if @only.present?
    except = @except.find { |k| k.is_a?(Hash) && k.key?(key) }.slice(key).values.first if @except.present?
    elements.map { |element| el_serializer.new(element, cache: cache, context: @context, only: only, except: except).serializable_hash }.reject(&:blank?)
  end
  self.defined_attributes = (defined_attributes || []) << key_name
end

.one(key, key_name: nil, serializer: nil, cache: false, &block) ⇒ Object

Defines a single association for the serializer

Examples:

using the default serializer of the associated model

one :group
# => {group: {id: 1234, name: "Group 1"}}

using a custom serializer

one :group, serializer: MyCustomGroupSerializer
# => {group: {id: 1234, name: "Group 1"}}

using a block with an inline serializer definition

one :group do
  attributes :id, :name
end
# => {group: {id: 1234, name: "Group 1"}}

using a different key name

one :group, key_name: :my_group
# => {my_group: {id: 1234, name: "Group 1"}}

using cache

one :group, cache: true
# => {group: {id: 1234, name: "Group 1"}}

using cache and expires_in

one :group, cache: {expires_in: 1.hour}
# => {group: {id: 1234, name: "Group 1"}}

Parameters:

  • key (Symbol)

    the association name

  • key_name (Symbol) (defaults to: nil)

    the key name in the hash

  • serializer (Class) (defaults to: nil)

    the serializer to use

  • cache (Boolean, Hash<Symbol, ActiveSupport::Duration>) (defaults to: false)

    whether to cache the result, or a hash with options for the cache

  • block (Proc)

    a block to use to define the serializer inline



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/barley/serializer.rb', line 126

def one(key, key_name: nil, serializer: nil, cache: false, &block)
  key_name ||= key
  if block
    serializer = Class.new(Barley::Serializer) do
      instance_eval(&block)
    end
  end
  define_method(key_name) do
    element = object.send(key)
    return {} if element.nil?

    el_serializer = serializer || element.serializer.class
    only = @only.find { |k| k.is_a?(Hash) && k.key?(key) }.slice(key).values.first if @only.present?
    except = @except.find { |k| k.is_a?(Hash) && k.key?(key) }.slice(key).values.first if @except.present?
    el_serializer.new(element, cache: cache, context: @context, only: only, except: except).serializable_hash
  end
  self.defined_attributes = (defined_attributes || []) << key_name
end

Instance Method Details

#clear_cache(key: cache_base_key) ⇒ Boolean

Clears the cache for the object

Parameters:

  • key (String) (defaults to: cache_base_key)

    the cache key

Returns:

  • (Boolean)

    whether the cache was cleared



267
268
269
# File 'lib/barley/serializer.rb', line 267

def clear_cache(key: cache_base_key)
  Barley::Cache.delete(key)
end

#serializable_hashHash

Serializes the object

Returns:

  • (Hash)

    the serializable hash



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/barley/serializer.rb', line 243

def serializable_hash
  hash = if @cache
    Barley::Cache.fetch(cache_base_key, expires_in: @expires_in) do
      _serializable_hash
    end
  else
    _serializable_hash
  end
  if @only.present?
    only = @only.map { |k| k.is_a?(Hash) ? k.keys.first : k }
    hash.slice!(*only)
  end
  if @except.present?
    except = @except.reject { |k| k.is_a?(Hash) }
    hash.except!(*except)
  end
  hash
end

#with_context(**args) ⇒ Barley::Serializer

Sets the context object for the serializer

The context object is a Struct built from the given arguments. It can be used to pass additional data to the serializer. The context object is accessible in the serializer with the ‘context` attribute.

Examples:

serializer.with_context(current_user: current_user, locale: I18n.locale)
# => #<Barley::Serializer:0x00007f8f3b8b3e08 @object=#<Product id: 1, name: "Product 1">, @context=#<struct current_user=1, locale=:en>>
# In the serializer:
attribute :name do
  "#{object.name[context.locale]}" # Will use the locale from the context
end

Parameters:

  • args (Hash)

    the context object attributes

Returns:



284
285
286
287
288
# File 'lib/barley/serializer.rb', line 284

def with_context(**args)
  @context = Struct.new(*args.keys).new(*args.values)

  self
end