Class: OjSerializers::Serializer

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

Overview

Public: Implementation of an “ActiveModelSerializer”-like DSL, but with a design that allows replacing the internal object, which greatly reduces object allocation.

Unlike ActiveModelSerializer, which builds a Hash which then gets encoded to JSON, this implementation allows to use Oj::StringWriter to write directly to JSON, greatly reducing the overhead of allocating and garbage collecting the hashes.

Constant Summary collapse

ALLOWED_INSTANCE_VARIABLES =

Public: Used to validate incorrect memoization during development. Users of this library might add additional options as needed.

%w[
  memo
  object
  options
  _routes
]
KNOWN_ATTRIBUTE_OPTIONS =
%i[
  attribute
  association
  identifier
  if
  optional
  type
  serializer
].to_set
CACHE =
(Rails) && Rails.cache) ||
(defined?(ActiveSupport::Cache::MemoryStore) ? ActiveSupport::Cache::MemoryStore.new : OjSerializers::Memo.new)
DEV_MODE =

Internal: Used to display warnings or detect misusage during development.

%w[test development].include?(environment) && !ENV['BENCHMARK']
DEFAULT_OPTIONS =
{}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

._attributesObject

Internal: List of attributes to be serialized.

Any attributes defined in parent classes are inherited.



234
235
236
# File 'lib/oj_serializers/serializer.rb', line 234

def _attributes
  @_attributes ||= superclass.try(:_attributes)&.dup || {}
end

.default_format(format) ⇒ Object

Public: Allows the user to specify ‘default_format :json`, as a simple way to ensure that `.one` and `.many` work as in Version 1.

This setting is inherited from parent classes.



105
106
107
108
# File 'lib/oj_serializers/serializer.rb', line 105

def default_format(format)
  define_singleton_method(:_default_format) { format }
  define_serialization_shortcuts
end

.inherited(subclass) ⇒ Object

Internal: Will alias the object according to the name of the wrapper class.



225
226
227
228
229
# File 'lib/oj_serializers/serializer.rb', line 225

def inherited(subclass)
  object_alias = subclass.name.demodulize.chomp('Serializer').underscore
  subclass.object_as(object_alias) unless method_defined?(object_alias) || object_alias == 'base'
  super
end

.many_as_hash(items, options = nil) ⇒ Object

Public: Renders an array of items using this serializer, without serializing to JSON.

items - Must respond to ‘each`. options - list of external options to pass to the sub class (available in `item.options`)

Returns an Array of Hash, each with the attributes specified in the serializer.



219
220
221
222
# File 'lib/oj_serializers/serializer.rb', line 219

def many_as_hash(items, options = nil)
  serializer = instance
  items.map { |item| serializer.render_as_hash(item, options) }
end

.many_as_json(items, options = nil) ⇒ Object

Public: Serializes an array of items using this serializer.

items - Must respond to ‘each`. options - list of external options to pass to the sub class (available in `item.options`)

Returns an Oj::StringWriter instance, which is encoded as raw json.



195
196
197
198
199
# File 'lib/oj_serializers/serializer.rb', line 195

def many_as_json(items, options = nil)
  writer = new_json_writer
  write_many(writer, items, options)
  writer
end

.object_as(name) ⇒ Object

Public: Creates an alias for the internal object.



140
141
142
# File 'lib/oj_serializers/serializer.rb', line 140

def object_as(name, **)
  define_method(name) { @object }
end

.one_as_hash(item, options = nil) ⇒ Object

Public: Renders the configured attributes for the specified object, without serializing to JSON.

item - the item to serialize options - list of external options to pass to the sub class (available in ‘item.options`)

Returns a Hash, with the attributes specified in the serializer.



208
209
210
# File 'lib/oj_serializers/serializer.rb', line 208

def one_as_hash(item, options = nil)
  instance.render_as_hash(item, options)
end

.one_as_json(item, options = nil) ⇒ Object

Public: Serializes the configured attributes for the specified object.

item - the item to serialize options - list of external options to pass to the sub class (available in ‘item.options`)

Returns an Oj::StringWriter instance, which is encoded as raw json.



183
184
185
186
187
# File 'lib/oj_serializers/serializer.rb', line 183

def one_as_json(item, options = nil)
  writer = new_json_writer
  write_one(writer, item, options)
  writer
end

.one_if(item, options = nil) ⇒ Object

Helper: Serializes the item unless it’s nil.



173
174
175
# File 'lib/oj_serializers/serializer.rb', line 173

def one_if(item, options = nil)
  one(item, options) if item
end

.render(item, options = nil) ⇒ Object

Helper: Serializes one or more items.



163
164
165
# File 'lib/oj_serializers/serializer.rb', line 163

def render(item, options = nil)
  many?(item) ? many(item, options) : one(item, options)
end

.render_as_hash(item, options = nil) ⇒ Object

Helper: Serializes one or more items.



168
169
170
# File 'lib/oj_serializers/serializer.rb', line 168

def render_as_hash(item, options = nil)
  many?(item) ? many_as_hash(item, options) : one_as_hash(item, options)
end

.sort_attributes_by(strategy) ⇒ Object

Public: Allows to sort fields by name instead of by definition order, or pass a Proc to apply a custom order.

This setting is inherited from parent classes.



114
115
116
117
118
119
120
121
# File 'lib/oj_serializers/serializer.rb', line 114

def sort_attributes_by(strategy)
  case strategy
  when :name, :definition, Proc
    define_singleton_method(:_sort_attributes_by) { strategy }
  else
    raise ArgumentError, "Unknown sorting option: #{strategy.inspect}"
  end
end

.transform_keys(strategy = nil, &block) ⇒ Object

Public: Allows to transform the JSON keys to camelCase, or pass a Proc to apply a custom transformation.

This setting is inherited from parent classes.



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/oj_serializers/serializer.rb', line 127

def transform_keys(strategy = nil, &block)
  transformer = case (strategy ||= block)
  when :camelize, :camel_case then ->(key) { key.camelize(:lower).chomp('?') }
  when :none then nil
  when Symbol then strategy.to_proc
  when Proc then strategy
  else
    raise(ArgumentError, "Expected transform_keys to be callable, got: #{strategy.inspect}")
  end
  define_singleton_method(:_transform_keys) { transformer }
end

Instance Method Details

#_check_instance_variablesObject



58
59
60
61
62
63
# File 'lib/oj_serializers/serializer.rb', line 58

def _check_instance_variables
  if instance_values.keys.any? { |key| !ALLOWED_INSTANCE_VARIABLES.include?(key) }
    bad_keys = instance_values.keys.reject { |key| ALLOWED_INSTANCE_VARIABLES.include?(key) }
    raise ArgumentError, "Serializer instances are reused so they must be stateless. Use `memo.fetch` for memoization purposes instead. Bad keys: #{bad_keys.join(',')} in #{self.class}"
  end
end

#optionsObject

Backwards Compatibility: Allows to access options passed through ‘render json`, in the same way than ActiveModel::Serializers.



52
53
54
# File 'lib/oj_serializers/serializer.rb', line 52

def options
  @options || DEFAULT_OPTIONS
end

#write_many(writer, items, options = nil) ⇒ Object

Internal: Used internally to write an array of objects to JSON.

writer - writer used to serialize results items - items to serialize results for options - list of external options to pass to the serializer (available as ‘options`)



85
86
87
88
89
90
91
# File 'lib/oj_serializers/serializer.rb', line 85

def write_many(writer, items, options = nil)
  writer.push_array
  items.each do |item|
    write_one(writer, item, options)
  end
  writer.pop
end

#write_one(writer, item, options = nil) ⇒ Object

Internal: Used internally to write a single object to JSON.

writer - writer used to serialize results item - item to serialize results for options - list of external options to pass to the serializer (available as ‘options`)

NOTE: Binds this instance to the specified object and options and writes to json using the provided writer.



74
75
76
77
78
# File 'lib/oj_serializers/serializer.rb', line 74

def write_one(writer, item, options = nil)
  writer.push_object
  write_to_json(writer, item, options)
  writer.pop
end