blueprinter 0.30.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7ffa1c885dee2ca30ed3ba3a2e4b86fb19c9a89b8613a9f2d77df76d233960b
4
- data.tar.gz: 23113e2f9faa11e8b5853d73f9843fa65054643f7f9446040c8d1bd970c3042f
3
+ metadata.gz: 0daefad3d40a1c147e4dff6ae392a0b36f344fac7b79b6a500bc39fbe50885bf
4
+ data.tar.gz: 41d4753399f53d94030ef650b7433399030ae282fe5e85124cee83f86b7558f3
5
5
  SHA512:
6
- metadata.gz: 04ac9dda1f68a1d87ce363e10aca4e80ee7767b4e81034bb427889b70b595c4aa289764a7287a60d395c5da0d2bd97f4f0d6e69275d9f14a6897d93e7258c3a5
7
- data.tar.gz: b0a5882b5cb3e701e02ccffb984d10f3a982d8e619ab54b3b49639d72e88869eb11f97ffa2b6b2c078cc91272121f6e6d88b85576c338e961f50f418e24cd086
6
+ metadata.gz: ff8384ffb0de03cfa91422313656a6e7ff9375b3335cdab1371e8a60db65a5cd9fbacaac1f32961c86e9bd213678cfdcd5911fc4a183962dbf57df8ff6155e54
7
+ data.tar.gz: f0cf694fd5984a274f8bfb2a664f7b6baa4406ee98a08a5666c2ccbf0e275627f3ed779e9770f3e6d506dc0b84eecb9ae3546846cd59819e93d1ea4676b63233
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.0.0 - 2024/01/17
2
+ * 🚀 [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).
3
+ * 🚀 [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).
4
+ * 💅 [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).
5
+
1
6
  ## 0.30.0 - 2023/09/16
2
7
  * 🚀 [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).
3
8
  * 💅 [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).
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![Test](https://github.com/procore-oss/blueprinter/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/procore-oss/blueprinter/actions/workflows/test.yaml)
2
2
  [![Gem Version](https://badge.fury.io/rb/blueprinter.svg)](https://badge.fury.io/rb/blueprinter)
3
- [![Gitter chat](https://badges.gitter.im/procore/blueprinter.svg)](https://gitter.im/blueprinter-gem/community)
3
+ [![Discord](https://img.shields.io/badge/Chat-EDEDED?logo=discord)](https://discord.gg/PbntEMmWws)
4
4
 
5
5
  <img src="blueprinter_logo.svg" width="25%">
6
6
 
@@ -765,7 +765,7 @@ processing and transforming of resulting view field hashes prior to serializatio
765
765
 
766
766
  Use `transform` to specify one transformer to be included for serialization.
767
767
  A transformer is a class, extending `Blueprinter::Transformer` and implementing the `transform` method.
768
- Whatever is returned from this `transform` method will end up being the resulting hash passed to serialization.
768
+ The modified `hash` object will be the resulting hash passed to serialization.
769
769
 
770
770
  #### Example
771
771
 
@@ -800,6 +800,28 @@ class UserBlueprint < Blueprinter::Base
800
800
  end
801
801
  ```
802
802
 
803
+ #### Transform across views
804
+
805
+ Transformers can be included across views:
806
+
807
+ ```ruby
808
+ class UserBlueprint < Blueprinter::Base
809
+ transform DefaultTransformer
810
+
811
+ view :normal do
812
+ transform ViewTransformer
813
+ end
814
+
815
+ view :extended do
816
+ include_view :normal
817
+ end
818
+ end
819
+ ```
820
+
821
+ Both the `normal` and `extended` views have `DefaultTransformer` and `ViewTransformer` applied.
822
+
823
+ Transformers are executed in a top-down order, so `DefaultTransformer` will be executed first, followed by `ViewTransformer`.
824
+
803
825
  #### Global Transforms
804
826
 
805
827
  You can also specify global default transformers. Create one or more transformer classes extending from `Blueprinter::Transformer` and set the `default_transformers` configuration
@@ -17,10 +17,12 @@ require_relative 'helpers/base_helpers'
17
17
  require_relative 'view'
18
18
  require_relative 'view_collection'
19
19
  require_relative 'transformer'
20
+ require_relative 'reflection'
20
21
 
21
22
  module Blueprinter
22
23
  class Base
23
24
  include BaseHelpers
25
+ extend Reflection
24
26
 
25
27
  # Specify a field or method name used as an identifier. Usually, this is
26
28
  # something like :id
@@ -256,6 +258,7 @@ module Blueprinter
256
258
  def self.prepare(object, view_name:, local_options:, root: nil, meta: nil)
257
259
  raise BlueprinterError, "View '#{view_name}' is not defined" unless view_collection.view? view_name
258
260
 
261
+ object = Blueprinter.configuration.extensions.pre_render(object, self, view_name, local_options)
259
262
  data = prepare_data(object, view_name, local_options)
260
263
  prepend_root_and_meta(data, root, meta)
261
264
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'extensions'
4
+
3
5
  module Blueprinter
4
6
  class Configuration
5
7
  attr_accessor :association_default, :datetime_format, :deprecations, :field_default, :generator, :if, :method,
@@ -22,6 +24,14 @@ module Blueprinter
22
24
  @custom_array_like_classes = []
23
25
  end
24
26
 
27
+ def extensions
28
+ @extensions ||= Extensions.new
29
+ end
30
+
31
+ def extensions=(list)
32
+ @extensions = Extensions.new(list)
33
+ end
34
+
25
35
  def array_like_classes
26
36
  @array_like_classes ||= [
27
37
  Array,
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blueprinter
4
+ #
5
+ # Base class for all extensions. All extension methods are implemented as no-ops.
6
+ #
7
+ class Extension
8
+ #
9
+ # Called eary during "render", this method receives the object to be rendered and
10
+ # may return a modified (or new) object to be rendered.
11
+ #
12
+ # @param object [Object] The object to be rendered
13
+ # @param _blueprint [Class] The Blueprinter class
14
+ # @param _view [Symbol] The blueprint view
15
+ # @param _options [Hash] Options passed to "render"
16
+ # @return [Object] The object to continue rendering
17
+ #
18
+ def pre_render(object, _blueprint, _view, _options)
19
+ object
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blueprinter
4
+ #
5
+ # Stores and runs Blueprinter extensions. An extension is any object that implements one or more of the
6
+ # extension methods:
7
+ #
8
+ # The Render Extension intercepts an object before rendering begins. The return value from this
9
+ # method is what is ultimately rendered.
10
+ #
11
+ # def pre_render(object, blueprint, view, options)
12
+ # # returns original, modified, or new object
13
+ # end
14
+ #
15
+ class Extensions
16
+ def initialize(extensions = [])
17
+ @extensions = extensions
18
+ end
19
+
20
+ def to_a
21
+ @extensions.dup
22
+ end
23
+
24
+ # Appends an extension
25
+ def <<(ext)
26
+ @extensions << ext
27
+ self
28
+ end
29
+
30
+ # Runs the object through all Render Extensions and returns the final result
31
+ def pre_render(object, blueprint, view, options = {})
32
+ @extensions.reduce(object) do |acc, ext|
33
+ ext.pre_render(acc, blueprint, view, options)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blueprinter
4
+ #
5
+ # Public methods for reflecting on a Blueprint.
6
+ #
7
+ module Reflection
8
+ Field = Struct.new(:name, :display_name, :options)
9
+ Association = Struct.new(:name, :display_name, :blueprint, :view, :options)
10
+
11
+ #
12
+ # Returns a Hash of views keyed by name.
13
+ #
14
+ # Example:
15
+ #
16
+ # widget_view = WidgetBlueprint.reflections[:default]
17
+ # category = widget_view.associations[:category]
18
+ # category.blueprint
19
+ # => CategoryBlueprint
20
+ # category.view
21
+ # => :default
22
+ #
23
+ # @return [Hash<Symbol, Blueprinter::Reflection::View>]
24
+ #
25
+ def reflections
26
+ @reflections ||= view_collection.views.transform_values do |view|
27
+ View.new(view.name, view_collection)
28
+ end
29
+ end
30
+
31
+ #
32
+ # Represents a view within a Blueprint.
33
+ #
34
+ class View
35
+ attr_reader :name
36
+
37
+ def initialize(name, view_collection)
38
+ @name = name
39
+ @view_collection = view_collection
40
+ end
41
+
42
+ #
43
+ # Returns a Hash of fields in this view (recursive) keyed by method name.
44
+ #
45
+ # @return [Hash<Symbol, Blueprinter::Reflection::Field>]
46
+ #
47
+ def fields
48
+ @fields ||= @view_collection.fields_for(name).each_with_object({}) do |field, obj|
49
+ next if field.options[:association]
50
+
51
+ obj[field.method] = Field.new(field.method, field.name, field.options)
52
+ end
53
+ end
54
+
55
+ #
56
+ # Returns a Hash of associations in this view (recursive) keyed by method name.
57
+ #
58
+ # @return [Hash<Symbol, Blueprinter::Reflection::Association>]
59
+ #
60
+ def associations
61
+ @associations ||= @view_collection.fields_for(name).each_with_object({}) do |field, obj|
62
+ next unless field.options[:association]
63
+
64
+ blueprint = field.options.fetch(:blueprint)
65
+ view = field.options[:view] || :default
66
+ obj[field.method] = Association.new(field.method, field.name, blueprint, view, field.options)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Blueprinter
4
- VERSION = '0.30.0'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -16,10 +16,6 @@ module Blueprinter
16
16
  @sort_by_definition = Blueprinter.configuration.sort_fields_by.eql?(:definition)
17
17
  end
18
18
 
19
- def transformers
20
- view_transformers.empty? ? Blueprinter.configuration.default_transformers : view_transformers
21
- end
22
-
23
19
  def track_definition_order(method, viewable: true)
24
20
  return unless @sort_by_definition
25
21
 
@@ -35,7 +35,9 @@ module Blueprinter
35
35
  end
36
36
 
37
37
  def transformers(view_name)
38
- views[view_name].transformers
38
+ included_transformers = gather_transformers_from_included_views(view_name).reverse
39
+ all_transformers = views[:default].view_transformers.concat(included_transformers).uniq
40
+ all_transformers.empty? ? Blueprinter.configuration.default_transformers : all_transformers
39
41
  end
40
42
 
41
43
  def [](view_name)
@@ -89,5 +91,14 @@ module Blueprinter
89
91
  ordered_fields[definition.name] = fields[definition.name]
90
92
  end
91
93
  end
94
+
95
+ def gather_transformers_from_included_views(view_name)
96
+ current_view = views[view_name]
97
+ current_view.included_view_names.flat_map do |included_view_name|
98
+ next [] if view_name == included_view_name
99
+
100
+ gather_transformers_from_included_views(included_view_name)
101
+ end.concat(current_view.view_transformers)
102
+ end
92
103
  end
93
104
  end
data/lib/blueprinter.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'blueprinter/base'
4
+ require_relative 'blueprinter/extension'
4
5
 
5
6
  module Blueprinter
6
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blueprinter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Procore Technologies, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-20 00:00:00.000000000 Z
11
+ date: 2024-01-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Blueprinter is a JSON Object Presenter for Ruby that takes business objects
14
14
  and breaks them down into simple hashes and serializes them to JSON. It can be used
@@ -30,6 +30,8 @@ files:
30
30
  - lib/blueprinter/configuration.rb
31
31
  - lib/blueprinter/deprecation.rb
32
32
  - lib/blueprinter/empty_types.rb
33
+ - lib/blueprinter/extension.rb
34
+ - lib/blueprinter/extensions.rb
33
35
  - lib/blueprinter/extractor.rb
34
36
  - lib/blueprinter/extractors/association_extractor.rb
35
37
  - lib/blueprinter/extractors/auto_extractor.rb
@@ -40,6 +42,7 @@ files:
40
42
  - lib/blueprinter/formatters/date_time_formatter.rb
41
43
  - lib/blueprinter/helpers/base_helpers.rb
42
44
  - lib/blueprinter/helpers/type_helpers.rb
45
+ - lib/blueprinter/reflection.rb
43
46
  - lib/blueprinter/transformer.rb
44
47
  - lib/blueprinter/version.rb
45
48
  - lib/blueprinter/view.rb