Skip to content

Commit

Permalink
Merge branch 'main' into bdl-codeql
Browse files Browse the repository at this point in the history
  • Loading branch information
bdLinick authored Jan 18, 2024
2 parents d50054e + a82f204 commit fcefbf7
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.0.0 - 2024/01/17
* 🚀 [BREAKING] Allow transformers to be included across views. See [README](https://github.com/procore-oss/blueprinter#transform-across-views), PR [#372](https://github.com/procore-oss/blueprinter/pull/372) and issue [#225](https://github.com/procore-oss/blueprinter/issues/225) for details. Note this changes the behavior of transformers which were previously only applied to the view they were defined on. Thanks to [@njbbaer](https://github.com/njbbaer) and [@bhooshiek-narendiran](https://github.com/bhooshiek-narendiran).
* 🚀 [FEATURE] Introduce extension API, with initial support for pre_render hook. See [#358](https://github.com/procore-oss/blueprinter/pull/358) for details. Thanks to [@jhollinger](https://github.com/jhollinger).
* 💅 [ENHANCEMENT] Add reflection on views, fields, and associations. See PR [#357](https://github.com/procore-oss/blueprinter/pull/357), and issue [#341](https://github.com/procore-oss/blueprinter/issues/341) for details. Thanks to [@jhollinger](https://github.com/jhollinger).

## 0.30.0 - 2023/09/16
* 🚀 [FEATURE] Allow configuring custom array-like classes to be treated as collections when serializing. More details can be found [here](https://github.com/procore-oss/blueprinter/pull/327). Thanks to [@toddnestor](https://github.com/toddnestor).
* 💅 [ENHANCEMENT] Reduce object allocations in fields calculations to save some memory. More details can be found [here](https://github.com/procore-oss/blueprinter/pull/327). Thanks to [@nametoolong](https://github.com/nametoolong).
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ processing and transforming of resulting view field hashes prior to serializatio

Use `transform` to specify one transformer to be included for serialization.
A transformer is a class, extending `Blueprinter::Transformer` and implementing the `transform` method.
Whatever is returned from this `transform` method will end up being the resulting hash passed to serialization.
The modified `hash` object will be the resulting hash passed to serialization.

#### Example

Expand Down
1 change: 1 addition & 0 deletions lib/blueprinter.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require_relative 'blueprinter/base'
require_relative 'blueprinter/extension'

module Blueprinter
end
1 change: 1 addition & 0 deletions lib/blueprinter/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,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)
data = prepare_data(object, view_name, local_options)
prepend_root_and_meta(data, root, meta)
end
Expand Down
10 changes: 10 additions & 0 deletions lib/blueprinter/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require_relative 'extensions'

module Blueprinter
class Configuration
attr_accessor :association_default, :datetime_format, :deprecations, :field_default, :generator, :if, :method,
Expand All @@ -22,6 +24,14 @@ def initialize
@custom_array_like_classes = []
end

def extensions
@extensions ||= Extensions.new
end

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

def array_like_classes
@array_like_classes ||= [
Array,
Expand Down
22 changes: 22 additions & 0 deletions lib/blueprinter/extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

module Blueprinter
#
# Base class for all extensions. All extension methods are implemented as no-ops.
#
class Extension
#
# Called eary during "render", 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
# @param _blueprint [Class] The Blueprinter class
# @param _view [Symbol] The blueprint view
# @param _options [Hash] Options passed to "render"
# @return [Object] The object to continue rendering
#
def pre_render(object, _blueprint, _view, _options)
object
end
end
end
37 changes: 37 additions & 0 deletions lib/blueprinter/extensions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

module Blueprinter
#
# Stores and runs Blueprinter extensions. An extension is any object that implements one or more of the
# extension methods:
#
# The Render Extension intercepts an object before rendering begins. The return value from this
# method is what is ultimately rendered.
#
# def pre_render(object, blueprint, view, options)
# # returns original, modified, or new object
# end
#
class Extensions
def initialize(extensions = [])
@extensions = extensions
end

def to_a
@extensions.dup
end

# Appends an extension
def <<(ext)
@extensions << ext
self
end

# Runs the object through all Render Extensions and returns the final result
def pre_render(object, blueprint, view, options = {})
@extensions.reduce(object) do |acc, ext|
ext.pre_render(acc, blueprint, view, options)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/blueprinter/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Blueprinter
VERSION = '0.30.0'
VERSION = '1.0.0'
end
126 changes: 126 additions & 0 deletions spec/units/extensions_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# frozen_string_literal: true

require 'json'
require 'ostruct'

describe Blueprinter::Extensions do
let(:all_extensions) {
[
foo_extension.new,
bar_extension.new,
zar_extension.new,
]
}

let(:foo_extension) {
Class.new(Blueprinter::Extension) do
def pre_render(object, _blueprint, _view, _options)
obj = object.dup
obj.foo = "Foo"
obj
end
end
}

let(:bar_extension) {
Class.new(Blueprinter::Extension) do
def pre_render(object, _blueprint, _view, _options)
obj = object.dup
obj.bar = "Bar"
obj
end
end
}

let(:zar_extension) {
Class.new(Blueprinter::Extension) do
def self.something_else(object, _blueprint, _view, _options)
object
end
end
}

it 'should append extensions' do
extensions = Blueprinter::Extensions.new
extensions << foo_extension.new
extensions << bar_extension.new
extensions << zar_extension.new
expect(extensions.to_a.map(&:class)).to eq [
foo_extension,
bar_extension,
zar_extension,
]
end

it "should initialize with extensions, removing any that don't have recognized extension methods" do
extensions = Blueprinter::Extensions.new(all_extensions)
expect(extensions.to_a.map(&:class)).to eq [
foo_extension,
bar_extension,
zar_extension,
]
end

context '#pre_render' do
before :each do
Blueprinter.configure do |config|
config.extensions = all_extensions
end
end

after :each do
Blueprinter.configure do |config|
config.extensions = []
end
end

let(:test_blueprint) {
Class.new(Blueprinter::Base) do
field :id
field :name
field :foo

view :with_bar do
field :bar
end
end
}

it 'should run all pre_render extensions' do
extensions = Blueprinter::Extensions.new(all_extensions)
obj = OpenStruct.new(id: 42, name: 'Jack')
obj = extensions.pre_render(obj, test_blueprint, :default, {})
expect(obj.id).to be 42
expect(obj.name).to eq 'Jack'
expect(obj.foo).to eq 'Foo'
expect(obj.bar).to eq 'Bar'
end

it 'should run with Blueprinter.render using default view' do
obj = OpenStruct.new(id: 42, name: 'Jack')
res = JSON.parse(test_blueprint.render(obj))
expect(res['id']).to be 42
expect(res['name']).to eq 'Jack'
expect(res['foo']).to eq 'Foo'
expect(res['bar']).to be_nil
end

it 'should run with Blueprinter.render using with_bar view' do
obj = OpenStruct.new(id: 42, name: 'Jack')
res = JSON.parse(test_blueprint.render(obj, view: :with_bar))
expect(res['id']).to be 42
expect(res['name']).to eq 'Jack'
expect(res['foo']).to eq 'Foo'
expect(res['bar']).to eq 'Bar'
end

it 'should run with Blueprinter.render_as_hash' do
obj = OpenStruct.new(id: 42, name: 'Jack')
res = test_blueprint.render_as_hash(obj, view: :with_bar)
expect(res[:id]).to be 42
expect(res[:name]).to eq 'Jack'
expect(res[:foo]).to eq 'Foo'
expect(res[:bar]).to eq 'Bar'
end
end
end

0 comments on commit fcefbf7

Please sign in to comment.