Skip to content

Commit

Permalink
Renderer - wip
Browse files Browse the repository at this point in the history
Signed-off-by: Jordan Hollinger <[email protected]>
  • Loading branch information
jhollinger committed Oct 26, 2024
1 parent 25f7964 commit 0b5e538
Show file tree
Hide file tree
Showing 42 changed files with 2,823 additions and 54 deletions.
1 change: 1 addition & 0 deletions lib/blueprinter/v2.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# frozen_string_literal: true

require 'blueprinter/v2/extensions'
require 'blueprinter/v2/base'
16 changes: 0 additions & 16 deletions lib/blueprinter/v2/association.rb

This file was deleted.

33 changes: 24 additions & 9 deletions lib/blueprinter/v2/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

require 'blueprinter/v2/dsl'
require 'blueprinter/v2/reflection'
require 'blueprinter/v2/render'
require 'blueprinter/v2/serializer'
require 'blueprinter/v2/view_builder'

module Blueprinter
Expand All @@ -12,7 +14,7 @@ class Base
extend Reflection

class << self
# Options set on this Blueprint
# Custom options set on this Blueprint
attr_accessor :options
# Extensions set on this Blueprint
attr_accessor :extensions
Expand Down Expand Up @@ -77,20 +79,29 @@ def self.[](name)
children ? view[children] : view
end

# MyBlueprint.render(obj).to_json
def self.render(obj, options = {})
if array_like? obj
if serializer.hooks.any?(:collection?, obj)
render_collection(obj, options)
else
render_object(obj, options)
end
end

# MyBlueprint.render_object(obj).to_json
def self.render_object(obj, options = {})
# TODO call external renderer
Render.new(obj, options, serializer: serializer, collection: false)
end

# MyBlueprint.render_collection(objs).to_json
def self.render_collection(objs, options = {})
# TODO call external renderer
Render.new(objs, options, serializer: serializer, collection: true)
end

# @api private
def self.serializer
eval! unless @evaled
@serializer
end

# Apply partials and field exclusions
Expand All @@ -115,12 +126,16 @@ def self.run_eval!
end

excludes.each { |f| fields.delete f }
@evaled = true
end
extensions.freeze
options.freeze
fields.freeze
fields.each do |_, f|
f.options&.freeze
f.freeze
end

# @api private
def self.array_like?(obj)
# TODO
@serializer = Serializer.new(self)
@evaled = true
end
end
end
Expand Down
36 changes: 27 additions & 9 deletions lib/blueprinter/v2/dsl.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

require 'blueprinter/v2/association'
require 'blueprinter/v2/field'
require 'blueprinter/v2/fields'

module Blueprinter
module V2
Expand Down Expand Up @@ -43,6 +42,13 @@ def use(*names)
#
# @param name [Symbol] Name of the field
# @param from [Symbol] Optionally specify a different method to call to get the value for "name"
# @param extractor [Class] Extractor class to use for this association
# @param if [Proc] TODO
# @param unless [Proc] TODO
# @param default [Object | Proc] TODO
# @param default_if [Proc] TODO
# @param exclude_if_nil [Boolean] TODO
# @param exclude_if_empty [Boolean] TODO
# @yield [TODO] Generate the value from the block
# @return [Blueprinter::V2::Field]
#
Expand All @@ -62,16 +68,22 @@ def field(name, from: name, **options, &definition)
# @param blueprint [Class|Proc] Blueprint class to use, or one defined with a Proc
# @param view [Symbol] Only for use with legacy (not V2) blueprints
# @param from [Symbol] Optionally specify a different method to call to get the value for "name"
# @param extractor [Class] Extractor class to use for this association
# @param if [Proc] TODO
# @param unless [Proc] TODO
# @param default [Object | Proc] TODO
# @param default_if [Proc] TODO
# @param exclude_if_nil [Boolean] TODO
# @param exclude_if_empty [Boolean] TODO
# @yield [TODO] Generate the value from the block
# @return [Blueprinter::V2::Association]
# @return [Blueprinter::V2::ObjectField]
#
def object(name, blueprint, from: name, view: nil, **options, &definition)
raise ArgumentError, 'The :view argument may not be used with V2 Blueprints' if view && blueprint.is_a?(V2)

fields[name.to_sym] = Association.new(
fields[name.to_sym] = ObjectField.new(
name: name,
blueprint: blueprint,
collection: false,
legacy_view: view,
from: from,
value_proc: definition,
Expand All @@ -86,16 +98,22 @@ def object(name, blueprint, from: name, view: nil, **options, &definition)
# @param blueprint [Class|Proc] Blueprint class to use, or one defined with a Proc
# @param view [Symbol] Only for use with legacy (not V2) blueprints
# @param from [Symbol] Optionally specify a different method to call to get the value for "name"
# @param extractor [Class] Extractor class to use for this association
# @param if [Proc] TODO
# @param unless [Proc] TODO
# @param default [Object | Proc] TODO
# @param default_if [Proc] TODO
# @param exclude_if_nil [Boolean] TODO
# @param exclude_if_empty [Boolean] TODO
# @yield [TODO] Generate the value from the block
# @return [Blueprinter::V2::Association]
# @return [Blueprinter::V2::Collection]
#
def collection(name, blueprint, from: name, view: nil, **options, &definition)
def collection(name, blueprint, from: name, view: nil, **options, &definition)
raise ArgumentError, 'The :view argument may not be used with V2 Blueprints' if view && blueprint.is_a?(V2)

fields[name.to_sym] = Association.new(
fields[name.to_sym] = Collection.new(
name: name,
blueprint: blueprint,
collection: true,
legacy_view: view,
from: from,
value_proc: definition,
Expand Down
184 changes: 184 additions & 0 deletions lib/blueprinter/v2/extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# frozen_string_literal: true

module Blueprinter
module V2
#
# Base class for extensions.
#
# Hook call order:
# - collection? (skipped with render_object/render_collection)
# - input_object | input_collection
# - each_input
# - field_value
# - formatters
# - exclude_field?
# - object_value
# - exclude_object?
# - collection_value
# - exclude_collection?
# - each_output
# - output_object | output_collection
#
class Extension
class << self
attr_accessor :formatters
end

def self.inherited(ext)
ext.formatters = {}
end

#
# Add a formatter for instances of the given class.
#
# Example:
# class MyExtension < Blueprinter::V2::Extension
# format(Time) { |context| context.value.iso8601 }
# format Date, :date_str
#
# def date_str(context)
# context.value.iso8601
# end
# end
#
# @param klass [Class] The class of objects to format
# @param formatter_method [Symbol] Name of a public instance method to call for formatting
# @yield Do formatting in the block instead
#
def self.format(klass, formatter_method = nil, &formatter_block)
formatters[klass] = formatter_method || formatter_block
end

#
# Returns true if the given object should be considered a collection.
#
# @param [Object]
# @return [Boolean]
#
def collection?(_object)
false
end

#
# Modify or replace the object passed to render/render_object.
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def input_object(context)
context.object
end


#
# Modify or replace the collection passed to render/render_collection.
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def input_collection(context)
context.object
end

#
# Modify or replace the object result before final render (e.g. to JSON).
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def output_object(context)
context.value
end

#
# Modify or replace the collection result before final render (e.g. to JSON).
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def output_collection(context)
context.value
end

#
# Modify or replace the object that's passed into each Blueprint.
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def each_input(context)
context.object
end

#
# Modify or replace the output from each Blueprint.
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def each_output(context)
context.value
end

#
# Modify or replace the value used for the field.
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def field_value(context)
context.value
end

#
# Modify or replace the value used for the object.
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def object_value(context)
context.value
end

#
# Modify or replace the value used for the collection.
#
# @param context [Blueprinter::V2::Serializer::Context]
# @return [Object]
#
def collection_value(context)
context.value
end

#
# Return true to exclude this field from the result.
#
# @param _context [Blueprinter::V2::Serializer::Context]
# @return [Boolean]
#
def exclude_field?(_context)
false
end

#
# Return true to exclude this object from the result.
#
# @param _context [Blueprinter::V2::Serializer::Context]
# @return [Boolean]
#
def exclude_object?(_context)
false
end

#
# Return true to exclude this collection from the result.
#
# @param _context [Blueprinter::V2::Serializer::Context]
# @return [Boolean]
#
def exclude_collection?(_context)
false
end
end
end
end
17 changes: 17 additions & 0 deletions lib/blueprinter/v2/extensions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Blueprinter
module V2
module Extensions
autoload :DefaultValues, 'blueprinter/v2/extensions/default_values'
autoload :ExcludeIfEmpty, 'blueprinter/v2/extensions/exclude_if_empty'
autoload :ExcludeIfNil, 'blueprinter/v2/extensions/exclude_if_nil'
autoload :Extraction, 'blueprinter/v2/extensions/extraction'
autoload :FieldSorter, 'blueprinter/v2/extensions/field_sorter'
autoload :IfConditionals, 'blueprinter/v2/extensions/if_conditionals'
autoload :NestedBlueprints, 'blueprinter/v2/extensions/nested_blueprints'
autoload :RootElement, 'blueprinter/v2/extensions/root_element'
autoload :UnlessConditionals, 'blueprinter/v2/extensions/unless_conditionals'
end
end
end
Loading

0 comments on commit 0b5e538

Please sign in to comment.