active_record_custom_preloader 0.3.0 → 0.4.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
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea88d7f2390b0e45e10bd34521c31856bc6ef72f
|
4
|
+
data.tar.gz: 465b71a9c3155369e73188a617bbaadad847322a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57180386f8368ab09c39033d093a8952de62143dd81d98d62c37c355c75e098b27a24684f70fcfb4802d1ee7d09d29ada1ccfde0ee27acb25b9e028e2828d741
|
7
|
+
data.tar.gz: 8ce168e2bcc13fe4dee152d6727103eddb57c67c601e7f4daa0904f795c8db257ce3286d2f56741c3c9e48fbb1c9b4f0314554eadb8048a71fefa7f85d98f7ac
|
data/README.md
CHANGED
@@ -31,6 +31,8 @@ Or install it yourself as:
|
|
31
31
|
## Usage
|
32
32
|
|
33
33
|
```ruby
|
34
|
+
require 'active_record_custom_preloader/railtie'
|
35
|
+
|
34
36
|
class Person < ActiveRecord::Base
|
35
37
|
add_custom_loader :_stats, class_name: 'StatsPreloader'
|
36
38
|
end
|
@@ -59,6 +61,67 @@ person.clear_custom_preloaded_value(:_stats) # will clear preloaded value for _s
|
|
59
61
|
person._stats # will preload again and return value
|
60
62
|
```
|
61
63
|
|
64
|
+
you can use `ActiveRecordCustomPreloader::WithMultipleForeignKeysLoading` module
|
65
|
+
for preloading active_record models which are related to parent record by multiple keys
|
66
|
+
```ruby
|
67
|
+
require 'active_record_custom_preloader/railtie'
|
68
|
+
|
69
|
+
class Employee < ActiveRecord::Base
|
70
|
+
# columns: id, name, department_id, position_id
|
71
|
+
add_custom_loader :_contract, class_name: 'EmployeeContractPreloader'
|
72
|
+
end
|
73
|
+
|
74
|
+
class Contract < ActiveRecord::Base
|
75
|
+
# columns: id, text, employee_department_id, position_id
|
76
|
+
end
|
77
|
+
|
78
|
+
class EmployeeContractPreloader < ActiveRecordCustomPreloader::Preloader
|
79
|
+
include ActiveRecordCustomPreloader::WithMultipleForeignKeysLoading
|
80
|
+
self.model_class_name = 'Contract'
|
81
|
+
self.association_foreign_keys_names = [:employee_department_id, :position_id]
|
82
|
+
|
83
|
+
def record_foreign_keys(record)
|
84
|
+
[record.department_id, record.position_id]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
also you can use `ActiveRecordCustomPreloader::WithContextDependentLoading` module
|
90
|
+
for preloading active_record models which should be filtered by some external context
|
91
|
+
```ruby
|
92
|
+
require 'active_record_custom_preloader/railtie'
|
93
|
+
|
94
|
+
class User < ActiveRecord::Base
|
95
|
+
# columns: id, name, pricelist_id
|
96
|
+
end
|
97
|
+
|
98
|
+
class Discount < ActiveRecord::Base
|
99
|
+
# columns id, user_pricelist_id, product_id, percentage
|
100
|
+
end
|
101
|
+
|
102
|
+
class Product < ActiveRecord::Base
|
103
|
+
# columns: id, name, price
|
104
|
+
add_loader :_discount, class_name: 'ProductDiscountPreloader'
|
105
|
+
end
|
106
|
+
|
107
|
+
class ProductDiscountPreloader < ActiveRecordCustomPreloader::Preloader
|
108
|
+
include ActiveRecordCustomPreloader::WithContextDependentLoading
|
109
|
+
self.to_many = true
|
110
|
+
self.association_group_key = :user_pricelist_id
|
111
|
+
self.record_group_key = :pricelist_id
|
112
|
+
|
113
|
+
def scoped_collection(records)
|
114
|
+
product_ids = records.map(&:id)
|
115
|
+
pricelist_id = args.fetch(:pricelist_id)
|
116
|
+
Discount.where(user_pricelist_id: pricelist_id, product_id: product_ids)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
user = User.first
|
121
|
+
products = Product.limit(10).custom_preload(:_discount, pricelist_id: user.pricelist_id)
|
122
|
+
products.first._discount # will return array with zero or more Discount records
|
123
|
+
```
|
124
|
+
|
62
125
|
## Development
|
63
126
|
|
64
127
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -75,4 +138,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
75
138
|
|
76
139
|
## Code of Conduct
|
77
140
|
|
78
|
-
Everyone interacting in the ActiveRecordCustomPreloader project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
141
|
+
Everyone interacting in the ActiveRecordCustomPreloader project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/senid231/active_record_custom_preloader/blob/master/CODE_OF_CONDUCT.md).
|
@@ -12,5 +12,44 @@ module ActiveRecordCustomPreloader
|
|
12
12
|
ActiveRecordCustomPreloader::AssociationsPreloader.new
|
13
13
|
end
|
14
14
|
end
|
15
|
+
|
16
|
+
# custom preload with optional args
|
17
|
+
# usage:
|
18
|
+
#
|
19
|
+
# class IncomingStatisticPreloader < ActiveRecordCustomPreloader::Preloader
|
20
|
+
# def preload(records)
|
21
|
+
# StatsApi.fetch(:incoming, group_by: args.fetch(:group_by))
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class OutgoingStatisticPreloader < ActiveRecordCustomPreloader::Preloader
|
26
|
+
# def preload(records)
|
27
|
+
# ids = records.map(&:id)
|
28
|
+
# api_result = StatsApi.fetch(:incoming, person_ids: ids, group: args.fetch(:group))
|
29
|
+
# values = api_result.group_by(&:person_id)
|
30
|
+
# records.each do |record|
|
31
|
+
# value = values[record.id] || []
|
32
|
+
# record._set_custom_preloaded_value(name, value)
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# class Person < ActiveRecord::Base
|
38
|
+
# add_loader :_incoming_stats, class_name: 'IncomingStatisticPreloader'
|
39
|
+
# add_loader :_outgoing_stats, class_name: 'OutgoingStatisticPreloader'
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# Person.limit(10).custom_preload(:_incoming_stats, :_outgoing_stats, group: 'hour')
|
43
|
+
# # is syntax sugar for
|
44
|
+
# Person.limit(10).preload(
|
45
|
+
# ActiveRecordCustomPreloader::PreloadWithOptions.new(:_incoming_stats, group: 'hour'),
|
46
|
+
# ActiveRecordCustomPreloader::PreloadWithOptions.new(:_outgoing_stats, group: 'hour')
|
47
|
+
# )
|
48
|
+
#
|
49
|
+
def custom_preload(*names)
|
50
|
+
args = names.extract_options!
|
51
|
+
preloads = names.map { |name| PreloadWithOptions.new(name, args) }
|
52
|
+
preload(*preloads)
|
53
|
+
end
|
15
54
|
end
|
16
55
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
# Usage example:
|
5
|
+
#
|
6
|
+
# class User < ApplicationRecord
|
7
|
+
# # columns: id, name, pricelist_id
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# class Pricelist < ApplicationRecord
|
11
|
+
# # columns: id, name
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class Discount < ApplicationRecord
|
15
|
+
# # columns id, user_pricelist_id, product_id, percentage
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# class Product < ApplicationRecord
|
19
|
+
# # columns: id, name, price
|
20
|
+
# add_loader :_discount, class_name: 'ProductDiscountPreloader'
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# class ProductDiscountPreloader < ActiveRecordCustomPreloader::Preloader
|
24
|
+
# include ActiveRecordCustomPreloader::WithContextDependentLoading
|
25
|
+
# self.to_many = true
|
26
|
+
# self.association_group_key = :user_pricelist_id
|
27
|
+
# self.record_group_key = :pricelist_id
|
28
|
+
#
|
29
|
+
# def scoped_collection(records)
|
30
|
+
# product_ids = records.map(&:id)
|
31
|
+
# pricelist_id = args.fetch(:pricelist_id)
|
32
|
+
# Discount.where(user_pricelist_id: pricelist_id, product_id: product_ids)
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# user = User.first
|
37
|
+
# products = Product.limit(10).custom_preload(:_discount, pricelist_id: user.pricelist_id)
|
38
|
+
# products.first._discount # will return array with zero or more Discount records
|
39
|
+
#
|
40
|
+
module ActiveRecordCustomPreloader
|
41
|
+
module WithContextDependentLoading
|
42
|
+
extend ActiveSupport::Concern
|
43
|
+
|
44
|
+
included do
|
45
|
+
# should be true for has_many and false for has_one.
|
46
|
+
# [optional] (default false)
|
47
|
+
class_attribute :to_many, instance_writer: false
|
48
|
+
self.to_many = false
|
49
|
+
|
50
|
+
# by this method association records will be grouped
|
51
|
+
# [required]
|
52
|
+
class_attribute :association_group_key, instance_writer: false
|
53
|
+
|
54
|
+
# by this method grouped association records will be matched with parent record
|
55
|
+
# [required]
|
56
|
+
class_attribute :record_group_key, instance_writer: false
|
57
|
+
end
|
58
|
+
|
59
|
+
# should return associations scope.
|
60
|
+
# must be overridden.
|
61
|
+
def scoped_collection(_parent_records)
|
62
|
+
raise NotImplementedError.new 'override #scoped_collection in a subclass'
|
63
|
+
end
|
64
|
+
|
65
|
+
# returns associations for provided parent_record.
|
66
|
+
# array for has_many and model or nil for has_one.
|
67
|
+
def associations_by_record(grouped_associations, parent_record)
|
68
|
+
key = parent_record.public_send(record_group_key)
|
69
|
+
associations = grouped_associations[key]
|
70
|
+
to_many ? associations || [] : associations&.first
|
71
|
+
end
|
72
|
+
|
73
|
+
def fetch_association(parent_records)
|
74
|
+
scope = scoped_collection(parent_records)
|
75
|
+
scope.to_a.group_by(&association_group_key)
|
76
|
+
end
|
77
|
+
|
78
|
+
def preload(parent_records)
|
79
|
+
grouped_associations = fetch_association(parent_records)
|
80
|
+
parent_records.each do |parent_record|
|
81
|
+
value = associations_by_record(grouped_associations, parent_record)
|
82
|
+
parent_record._set_custom_preloaded_value(name, value)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_custom_preloader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Talakevich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- lib/active_record_custom_preloader/railtie.rb
|
106
106
|
- lib/active_record_custom_preloader/relation_patch.rb
|
107
107
|
- lib/active_record_custom_preloader/version.rb
|
108
|
+
- lib/active_record_custom_preloader/with_context_dependent_loading.rb
|
108
109
|
- lib/active_record_custom_preloader/with_multiple_foreign_keys_loading.rb
|
109
110
|
homepage: https://github.com/senid231/active_record_custom_preloader
|
110
111
|
licenses:
|