Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental V2 renderer #479

Draft
wants to merge 16 commits into
base: release-2.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ Rake::TestTask.new(:benchmarks) do |t|
t.verbose = false
end

Rake::TestTask.new(:speedtest) do |t|
t.libs.append('lib', 'spec')
t.pattern = 'spec/benchmarks/speedtest.rb'
t.verbose = false
end

task default: %i[spec rubocop]
1 change: 1 addition & 0 deletions lib/blueprinter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Blueprinter
autoload :Configuration, 'blueprinter/configuration'
autoload :Errors, 'blueprinter/errors'
autoload :Extension, 'blueprinter/extension'
autoload :Hooks, 'blueprinter/hooks'
autoload :Transformer, 'blueprinter/transformer'
autoload :V2, 'blueprinter/v2'

Expand Down
2 changes: 1 addition & 1 deletion lib/blueprinter/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ def self.render_as_json(object, options = {})
def self.prepare(object, view_name:, local_options:, root: nil, meta: nil)
raise BlueprinterError, "View '#{view_name}' is not defined" unless view_collection.view? view_name

object = Blueprinter.configuration.extensions.pre_render(object, self, view_name, local_options)
object = Blueprinter.configuration.hooks.reduce(:pre_render, object) { |val| [val, self, view_name, local_options] }
data = prepare_data(object, view_name, local_options)
prepend_root_and_meta(data, root, meta)
end
Expand Down
10 changes: 7 additions & 3 deletions lib/blueprinter/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

require 'json'
require 'blueprinter/extensions'
require 'blueprinter/hooks'
require 'blueprinter/extractors/auto_extractor'

module Blueprinter
Expand All @@ -27,11 +27,15 @@ def initialize
end

def extensions
@extensions ||= Extensions.new
@extensions ||= []
end

def extensions=(list)
@extensions = Extensions.new(list)
@extensions = list
end

def hooks
@hooks ||= Blueprinter::Hooks.new(extensions)
end

def array_like_classes
Expand Down
192 changes: 190 additions & 2 deletions lib/blueprinter/extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,199 @@

module Blueprinter
#
# Base class for all extensions. All extension methods are implemented as no-ops.
# Base class for all extensions.
#
# V2 hook call order:
# - collection? (skipped if calling render_object/render_collection)
# - around
# - input_object | input_collection
# - around_blueprint
# - prepare (only first time during a given render)
# - blueprint_fields (only first time during a given render)
# - blueprint_input
# - field_value
# - exclude_field?
# - object_value
# - exclude_object?
# - collection_value
# - exclude_collection?
# - blueprint_output
# - output_object | output_collection
#
# V1 hook call order:
# - pre_render
#
class Extension
#
# Called eary during "render", this method receives the object to be rendered and
# Returns true if the given object should be treated as a collection (i.e. supports `map { |obj| ... }`).
#
# @param _object [Object]
# @return [Boolean]
#
def collection?(_object)
false
end

#
# Runs around the entire rendering process. MUST yield!
#
# @param _context [Blueprinter::V2::Context]
#
def around(_context)
yield
end

#
# Runs around any Blueprint serialization. Surrounds the `prepare` through `blueprint_output` hooks. MUST yield!
#
# @param _context [Blueprinter::V2::Context]
#
def around_blueprint(_context)
yield
end

#
# Called once per blueprint per render. A common use is to pre-calculate certain options
# and cache them in context.data, so we don't have to recalculate them for every field.
#
# @param _context [Blueprinter::V2::Context]
#
def prepare(_context); end

#
# Returns the fields that should be included in the correct order. Default is all fields in the order in which they were defined.
#
# NOTE Only runs once per Blueprint per render.
#
# @param _context [Blueprinter::V2::Context]
# @return [Array<Blueprinter::V2::Field|Blueprinter::V2::Object|Blueprinter::V2::Collection>]
#
def blueprint_fields(_context)
[]
end

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

#
# Modify or replace the collection passed to render/render_collection.
#
# @param context [Blueprinter::V2::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::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::Context]
# @return [Object]
#
def output_collection(context)
context.value
end

#
# Modify or replace an object right before it's serialized by a Blueprint.
#
# @param context [Blueprinter::V2::Context]
# @return [Object]
#
def blueprint_input(context)
context.object
end

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

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

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

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

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

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

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

#
# Called eary during "render" in V1, this method receives the object to be rendered and
# may return a modified (or new) object to be rendered.
#
# @param object [Object] The object to be rendered
Expand Down
37 changes: 0 additions & 37 deletions lib/blueprinter/extensions.rb

This file was deleted.

Loading