lib/simply_serializable/serializer.rb in simply_serializable-1.2.2 vs lib/simply_serializable/serializer.rb in simply_serializable-1.3.0

- old
+ new

@@ -15,81 +15,135 @@ method_prefix: :serialize, object:, only: nil ) @object = object + @id = id @attributes = attributes @include_readable_instance_variables = include_readable_instance_variables @except = except&.map(&:to_sym) @only = only&.map(&:to_sym) @method_prefix = method_prefix - @cache = cache - @cache[cache_key] = nil - @serialized = false + @local_cache = cache + @local_cache[cache_key] = nil populate_attributes end def cache - serialize unless @serialized - @cache + @cache ||= begin + serialize + @local_cache + end end - def deep_serialize(obj) - case obj - when FalseClass, Float, nil, Integer, String, Symbol, TrueClass - obj - when Array - obj.map { |v| deep_serialize(v) } - when Hash - Hash[obj.map { |k, v| [deep_serialize(k), deep_serialize(v)] }] - when Module - obj.name - else - serialize_object(obj) + def id + @id ||= cache_key + end + + def nestable? + return @nestable unless @nestable.nil? + + serialize + @nestable + end + + def nested + @nested ||= begin + deep_nest(serialize[:root]) end end def serialize @serialize ||= begin - @serialized = true + @nestable = true ret = deep_serialize(object_value_hash) - cache[cache_key] = ret + @local_cache[cache_key] = { + id: cache_key, + object: object.class.name, + fingeprint: fingerprint, + data: ret + } + { root: cache_key, - objects: cache + objects: @local_cache } end end def to_s @to_s ||= serialize.to_s end private + def cache_key + @cache_key ||= cache_key_for(object) + end + + def cache_key_for(obj) + "#{obj.class.name}/#{fingerprint_for(obj)}" + end + + def deep_nest(obj_id) + raise Error::CircularDependencyError unless @nestable + + Hash[@local_cache[obj_id][:data].map do |k, v| + v = deep_nest(v.dig(:id)) if v.is_a?(Hash) && v.dig(:object) == :reference + [k, v] + end] + end + + def deep_serialize(obj) + case obj + when FalseClass, Float, nil, Integer, String, Symbol, TrueClass + obj + when Array + obj.map { |v| deep_serialize(v) } + when Hash + Hash[obj.map { |k, v| [deep_serialize(k), deep_serialize(v)] }] + when Module + obj.name + else + serialize_object(obj) + end + end + + def fingerprint + @fingerprint ||= fingerprint_for(object) + end + + def fingerprint_for(obj) + if obj.respond_to?(:fingerprint) + obj.fingerprint + else + Fingerprintable::Fingerprinter.new(object: obj).fingerprint + end + end + def instance_vars_with_readers instance_variables = Hash[object.instance_variables.map { |e| [e, 1] }] ret = object.class.instance_methods.select do |method| instance_variables.key?("@#{method}".to_sym) end ret.map(&:to_sym) end - def serialize_object(use_object) - use_object_cache_key = cache_key(use_object) - return use_object_cache_key if cache.key?(use_object_cache_key) - raise "#{use_object.class.name} does not respond to serialize. Did you mean to include Serializable in this class?" unless use_object.respond_to?(:serialize) - - serializer = use_object.serializer(cache: cache) - cache.merge!(serializer.cache) - use_object_cache_key + def method_for_attribute(attr) + if object.class.instance_methods.include?("#{@method_prefix}_#{attr}".to_sym) + "#{@method_prefix}_#{attr}" + else + attr + end end - def cache_key(obj = object) - obj.serializable_id + def object_value_hash + Hash[attributes.map do |attr| + [attr, object.send(method_for_attribute(attr))] + end] end def populate_attributes raise 'You cannot pass only and except values. Please choose one.' if !only.nil? && !except.nil? @@ -98,20 +152,34 @@ @attributes = only unless only.nil? @attributes = attributes - except unless except.nil? attributes end - def method_for_attribute(attr) - if object.class.instance_methods.include?("#{@method_prefix}_#{attr}".to_sym) - "#{@method_prefix}_#{attr}" - else - attr + def serialize_object(use_object) + use_object_cache_key = cache_key_for(use_object) + if @local_cache.key?(use_object_cache_key) + @nestable = false + return reference_to(use_object_cache_key) end + serializer = unless use_object.respond_to?(:serialize) + raise "#{use_object.class.name} does not respond to serialize. Did you mean to include Serializable in this class?" unless @include_readable_instance_variables + + Serializer.new( + cache: @local_cache, + object: use_object + ) + else + use_object.serializer(cache: @local_cache) + end + serializer + @local_cache.merge!(serializer.cache) + reference_to(use_object_cache_key) end - def object_value_hash - Hash[attributes.map do |attr| - [attr, object.send(method_for_attribute(attr))] - end] + def reference_to(key) + { + object: :reference, + id: key + } end end end