active_record_custom_preloader 0.2.2 → 0.3.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: ebc260ca02c7f72a2c65f2cb594735d982482108
4
- data.tar.gz: 473f45b86356872c382adfde7fa35f7229e5ec38
3
+ metadata.gz: 99ce25238e6af5cdf1e5ded9fb5a20cb538eea30
4
+ data.tar.gz: 6e51a7f912349423c01112afd78b06f0a59806a4
5
5
  SHA512:
6
- metadata.gz: 38dd285e78ce11f4d9648db79b56b95c196916bf7b5c7412d1e81ff6eb7c0bd2ad5f888a215be3e96b54e35afae1258f0bc626eb0452bacf00ea06f0a7b96a75
7
- data.tar.gz: f4b515d22b3a83e74a57e55ac6ceaf7acc0212e09f27856f3c8c90df9a48c073444deb5e48526e49dfd80fed81ae75c3035c95dac9ed8e3ea06d5c24b6f326c3
6
+ metadata.gz: 4f732ec7ac341a988cfbe4163211a0f85176980c31b9581641d5ad9ace75f045a9f021837f2329e7dd7c0748ee97b132825ddc4364066878f319e49dcf152cc1
7
+ data.tar.gz: af9a8f4e709aac03f81b250ac96a4f61289c640e59127dfd5a092a81805e5ffa82ba8c24e77c49b59ffca9aa9a369c94d77f4019e2d0e80bdb349237aa2ef120
data/README.md CHANGED
@@ -4,12 +4,22 @@ custom preloader for ActiveRecord model
4
4
 
5
5
  ## Installation
6
6
 
7
- Add this line to your application's Gemfile:
7
+ Add this line to your application's `Gemfile`:
8
8
 
9
9
  ```ruby
10
10
  gem 'active_record_custom_preloader'
11
11
  ```
12
12
 
13
+ if you use Rails you should require `railtie.rb` file
14
+ like this in `config/application.rb`:
15
+ ```ruby
16
+ require 'active_record_custom_preloader/railtie'
17
+ ```
18
+ or like this in `Gemfile`
19
+ ```ruby
20
+ gem 'active_record_custom_preloader', require: 'active_record_custom_preloader/railtie'
21
+ ```
22
+
13
23
  And then execute:
14
24
 
15
25
  $ bundle
@@ -25,7 +35,10 @@ class Person < ActiveRecord::Base
25
35
  add_custom_loader :_stats, class_name: 'StatsPreloader'
26
36
  end
27
37
 
28
- class StatsPreloader < ActiveRecordCustomerPreloader::Preloader
38
+ class ApplicationPreloader < ActiveRecordCustomPreloader::Preloader
39
+ end
40
+
41
+ class StatsPreloader < ApplicationPreloader
29
42
  def preload(records)
30
43
  ids = records.map(&:id)
31
44
  values = CustomApi.fetch_stats_for(ids)
@@ -4,6 +4,7 @@ require 'active_record_custom_preloader/preload_with_options'
4
4
  require 'active_record_custom_preloader/associations_preloader'
5
5
  require 'active_record_custom_preloader/model_patch'
6
6
  require 'active_record_custom_preloader/relation_patch'
7
+ require 'active_record_custom_preloader/with_multiple_foreign_keys_loading'
7
8
 
8
9
  module ActiveRecordCustomPreloader
9
10
  class Error < StandardError
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordCustomPreloader
2
- VERSION = '0.2.2'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+ require 'active_support/concern'
3
+
4
+ # Usage example:
5
+ #
6
+ # class Employee < ApplicationRecord
7
+ # # columns: id, name, department_id, position_id
8
+ # add_custom_loader :_contract, class_name: 'EmployeeContractPreloader'
9
+ # end
10
+ #
11
+ # class Contract < ApplicationRecord
12
+ # # columns: id, text, employee_department_id, position_id
13
+ # end
14
+ #
15
+ # class EmployeeContractPreloader < ActiveRecordCustomPreloader::Preloader
16
+ # include ActiveRecordCustomPreloader::WithMultipleForeignKeysLoading
17
+ # self.model_class_name = 'Contract'
18
+ # self.association_foreign_keys_names = [:employee_department_id, :position_id]
19
+ #
20
+ # def record_foreign_keys(record)
21
+ # [record.department_id, record.position_id]
22
+ # end
23
+ # end
24
+ #
25
+ module ActiveRecordCustomPreloader
26
+ module WithMultipleForeignKeysLoading
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ # should be true for has_many and false for has_one.
31
+ # [optional] (default false)
32
+ class_attribute :to_many, instance_writer: false
33
+ self.to_many = false
34
+
35
+ # model class of association records
36
+ # [required]
37
+ class_attribute :model_class_name, instance_writer: false
38
+
39
+ # Names of foreign keys which link association record to parent record.
40
+ # Should returns array of key names for association record for query.
41
+ # [required]
42
+ class_attribute :association_foreign_keys_names, instance_writer: false
43
+
44
+ private :fetch_association
45
+ end
46
+
47
+ # association table is queried by return value of this method.
48
+ # returns array of keys for parent record to match association record
49
+ def record_foreign_keys(parent_record)
50
+ association_foreign_keys(parent_record)
51
+ end
52
+
53
+ # association records are grouped by return value of this method.
54
+ # it should match
55
+ # returns array of keys for association record to match parent record
56
+ def association_foreign_keys(assoc_record)
57
+ association_foreign_keys_names.map { |name| assoc_record.public_send(name) }
58
+ end
59
+
60
+ # returns associations for provided parent_record.
61
+ # array for has_many and model or nil for has_one.
62
+ def associations_by_record(grouped_associations, parent_record)
63
+ associations = grouped_associations[record_foreign_keys(parent_record)]
64
+ to_many ? associations || [] : associations&.first
65
+ end
66
+
67
+ # default scope for association records.
68
+ # you can override it for example to preload some values to association records.
69
+ def associations_scope
70
+ model_class_name.constantize.all
71
+ end
72
+
73
+ def fetch_association(parent_records)
74
+ keys = parent_records.map(&method(:record_foreign_keys))
75
+ condition_part = association_foreign_keys_names.map { |name| "#{name} = ?" }.join(' AND ')
76
+ conditions = []
77
+ keys.size.times { conditions.push(condition_part) }
78
+ condition_sql = conditions.map { |condition| "(#{condition})" }.join(' OR ')
79
+ condition_bindings = keys.flatten
80
+ return associations_scope.none if condition_sql.blank? || condition_bindings.empty?
81
+ associations = associations_scope.where(condition_sql, *condition_bindings).to_a
82
+ associations.group_by(&method(:association_foreign_keys))
83
+ end
84
+
85
+ def preload(parent_records)
86
+ grouped_associations = fetch_association(parent_records)
87
+ parent_records.each do |parent_record|
88
+ value = associations_by_record(grouped_associations, parent_record)
89
+ parent_record._set_custom_preloaded_value(name, value)
90
+ end
91
+ end
92
+
93
+ end
94
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_custom_preloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Talakevich
@@ -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_multiple_foreign_keys_loading.rb
108
109
  homepage: https://github.com/senid231/active_record_custom_preloader
109
110
  licenses:
110
111
  - MIT