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: 99ce25238e6af5cdf1e5ded9fb5a20cb538eea30
4
- data.tar.gz: 6e51a7f912349423c01112afd78b06f0a59806a4
3
+ metadata.gz: ea88d7f2390b0e45e10bd34521c31856bc6ef72f
4
+ data.tar.gz: 465b71a9c3155369e73188a617bbaadad847322a
5
5
  SHA512:
6
- metadata.gz: 4f732ec7ac341a988cfbe4163211a0f85176980c31b9581641d5ad9ace75f045a9f021837f2329e7dd7c0748ee97b132825ddc4364066878f319e49dcf152cc1
7
- data.tar.gz: af9a8f4e709aac03f81b250ac96a4f61289c640e59127dfd5a092a81805e5ffa82ba8c24e77c49b59ffca9aa9a369c94d77f4019e2d0e80bdb349237aa2ef120
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/[USERNAME]/active_record_custom_preloader/blob/master/CODE_OF_CONDUCT.md).
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
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordCustomPreloader
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  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.3.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-09 00:00:00.000000000 Z
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: