deep_preloader 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: 60cc928760d63a608b81e43c8888d1ba827bd82fe928ee8d3ece46f06306e885
4
+ data.tar.gz: 0dcec8c15e0dd6f51127ba9b8f868091ce775a67fc5585786b446ac673875b50
5
+ SHA512:
6
+ metadata.gz: e96854b66b2aeda406e0f7b6da7d7a61b6de93da257b1aeb4392b7710182cdc861574871336278c743e614f47c20bc15e10251538ea3214c03a89c09c6dc3307
7
+ data.tar.gz: 2aa7588b8d4ab4a2052866ef96b93a46c6f2ac0ed9c6c10aed86088b4b8a13a3d102da7734d240ac0ec33d38ed3bc4872724f44574c0c2fedc8f8b693d95f4e9
@@ -0,0 +1,103 @@
1
+ version: 2.1
2
+
3
+ executors:
4
+ ruby:
5
+ parameters:
6
+ ruby-version:
7
+ type: string
8
+ default: "2.6"
9
+ gemfile:
10
+ type: string
11
+ default: "Gemfile"
12
+ docker:
13
+ - image: circleci/ruby:<< parameters.ruby-version >>
14
+ environment:
15
+ BUNDLE_JOBS: 3
16
+ BUNDLE_RETRY: 3
17
+ BUNDLE_PATH: vendor/bundle
18
+ RAILS_ENV: test
19
+ BUNDLE_GEMFILE: << parameters.gemfile >>
20
+
21
+ jobs:
22
+ test:
23
+ parameters:
24
+ ruby-version:
25
+ type: string
26
+ gemfile:
27
+ type: string
28
+ executor:
29
+ name: ruby
30
+ ruby-version: << parameters.ruby-version >>
31
+ gemfile: << parameters.gemfile >>
32
+ parallelism: 1
33
+ steps:
34
+ - checkout
35
+
36
+ - run:
37
+ # Remove the non-appraisal gemfile for safety: we never want to use it.
38
+ name: Prepare bundler
39
+ command: bundle -v
40
+
41
+ - run:
42
+ name: Compute a gemfile lock
43
+ command: bundle lock && cp "${BUNDLE_GEMFILE}.lock" /tmp/gem-lock
44
+
45
+ - restore_cache:
46
+ keys:
47
+ - deep_preloader-<< parameters.ruby-version >>-{{ checksum "/tmp/gem-lock" }}
48
+ - deep_preloader-
49
+
50
+ - run:
51
+ name: Bundle Install
52
+ command: bundle check || bundle install
53
+
54
+ - save_cache:
55
+ key: deep_preloader-<< parameters.ruby-version >>-{{ checksum "/tmp/gem-lock" }}
56
+ paths:
57
+ - vendor/bundle
58
+
59
+ - run:
60
+ name: Run rspec
61
+ command: bundle exec rspec --profile 10 --format RspecJunitFormatter --out test_results/rspec.xml --format progress
62
+
63
+ - store_test_results:
64
+ path: test_results
65
+
66
+ publish:
67
+ executor: ruby
68
+ steps:
69
+ - checkout
70
+ - run:
71
+ name: Setup Rubygems
72
+ command: |
73
+ mkdir ~/.gem &&
74
+ echo -e "---\r\n:rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials &&
75
+ chmod 0600 ~/.gem/credentials
76
+ - run:
77
+ name: Publish to Rubygems
78
+ command: |
79
+ gem build deep_preloader.gemspec
80
+ gem push deep_preloader-*.gem
81
+
82
+ workflows:
83
+ version: 2.1
84
+ build:
85
+ jobs:
86
+ - test:
87
+ name: 'ruby 2.5 ActiveRecord 5.2'
88
+ ruby-version: "2.5"
89
+ gemfile: gemfiles/activerecord_5_2.gemfile
90
+ - test:
91
+ name: 'ruby 2.6 ActiveRecord 5.2'
92
+ ruby-version: "2.6"
93
+ gemfile: gemfiles/activerecord_5_2.gemfile
94
+ - test:
95
+ name: 'ruby 2.6 ActiveRecord 6.0-beta'
96
+ ruby-version: "2.6"
97
+ gemfile: gemfiles/activerecord_6_0_beta.gemfile
98
+ - publish:
99
+ filters:
100
+ branches:
101
+ only: master
102
+ tags:
103
+ ignore: /.*/
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ gemfiles/*.gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ dist: trusty
2
+ sudo: false
3
+ language: ruby
4
+ rvm:
5
+ - 2.5
6
+ gemfile:
7
+ - gemfiles/activerecord_5_2.gemfile
8
+ notifications:
9
+ email: false
data/Appraisals ADDED
@@ -0,0 +1,7 @@
1
+ appraise "activerecord-5-2" do
2
+ gem "activerecord", "~> 5.2.0"
3
+ end
4
+
5
+ appraise 'activerecord-6-0-beta' do
6
+ gem 'activerecord', '~> 6.0.0.beta'
7
+ end
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in deep_preloader.gemspec
4
+ gemspec
5
+ gem 'rspec_junit_formatter'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 DMM.com
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # DeepPreloader
2
+ [![Build Status](https://travis-ci.org/iknow/deep_preloader.svg?branch=master)](https://travis-ci.org/iknow/deep_preloader)
3
+
4
+ An explicit preloader for ActiveRecord, supporting traversal of polymorphic relationships.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "deep_preloader"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'deep_preloader/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "deep_preloader"
8
+ spec.version = DeepPreloader::VERSION
9
+ spec.authors = ["iKnow Team"]
10
+ spec.email = ["dev@iknow.jp"]
11
+
12
+ spec.summary = %q{Explicit preloader for ActiveRecord}
13
+ spec.homepage = "http://github.com/iknow/deep_preloader"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activerecord", '>= 5.0.0'
22
+
23
+ spec.add_development_dependency "bundler"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "database_cleaner"
27
+ spec.add_development_dependency "minitest"
28
+
29
+ spec.add_development_dependency "byebug"
30
+ spec.add_development_dependency "pry"
31
+ spec.add_development_dependency "method_source"
32
+ spec.add_development_dependency "appraisal"
33
+ spec.add_development_dependency "sqlite3"
34
+ end
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec_junit_formatter"
6
+ gem "activerecord", "~> 5.2.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec_junit_formatter"
6
+ gem "activerecord", "~> 6.0.0.beta"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ class DeepPreloader::AbstractSpec
2
+ def for_type(clazz)
3
+ raise ArgumentError.new("Cannot type dispatch on non-polymorphic preload spec #{self.inspect}")
4
+ end
5
+ end
@@ -0,0 +1,56 @@
1
+ require 'deep_preloader/abstract_spec'
2
+
3
+ class DeepPreloader::PolymorphicSpec < DeepPreloader::AbstractSpec
4
+ attr_reader :specs_by_type
5
+
6
+ def self.parse(data)
7
+ if data.is_a?(Hash)
8
+ specs = data.each_with_object({}) do |(k, v), h|
9
+ h[k.to_s] = DeepPreloader::Spec.parse(v)
10
+ end
11
+ self.new(specs)
12
+ else
13
+ raise ArgumentError.new("Invalid polymorphic spec: '#{data.inspect}' is not a hash")
14
+ end
15
+ end
16
+
17
+ def initialize(specs_by_type = {})
18
+ @specs_by_type = specs_by_type
19
+ end
20
+
21
+ def for_type(clazz)
22
+ specs_by_type[clazz.name]
23
+ end
24
+
25
+ def merge!(other)
26
+ case other
27
+ when nil
28
+ return
29
+ when DeepPreloader::PolymorphicSpec
30
+ other.specs_by_type.each do |k, v|
31
+ if specs_by_type[k]
32
+ specs_by_type[k].merge!(v)
33
+ else
34
+ specs_by_type[k] = v
35
+ end
36
+ end
37
+ else
38
+ raise ArgumentError.new("Cannot merge #{other.class.name} into #{self.inspect}")
39
+ end
40
+ self
41
+ end
42
+
43
+ def hash
44
+ [self.class, self.specs_by_type].hash
45
+ end
46
+
47
+ def ==(other)
48
+ self.class == other.class && self.specs_by_type == other.specs_by_type
49
+ end
50
+
51
+ alias eql? ==
52
+
53
+ def inspect
54
+ "PolySpec#{specs_by_type.inspect}"
55
+ end
56
+ end
@@ -0,0 +1,63 @@
1
+ require 'deep_preloader/abstract_spec'
2
+
3
+ class DeepPreloader::Spec < DeepPreloader::AbstractSpec
4
+ attr_reader :association_specs
5
+
6
+ def self.parse(data)
7
+ case data
8
+ when Array
9
+ data.inject(self.new) do |acc, v|
10
+ acc.merge!(parse(v))
11
+ end
12
+ when Hash
13
+ assoc_specs = data.each_with_object({}) do |(k, v), h|
14
+ h[k.to_sym] = parse(v)
15
+ end
16
+ self.new(assoc_specs)
17
+ when String, Symbol
18
+ self.new({ data.to_sym => nil })
19
+ when DeepPreloader::AbstractSpec
20
+ data
21
+ when nil
22
+ nil
23
+ else
24
+ raise ArgumentError.new("Cannot parse invalid hash preload spec: #{hash.inspect}")
25
+ end
26
+ end
27
+
28
+ def initialize(association_specs = {})
29
+ @association_specs = association_specs
30
+ end
31
+
32
+ def merge!(other)
33
+ case other
34
+ when nil
35
+ return
36
+ when DeepPreloader::Spec
37
+ other.association_specs.each do |k, v|
38
+ if association_specs[k]
39
+ association_specs[k].merge!(v)
40
+ else
41
+ association_specs[k] = v
42
+ end
43
+ end
44
+ else
45
+ raise ArgumentError.new("Cannot merge #{other.class.name} into #{self.inspect}")
46
+ end
47
+ self
48
+ end
49
+
50
+ def hash
51
+ [self.class, self.association_specs].hash
52
+ end
53
+
54
+ def ==(other)
55
+ self.class == other.class && self.association_specs == other.association_specs
56
+ end
57
+
58
+ alias eql? ==
59
+
60
+ def inspect
61
+ "Spec#{association_specs.inspect}"
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ class DeepPreloader
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,271 @@
1
+ require "deep_preloader/version"
2
+ require "deep_preloader/spec"
3
+ require "deep_preloader/polymorphic_spec"
4
+ require "active_record"
5
+
6
+ DEBUG = ENV['DEBUG'].present?
7
+
8
+ class DeepPreloader
9
+ def self.preload(models, spec, lock: nil)
10
+ return if spec.nil? || models.blank?
11
+
12
+ worker = PreloadWorker.new(lock: lock)
13
+ spec = Spec.parse(spec) unless spec.is_a?(AbstractSpec)
14
+
15
+ models_by_class = Array.wrap(models).group_by(&:class)
16
+
17
+ case spec
18
+ when Spec
19
+ unless models_by_class.size == 1
20
+ raise ArgumentError.new("Provided multiple model types to non-polymorphic preload spec")
21
+ end
22
+
23
+ model_class, models = models_by_class.first
24
+ worker.add_associations_from_spec(models, model_class, spec)
25
+ when PolymorphicSpec
26
+ models_by_class.each do |model_class, models|
27
+ model_spec = spec.for_type(model_class)
28
+ next unless model_spec
29
+ worker.add_associations_from_spec(models, model_class, model_spec)
30
+ end
31
+ end
32
+
33
+ worker.run!
34
+ models
35
+ end
36
+
37
+ class PreloadWorker
38
+ def initialize(lock:)
39
+ @lock = lock
40
+ @worklist = {}
41
+ end
42
+
43
+ def add_associations_from_spec(models, model_class, spec)
44
+ spec.association_specs.each do |association_name, child_spec|
45
+ association_reflection = model_class.reflect_on_association(association_name)
46
+ if association_reflection.nil?
47
+ raise ArgumentError.new("Preloading error: couldn't find association #{association_name} on model class #{model_class.name}")
48
+ end
49
+
50
+ if association_reflection.polymorphic?
51
+ add_polymorphic_association(models, association_reflection, child_spec)
52
+ else
53
+ add_association(models, association_reflection, child_spec)
54
+ end
55
+ end
56
+ end
57
+
58
+ def run!
59
+ while(@worklist.present?)
60
+ context, entries = @worklist.shift
61
+ ActiveRecord::Base.logger.debug("Preloading children in context #{context}") if DEBUG
62
+
63
+ loaded_entries, unloaded_entries = entries.partition(&:loaded?)
64
+
65
+ unloaded_keys = unloaded_entries.map(&:key).to_set.delete(nil)
66
+
67
+ ActiveRecord::Base.logger.debug("Need to load children for following keys: #{unloaded_keys.to_a}") if DEBUG
68
+
69
+ found_children = {}
70
+
71
+ if unloaded_keys.present?
72
+ # When :belongs_to, children could be shared with already loaded
73
+ # entries - use what we already have.
74
+ loaded_entries.each do |entry|
75
+ next unless entry.belongs_to?
76
+
77
+ if entry.belongs_to? && unloaded_keys.delete?(entry.key)
78
+ found_children[entry.key] = entry.children
79
+ end
80
+ end
81
+ ActiveRecord::Base.logger.debug("found loaded children for keys #{found_children.keys}") if DEBUG
82
+ end
83
+
84
+ if unloaded_keys.present?
85
+ fetched_children = context.load_children(unloaded_keys.to_a, lock: @lock)
86
+ ActiveRecord::Base.logger.debug("fetched children for keys #{fetched_children.keys}") if DEBUG
87
+ found_children.merge!(fetched_children)
88
+ end
89
+
90
+ unloaded_entries.each do |entry|
91
+ children = found_children.fetch(entry.key, [])
92
+ entry.children = children
93
+ end
94
+
95
+ entries.each do |entry|
96
+ children = entry.children
97
+ child_spec = entry.child_spec
98
+ next unless child_spec && children.present?
99
+ child_class = children.first.class # children of a given parent are all of the same type
100
+ add_associations_from_spec(children, child_class, child_spec)
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ private
108
+
109
+ def add_polymorphic_association(models, association_reflection, polymorphic_child_spec)
110
+ assoc_name = association_reflection.name
111
+
112
+ # If a model belongs_to a polymorphic child, we know what type it is.
113
+ # Group models by the type of their associated child and add each
114
+ # separately.
115
+ models_by_child_class = models.group_by { |m| m.association(assoc_name).klass }
116
+
117
+ # For models with no child there's nothing to preload, but we still need
118
+ # to set the association target. Since we can't infer a class for
119
+ # `add_association`, set it up here.
120
+ models_by_child_class.delete(nil)&.each do |model|
121
+ model.association(assoc_name).loaded!
122
+ end
123
+
124
+ models_by_child_class.each do |child_class, child_class_models|
125
+ child_preload_spec = polymorphic_child_spec&.for_type(child_class)
126
+ add_association(child_class_models, association_reflection, child_preload_spec, type: child_class)
127
+ end
128
+ end
129
+
130
+ def add_association(models, association_reflection, child_preload_spec, type: association_reflection.klass)
131
+ key_col = child_key_column(association_reflection)
132
+ child_constraints = child_constraints(association_reflection)
133
+
134
+ context = WorklistContext.new(type, key_col, child_constraints)
135
+ models.each do |model|
136
+ entry = WorklistEntry.new(model, association_reflection, child_preload_spec)
137
+ worklist_add(context, entry)
138
+ end
139
+ end
140
+
141
+ def worklist_add(key, entry)
142
+ (@worklist[key] ||= []) << entry
143
+ end
144
+
145
+ def child_constraints(association_reflection)
146
+ constraints = []
147
+ if association_reflection.options[:as]
148
+ # each parent model is pointed to from a child type that could also belong to other types of parent. Constrain the search to this parent.
149
+ constraints << [association_reflection.type, association_reflection.active_record.base_class.sti_name]
150
+ end
151
+
152
+ unless association_reflection.constraints.blank?
153
+ raise ArgumentError.new("Preloading conditional associations not supported: #{association_reflection.name}")
154
+ end
155
+
156
+ unless association_reflection.scope.blank?
157
+ raise ArgumentError.new("Preloading scoped associations not supported: #{association_reflection.name}")
158
+ end
159
+
160
+ constraints
161
+ end
162
+
163
+ def child_key_column(association_reflection)
164
+ case association_reflection.macro
165
+ when :belongs_to
166
+ association_reflection.active_record_primary_key
167
+ when :has_one, :has_many
168
+ association_reflection.foreign_key
169
+ else
170
+ raise "Unsupported association type #{association_reflection.macro}"
171
+ end
172
+ end
173
+ end
174
+
175
+ # entries need to be grouped by:
176
+ # child_type - look up in same table
177
+ # child_key - compare the same keys
178
+ # child_search_constraints - where constraints on child lookup such as polymorphic type or association scope.
179
+ WorklistContext = Struct.new(:child_type, :child_key_column, :child_constraints) do
180
+ def load_children(keys, lock: nil)
181
+ scope = child_constraints.inject(child_type.unscoped) do |sc, (col, val)|
182
+ sc.where(col => val)
183
+ end
184
+
185
+ if lock
186
+ scope = scope.order(:id).lock(lock)
187
+ end
188
+
189
+ scope.where(child_key_column => keys).group_by { |c| c.read_attribute(child_key_column) }
190
+ end
191
+ end
192
+
193
+ class WorklistEntry
194
+ attr_reader :model, :association_reflection, :child_spec
195
+
196
+ def initialize(model, association_reflection, child_spec)
197
+ @model = model
198
+ @association_reflection = association_reflection
199
+ @child_spec = child_spec
200
+ end
201
+
202
+ def association_name
203
+ @association_reflection.name
204
+ end
205
+
206
+ def association_macro
207
+ @association_reflection.macro
208
+ end
209
+
210
+ def loaded?
211
+ model.association(association_name).loaded?
212
+ end
213
+
214
+ def belongs_to?
215
+ association_macro == :belongs_to
216
+ end
217
+
218
+ def collection?
219
+ association_macro == :has_many
220
+ end
221
+
222
+ def key
223
+ model.read_attribute(parent_key_column)
224
+ end
225
+
226
+ # Conceal the difference between singular and collection associations so
227
+ # that `load_children` can always `group_by` the key
228
+ def children
229
+ target = model.association(association_name).target
230
+
231
+ if collection?
232
+ target
233
+ elsif target
234
+ [target]
235
+ else
236
+ []
237
+ end
238
+ end
239
+
240
+ def children=(targets)
241
+ if collection?
242
+ target = targets
243
+ else
244
+ if targets.size > 1
245
+ raise RuntimeError.new("Internal preloader error: attempted to attach multiple children to a singular association")
246
+ end
247
+ target = targets.first
248
+ end
249
+
250
+ ActiveRecord::Base.logger.debug("attaching children to #{model.inspect}.#{association_name}: #{targets}") if DEBUG
251
+
252
+ association = model.association(association_name)
253
+ association.loaded!
254
+ association.target = target
255
+ targets.each { |t| association.set_inverse_instance(t) }
256
+ targets
257
+ end
258
+
259
+ def parent_key_column
260
+ case association_macro
261
+ when :belongs_to
262
+ @association_reflection.foreign_key
263
+ when :has_one, :has_many
264
+ @association_reflection.active_record_primary_key
265
+ else
266
+ raise "Unsupported association type #{@association_reflection.macro}"
267
+ end
268
+ end
269
+ end
270
+
271
+ end
metadata ADDED
@@ -0,0 +1,216 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deep_preloader
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - iKnow Team
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-04-08 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: 5.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: database_cleaner
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: method_source
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: appraisal
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: sqlite3
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description:
168
+ email:
169
+ - dev@iknow.jp
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".circleci/config.yml"
175
+ - ".gitignore"
176
+ - ".rspec"
177
+ - ".travis.yml"
178
+ - Appraisals
179
+ - Gemfile
180
+ - LICENSE.txt
181
+ - README.md
182
+ - Rakefile
183
+ - bin/console
184
+ - bin/setup
185
+ - deep_preloader.gemspec
186
+ - gemfiles/activerecord_5_2.gemfile
187
+ - gemfiles/activerecord_6_0_beta.gemfile
188
+ - lib/deep_preloader.rb
189
+ - lib/deep_preloader/abstract_spec.rb
190
+ - lib/deep_preloader/polymorphic_spec.rb
191
+ - lib/deep_preloader/spec.rb
192
+ - lib/deep_preloader/version.rb
193
+ homepage: http://github.com/iknow/deep_preloader
194
+ licenses:
195
+ - MIT
196
+ metadata: {}
197
+ post_install_message:
198
+ rdoc_options: []
199
+ require_paths:
200
+ - lib
201
+ required_ruby_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ required_rubygems_version: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ requirements: []
212
+ rubygems_version: 3.0.3
213
+ signing_key:
214
+ specification_version: 4
215
+ summary: Explicit preloader for ActiveRecord
216
+ test_files: []