blueprinter 0.30.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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