blueprinter-activerecord 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 65c4c6d27e0bea2ed2d73f35949587cf6f2197cd540fe2a1ceef1a49d2d135d8
4
+ data.tar.gz: 850d61584f87c743478d496ce1117cdd2bda7b6db7d92a582cb875f7d63c4f70
5
+ SHA512:
6
+ metadata.gz: 40c750e1e67710a3e695a495be6702393f6f72f145ebf47cc21d78587c34cb14ae899edb4cb76a31c599bdb075b5f93c490ff7eb7f5ddc9d16bf15e22314f544
7
+ data.tar.gz: 531d150f3cc8aa474d8c149ae35663395cf0893e406953cf0174cd06fc77ff92f8c7c1cd4ccbaa63a054b7ba930fcb92efd4bf501c6323e1f6033d8a40787053
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueprinterActiveRecord
4
+ #
5
+ # A Blueprinter extension to log what preloads were found and added by the BlueprinterActiveRecord::Preloader extension.
6
+ #
7
+ # This extension may safely be used alongside the BlueprinterActiveRecord::MissingPreloadsLogger extension. Each query will
8
+ # only be processed by one.
9
+ #
10
+ # NOTE Only queries that pass through a Blueprint's "render" method will be found.
11
+ #
12
+ # Blueprinter.configure do |config|
13
+ # # The Preloader extension MUST be added first!
14
+ # config.extensions << BlueprinterActiveRecord::Preloader.new
15
+ #
16
+ # config.extensions << BlueprinterActiveRecord::AddedPreloadsLogger.new do |info|
17
+ # next unless info.found.any?
18
+ #
19
+ # Rails.logger.info({
20
+ # event: "added_preloads",
21
+ # root_model: info.query.model.name,
22
+ # sql: info.query.to_sql,
23
+ # added: info.found.map { |x| x.join " > " },
24
+ # percent_added: info.percent_found,
25
+ # trace: info.trace,
26
+ # }.to_json)
27
+ # end
28
+ # end
29
+ #
30
+ class AddedPreloadsLogger < Blueprinter::Extension
31
+ include Helpers
32
+
33
+ #
34
+ # Initialize and configure the extension.
35
+ #
36
+ # @yield [BlueprinterActiveRecord::PreloadInfo] Your logging action
37
+ #
38
+ def initialize(&log_proc)
39
+ @log_proc = log_proc
40
+ end
41
+
42
+ def pre_render(object, blueprint, view, options)
43
+ if object.is_a?(ActiveRecord::Relation) && object.before_preload_blueprint
44
+ from_code = object.before_preload_blueprint
45
+ from_blueprint = Preloader.preloads(blueprint, view, object.model)
46
+ info = PreloadInfo.new(object, from_code, from_blueprint, caller)
47
+ @log_proc&.call(info)
48
+ end
49
+ object
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueprinterActiveRecord
4
+ module Helpers
5
+ extend self
6
+
7
+ #
8
+ # Combines all types of preloads (preload, includes, eager_load) into a single nested hash
9
+ #
10
+ # @param q [ActiveRecord::Relation]
11
+ # @return [Hash] Symbol keys with Hash values of arbitrary depth
12
+ #
13
+ def extract_preloads(q)
14
+ merge_values [*q.values[:preload], *q.values[:includes], *q.values[:eager_load]]
15
+ end
16
+
17
+ #
18
+ # Count the number of preloads in a nested Hash.
19
+ #
20
+ # @param preloads [Hash] Nested Hash of preloads
21
+ # @return [Integer] The number of associations in the Hash
22
+ #
23
+ def count_preloads(preloads)
24
+ preloads.reduce(0) { |acc, (_key, val)|
25
+ acc + 1 + count_preloads(val)
26
+ }
27
+ end
28
+
29
+ #
30
+ # Finds preloads from 'after' that are missing in 'before'.
31
+ #
32
+ # @param before [Hash] The extracted preloads from before Preloader ran
33
+ # @param after [Hash] The extracted preloads from after Preloader ran
34
+ # @param diff [Array<BlueprinterActiveRecord::MissingPreload>] internal use
35
+ # @param path [Array<Symbol>] internal use
36
+ # @return [Array<Array<Symbol>>] the preloads missing from 'before' . They're in a "path" structure, with the last element of each sub-array being the missing preload, e.g. `[[:widget], [:project, :company]]`
37
+ #
38
+ def diff_preloads(before, after, diff = [], path = [])
39
+ after.each_with_object(diff) do |(key, after_val), obj|
40
+ sub_path = path + [key]
41
+ before_val = before[key]
42
+ obj << sub_path if before_val.nil?
43
+ diff_preloads(before_val || {}, after_val, diff, sub_path)
44
+ end
45
+ end
46
+
47
+ #
48
+ # Merges 'values', which may be any nested structure of arrays, hashes, strings, and symbols into a nested hash.
49
+ #
50
+ # @param value [Array|Hash|String|Symbol]
51
+ # @param result [Hash]
52
+ # @return [Hash] Symbol keys with Hash values of arbitrary depth
53
+ #
54
+ def merge_values(value, result = {})
55
+ case value
56
+ when Array
57
+ value.each { |val| merge_values(val, result) }
58
+ when Hash
59
+ value.each { |key, val|
60
+ key = key.to_sym
61
+ result[key] ||= {}
62
+ merge_values(val, result[key])
63
+ }
64
+ when Symbol
65
+ result[value] ||= {}
66
+ when String
67
+ result[value.to_sym] ||= {}
68
+ else
69
+ raise ArgumentError, "Unexpected value of type '#{value.class.name}' (#{value.inspect})"
70
+ end
71
+ result
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueprinterActiveRecord
4
+ #
5
+ # A Blueprinter extension to log what COULD have been preloaded with the BlueprinterActiveRecord::Preloader extension.
6
+ #
7
+ # This extension may safely be used alongside the BlueprinterActiveRecord::Preloader and BlueprinterActiveRecord::AddedPreloadsLogger
8
+ # extensions. Any queries processed by those extensions will be ignored by this one.
9
+ #
10
+ # NOTE Only queries that pass through a Blueprint's "render" method will be found.
11
+ #
12
+ # Blueprinter.configure do |config|
13
+ # config.extensions << BlueprinterActiveRecord::MissingPreloadsLogger.new do |info|
14
+ # next unless info.found.any?
15
+ #
16
+ # Rails.logger.info({
17
+ # event: "missing_preloads",
18
+ # root_model: info.query.model.name,
19
+ # sql: info.query.to_sql,
20
+ # missing: info.found.map { |x| x.join " > " },
21
+ # percent_missing: info.percent_found,
22
+ # trace: info.trace,
23
+ # }.to_json)
24
+ # end
25
+ # end
26
+ #
27
+ class MissingPreloadsLogger < Blueprinter::Extension
28
+ include Helpers
29
+
30
+ #
31
+ # Initialize and configure the extension.
32
+ #
33
+ # @yield [BlueprinterActiveRecord::PreloadInfo] Your logging action
34
+ #
35
+ def initialize(&log_proc)
36
+ @log_proc = log_proc
37
+ end
38
+
39
+ def pre_render(object, blueprint, view, options)
40
+ if object.is_a?(ActiveRecord::Relation) && !object.before_preload_blueprint
41
+ from_code = extract_preloads object
42
+ from_blueprint = Preloader.preloads(blueprint, view, object.model)
43
+ info = PreloadInfo.new(object, from_code, from_blueprint, caller)
44
+ @log_proc&.call(info)
45
+ end
46
+ object
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueprinterActiveRecord
4
+ #
5
+ # Info about preloads from a query that was run through a Blueprinter's render method.
6
+ #
7
+ # Used for logging by the BlueprinterActiveRecord::MissingPreloadsLogger and BlueprinterActiveRecord::AddedPreloadsLogger extensions.
8
+ #
9
+ class PreloadInfo
10
+ include Helpers
11
+
12
+ # @return [ActiveRecord::Relation] The base query
13
+ attr_reader :query
14
+
15
+ # @return [Array<String>] Stack trace to the query
16
+ attr_reader :trace
17
+
18
+ #
19
+ # @param query [ActiveRecord::Relation] The query passed to "render"
20
+ # @param from_code [Hash] Nested Hash of preloads, includes, and eager_loads that were present in query when passed to "render"
21
+ # @param from_blueprint [Hash] Nested Hash of associations pulled from the Blueprint view
22
+ # @param trace [Array<String>] Stack trace to query
23
+ #
24
+ def initialize(query, from_code, from_blueprint, trace)
25
+ @query = query
26
+ @from_code = from_code
27
+ @from_blueprint = from_blueprint
28
+ @trace = trace
29
+ end
30
+
31
+ # @return [Integer] The percent of total preloads found by BlueprinterActiveRecord
32
+ def percent_found
33
+ total = num_existing + found.size
34
+ ((found.size / num_existing.to_f) * 100).round
35
+ end
36
+
37
+ # @return [Integer] The number of preloads, includes, and eager_loads that existed before BlueprinterActiveRecord was involved
38
+ def num_existing
39
+ @num_existing ||= count_preloads(hash)
40
+ end
41
+
42
+ # @return [Array<Array<Symbol>>] Array of "preload paths" (e.g. [[:project, :company]]) to missing preloads that could have been found & added by BlueprinterActiveRecord::Preloader
43
+ def found
44
+ @found ||= diff_preloads(@from_code, hash)
45
+ end
46
+
47
+ # @return [Array<Array<Symbol>>] Array of "preload paths" (e.g. [[:project, :company]]) from the blueprint that were visible to the preloader
48
+ def visible
49
+ @visible ||= diff_preloads({}, @from_blueprint)
50
+ end
51
+
52
+ # @return [Hash] Nested hash of all preloads, both manually added and auto found
53
+ def hash
54
+ @hash ||= merge_values [@from_code, @from_blueprint]
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueprinterActiveRecord
4
+ # A Blueprinter extension to automatically preload a Blueprint view's ActiveRecord associations during render
5
+ class Preloader < Blueprinter::Extension
6
+ include Helpers
7
+
8
+ attr_reader :use, :auto, :auto_proc
9
+
10
+ #
11
+ # Initialize and configure the extension.
12
+ #
13
+ # @param auto [true|false] When true, preload for EVERY ActiveRecord::Relation passed to a Blueprint
14
+ # @param use [:preload|:includes] When `auto` is true, use this method (e.g. :preload) for preloading
15
+ # @yield [Object, Class, Symbol, Hash] Instead of passing `auto` as a boolean, you may define a block that accepts the object to render, the blueprint class, the view, and options. If the block returns true, auto preloading will take place.
16
+ #
17
+ def initialize(auto: false, use: :preload, &auto_proc)
18
+ @auto = auto
19
+ @auto_proc = auto_proc
20
+ @use =
21
+ case use
22
+ when :preload, :includes
23
+ use
24
+ else
25
+ raise ArgumentError, "Unknown value '#{use.inspect}' for `BlueprinterActiveRecord::Preloader` argument 'use'. Valid values are :preload, :includes."
26
+ end
27
+ end
28
+
29
+ #
30
+ # Implements the "pre_render" Blueprinter Extension to preload associations from a view.
31
+ # If auto is true, all ActiveRecord::Relation objects will be preloaded. If auto is false,
32
+ # only queries that have called `.preload_blueprint` will be preloaded.
33
+ #
34
+ # NOTE: If auto is on, *don't* be concerned that you'll end up with duplicate preloads. Even if
35
+ # the query ends up with overlapping members in 'preload' and 'includes', ActiveRecord
36
+ # intelligently handles them. There are several unit tests which confirm this behavior.
37
+ #
38
+ def pre_render(object, blueprint, view, options)
39
+ case object
40
+ when ActiveRecord::Relation
41
+ if object.preload_blueprint_method || auto || auto_proc&.call(object, blueprint, view, options) == true
42
+ object.before_preload_blueprint = extract_preloads object
43
+ blueprint_preloads = self.class.preloads(blueprint, view, object.model)
44
+ loader = object.preload_blueprint_method || use
45
+ object.public_send(loader, blueprint_preloads)
46
+ else
47
+ object
48
+ end
49
+ else
50
+ object
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ #
57
+ # Returns an ActiveRecord preload plan extracted from the Blueprint and view (recursive).
58
+ #
59
+ # Example:
60
+ #
61
+ # preloads = BlueprinterActiveRecord::Preloader.preloads(WidgetBlueprint, :extended, Widget)
62
+ # q = Widget.where(...).order(...).preload(preloads)
63
+ #
64
+ # @param blueprint [Class] The Blueprint class
65
+ # @param view_name [Symbol] Name of the view in blueprint
66
+ # @param model [Class] The ActiveRecord model class that blueprint represents
67
+ # @return [Hash] A Hash containing preload/eager_load/etc info for ActiveRecord
68
+ #
69
+ def self.preloads(blueprint, view_name, model=nil)
70
+ view = blueprint.reflections.fetch(view_name)
71
+ view.associations.each_with_object({}) { |(_name, assoc), acc|
72
+ ref = model ? model.reflections[assoc.name.to_s] : nil
73
+ if (ref || model.nil?) && !assoc.blueprint.is_a?(Proc)
74
+ ref_model = ref && !(ref.belongs_to? && ref.polymorphic?) ? ref.klass : nil
75
+ acc[assoc.name] = preloads(assoc.blueprint, assoc.view, ref_model)
76
+ end
77
+ }
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueprinterActiveRecord
4
+ module QueryMethods
5
+ module Delegates
6
+ def preload_blueprint(blueprint = nil, view = :default, use: :preload)
7
+ all.preload_blueprint(blueprint, view, use: use)
8
+ end
9
+ end
10
+
11
+ ACTIONS = %i(preload eager_load includes).freeze
12
+
13
+ #
14
+ # Automatically preload (or `eager_load` or `includes`) the associations in the given
15
+ # blueprint and view (recursively).
16
+ #
17
+ # You can have the Blueprint and view autodetected on render:
18
+ #
19
+ # q = Widget.where(...).preload_blueprint
20
+ # WidgetBlueprint.render(q, view: :extended)
21
+ #
22
+ # Or you can pass them up front:
23
+ #
24
+ # widgets = Widget.where(...).preload_blueprint(WidgetBlueprint, :extended).to_a
25
+ # # do something with widgets, then render
26
+ # WidgetBlueprint.render(widgets, view: :extended)
27
+ #
28
+ # @param blueprint [Class] The Blueprinter class to use (ignore to autodetect on render)
29
+ # @param view [Symbol] The Blueprinter view name to use (ignore to autodetect on render)
30
+ # @param use [Symbol] The eager loading strategy to use (:preload, :includes, :eager_load)
31
+ # @return [ActiveRecord::Relation]
32
+ #
33
+ def preload_blueprint(blueprint = nil, view = :default, use: :preload)
34
+ spawn.preload_blueprint!(blueprint, view, use: use)
35
+ end
36
+
37
+ # See preload_blueprint
38
+ def preload_blueprint!(blueprint = nil, view = :default, use: :preload)
39
+ unless ACTIONS.include? use
40
+ valid = ACTIONS.map(&:inspect).join(", ")
41
+ raise BlueprinterError, "Unknown `preload_blueprint` method :#{use}. Valid methods are #{valid}."
42
+ end
43
+
44
+ if blueprint and view
45
+ # preload right now
46
+ preloads = Preloader.preloads(blueprint, view, model)
47
+ public_send(use, preloads)
48
+ else
49
+ # preload during render
50
+ @values[:preload_blueprint_method] = use
51
+ self
52
+ end
53
+ end
54
+
55
+ def preload_blueprint_method
56
+ @values[:preload_blueprint_method]
57
+ end
58
+
59
+ # Get the preloads present before the Preloader extension ran (internal, for PreloadLogger)
60
+ def before_preload_blueprint
61
+ @values[:before_preload_blueprint]
62
+ end
63
+
64
+ # Set the preloads present before the Preloader extension ran (internal, for PreloadLogger)
65
+ def before_preload_blueprint=(val)
66
+ @values[:before_preload_blueprint] = val
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,7 @@
1
+ module BlueprinterActiveRecord
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ Dir[File.join(File.dirname(__FILE__), "..", "tasks/*.rake")].each { |f| load f }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueprinterActiveRecord
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'blueprinter'
4
+ require 'active_record'
5
+
6
+ module BlueprinterActiveRecord
7
+ autoload :QueryMethods, 'blueprinter-activerecord/query_methods'
8
+ autoload :Preloader, 'blueprinter-activerecord/preloader'
9
+ autoload :AddedPreloadsLogger, 'blueprinter-activerecord/added_preloads_logger'
10
+ autoload :MissingPreloadsLogger, 'blueprinter-activerecord/missing_preloads_logger'
11
+ autoload :PreloadInfo, 'blueprinter-activerecord/preload_info'
12
+ autoload :Helpers, 'blueprinter-activerecord/helpers'
13
+ autoload :Version, 'blueprinter-activerecord/version'
14
+ end
15
+
16
+ ActiveRecord::Relation.send(:include, BlueprinterActiveRecord::QueryMethods)
17
+ ActiveRecord::Base.extend(BlueprinterActiveRecord::QueryMethods::Delegates)
18
+
19
+ require 'blueprinter-activerecord/railtie' if defined? Rails::Railtie
@@ -0,0 +1,21 @@
1
+ namespace :blueprinter do
2
+ namespace :activerecord do
3
+ desc "Prints the preload plan"
4
+ task :preloads, [:blueprint, :view, :model] => :environment do |_, args|
5
+ def pretty(hash, indent = 0)
6
+ s = " " * indent
7
+ buff = "{"
8
+ hash.each { |key, val|
9
+ buff << "\n#{s} :#{key} => #{pretty val, indent + 2},"
10
+ }
11
+ buff << "\n#{s}" if hash.any?
12
+ buff << "}"
13
+ end
14
+
15
+ model = args[:model].constantize
16
+ blueprint = args[:blueprint].constantize
17
+ preloads = BlueprinterActiveRecord::Preloader.preloads(blueprint, args[:view].to_sym, model)
18
+ puts pretty preloads
19
+ end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blueprinter-activerecord
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Procore Technologies, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7.2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '6.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7.2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: blueprinter
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: appraisal
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.5'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.5'
61
+ - !ruby/object:Gem::Dependency
62
+ name: database_cleaner
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: minitest
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '5.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '5.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '13.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: sqlite3
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.4'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.4'
117
+ description: Eager loading and other ActiveRecord helpers for Blueprinter
118
+ email:
119
+ - opensource@procore.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - lib/blueprinter-activerecord.rb
125
+ - lib/blueprinter-activerecord/added_preloads_logger.rb
126
+ - lib/blueprinter-activerecord/helpers.rb
127
+ - lib/blueprinter-activerecord/missing_preloads_logger.rb
128
+ - lib/blueprinter-activerecord/preload_info.rb
129
+ - lib/blueprinter-activerecord/preloader.rb
130
+ - lib/blueprinter-activerecord/query_methods.rb
131
+ - lib/blueprinter-activerecord/railtie.rb
132
+ - lib/blueprinter-activerecord/version.rb
133
+ - lib/tasks/blueprinter_activerecord.rake
134
+ homepage: https://github.com/procore-oss/blueprinter-activerecord
135
+ licenses:
136
+ - MIT
137
+ metadata:
138
+ homepage_uri: https://github.com/procore-oss/blueprinter-activerecord
139
+ source_code_uri: https://github.com/procore-oss/blueprinter-activerecord
140
+ changelog_uri: https://github.com/procore-oss/blueprinter-activerecord/CHANGELOG.md
141
+ allowed_push_host: https://rubygems.org
142
+ rubygems_mfa_required: 'true'
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '2.7'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubygems_version: 3.4.19
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: Extensions for using ActiveRecord with ActiveRecord
162
+ test_files: []