nested_select 0.4.0 → 0.4.2
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 +4 -4
- data/Appraisals +11 -0
- data/CHANGELOG.md +13 -0
- data/Dockerfile_apprsl +22 -0
- data/README.md +35 -29
- data/docker-compose.yml +8 -0
- data/lib/nested_select/deep_merger.rb +0 -10
- data/lib/nested_select/preloader/association.rb +16 -6
- data/lib/nested_select/preloader/branch.rb +1 -1
- data/lib/nested_select/preloader/through_association.rb +3 -65
- data/lib/nested_select/preloader.rb +0 -7
- data/lib/nested_select/version.rb +1 -1
- metadata +18 -7
- data/.idea/misc.xml +0 -6
- data/.idea/modules.xml +0 -8
- data/.idea/nested_select.iml +0 -126
- data/.idea/vcs.xml +0 -6
- data/test_habtm_reflections.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a119142073dc411c9af1581fa20953d0931293ab8d743e197c1fe8ab66b6d85
|
4
|
+
data.tar.gz: 2e33d0e18a6285d508198d32b8c7094eb4e438257d52c45d3de65f6fc5347733
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58aa697961c7872373973bdbdde0f097e70643fd2c8d325b26df8e079bb5ce0d3be4ca5f1445e907e6a0ccf3f9caa8e21d5dc21a5d2f646f40d5bd649132952d
|
7
|
+
data.tar.gz: 402a314b8f3313f2431ec6b1f707408444ea26e5ee1f199f3de525afb85ba507297b2cf70aa9a90e54f6a9a01d3ff1038d842b81b753879533c295743c5b76b7
|
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## [0.4.2] - 2025-03-01
|
2
|
+
- Refactoring
|
3
|
+
- Removed all commented out code
|
4
|
+
|
5
|
+
## [0.4.1] - 2025-03-01
|
6
|
+
- Appraisal added as a development dependency
|
7
|
+
- added github CI
|
8
|
+
|
9
|
+
## [0.4.0] - 2025-02-09
|
10
|
+
- through relations are now also supported and allowed to partial selection via reverse nested_selection tree
|
11
|
+
- README updated with more examples and corner cases
|
12
|
+
- nested_select will prevent multiple partial instantiation with different attributes
|
13
|
+
|
1
14
|
## [0.3.0] - 2025-01-25
|
2
15
|
|
3
16
|
- nested_select belongs_to limitation now prevents accidental foreign_key absence
|
data/Dockerfile_apprsl
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
FROM ruby:3.2-bullseye
|
2
|
+
|
3
|
+
WORKDIR /app
|
4
|
+
RUN apt-get update && apt-get -y install lsb-release
|
5
|
+
#
|
6
|
+
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
|
7
|
+
sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' && \
|
8
|
+
apt-get update && apt-get -y install postgresql postgresql-client-14
|
9
|
+
|
10
|
+
RUN sh -c 'echo "local all all trust" > /etc/postgresql/$(ls /etc/postgresql)/main/pg_hba.conf' && \
|
11
|
+
service postgresql start && \
|
12
|
+
psql -U postgres -c 'CREATE DATABASE "niceql-test"'
|
13
|
+
|
14
|
+
RUN gem install bundler
|
15
|
+
|
16
|
+
COPY lib/rails_sql_prettifier/version.rb /app/lib/rails_sql_prettifier/version.rb
|
17
|
+
COPY rails_sql_prettifier.gemspec /app/
|
18
|
+
COPY Gemfil* /app/
|
19
|
+
COPY Appraisals /app/
|
20
|
+
#
|
21
|
+
RUN bundle install
|
22
|
+
RUN bundle exec appraisal install
|
data/README.md
CHANGED
@@ -1,18 +1,15 @@
|
|
1
|
-
# WIP disclaimer
|
2
|
-
The gem is under active development now.
|
3
|
-
Use in prod with caution only if you are properly covered by your CI.
|
4
|
-
Read **Safety** and **Limitations** sections before.
|
5
|
-
|
6
1
|
# Nested select -- 7 times faster and 33 times less RAM on preloading relations with heavy columns!
|
7
|
-
nested_select allows the partial selection of the relations attributes during preloading process,
|
8
|
-
|
2
|
+
nested_select allows the partial selection of the relations attributes during preloading process,
|
3
|
+
leading to less RAM and CPU usage.
|
4
|
+
Here is a benchmark output for a [gist I've created](https://gist.github.com/alekseyl/5d08782808a29df6813f16965f70228a) to run real-life example:
|
5
|
+
displaying a course with its structure.
|
9
6
|
|
10
7
|
Given:
|
11
8
|
- Models are Course, Topic, Lesson.
|
12
9
|
- Their relations has a following structure: course has_many topics, each topic has_many lessons.
|
13
10
|
- To display a single course you need its structure, minimum data needed: topic and lessons titles and ordering.
|
14
11
|
|
15
|
-
**Single course**, a real
|
12
|
+
**Single course**, a real example against production data and a real flow (~ x33 times less RAM):
|
16
13
|
|
17
14
|
```
|
18
15
|
irb(main):216:0>compare_nested_select(ids, 1, silence_ar_logger_for_memory_profiling: false)
|
@@ -26,7 +23,7 @@ simple includes 0.209188 0.058340 0.267528 ( 0.903893)
|
|
26
23
|
# partial selection
|
27
24
|
D, [2025-01-12T19:08:36.163282 #503] DEBUG -- : Topic Load (4.1ms) SELECT "topics"."id", "topics"."position", "topics"."title", "topics"."course_id" FROM "topics" WHERE "topics"."deleted_at" IS NULL AND "topics"."course_id" = $1 [["course_id", 1624]]
|
28
25
|
D, [2025-01-12T19:08:36.168803 #503] DEBUG -- : Lesson Load (3.9ms) SELECT "lessons"."id", "lessons"."title", "lessons"."topic_id", "lessons"."position", "lessons"."topic_id" FROM "lessons" WHERE "lessons"."deleted_at" IS NULL AND "lessons"."topic_id" = $1 [["topic_id", 7297]]
|
29
|
-
#
|
26
|
+
# selects in full
|
30
27
|
D, [2025-01-12T19:08:37.220379 #503] DEBUG -- : Topic Load (4.2ms) SELECT "topics"."id", "topics"."position", "topics"."title", "topics"."course_id" FROM "topics" WHERE "topics"."deleted_at" IS NULL AND "topics"."course_id" = $1 [["course_id", 1624]]
|
31
28
|
D, [2025-01-12T19:08:37.247484 #503] DEBUG -- : Lesson Load (25.7ms) SELECT "lessons".* FROM "lessons" WHERE "lessons"."deleted_at" IS NULL AND "lessons"."topic_id" = $1 [["topic_id", 7297]]
|
32
29
|
|
@@ -41,8 +38,8 @@ RAM ratio improvements x33.54678126442086 on retain objects
|
|
41
38
|
RAM ratio improvements x15.002820281285949 on total_allocated objects
|
42
39
|
```
|
43
40
|
|
44
|
-
**100 courses**, this is kinda a synthetic example
|
45
|
-
|
41
|
+
**100 courses**, this is kinda a synthetic example since there is no UI for multiple courses display together with their structures.
|
42
|
+
It executed against the real production data. (nested select serves x7 faster):
|
46
43
|
|
47
44
|
```
|
48
45
|
irb(main):280:0> compare_nested_select(ids, 100)
|
@@ -66,9 +63,8 @@ RAM ratio improvements x15.57707431190517 on retain objects
|
|
66
63
|
RAM ratio improvements x11.836000856510193 on total_allocated objects
|
67
64
|
```
|
68
65
|
|
69
|
-
|
70
|
-
|
71
|
-
and you want it to be less demanding in RAM and CPU -- you should try nested_select
|
66
|
+
**Summary:** if you have CPU/RAM bottlenecks, heavy relations instantiation for heavy views or reports generation,
|
67
|
+
and you want it to be less demanding in RAM and CPU -- you should try nested_select.
|
72
68
|
|
73
69
|
## Installation
|
74
70
|
|
@@ -102,10 +98,11 @@ end
|
|
102
98
|
User.includes(:profile).select(profile: :photo_url).limit(10)
|
103
99
|
```
|
104
100
|
|
105
|
-
### Partial through relations
|
106
|
-
Whenever you are using through relations between models
|
107
|
-
|
108
|
-
|
101
|
+
### Partial preloading of through relations
|
102
|
+
Whenever you are using `through` relations between models and running preload,
|
103
|
+
then rails will fully load all intermediate objects under the hood!
|
104
|
+
That is definitely wastes lots of RAM, CPU including those on the DB side.
|
105
|
+
With nested_select you can apply selections to `through` relations.
|
109
106
|
Ex:
|
110
107
|
|
111
108
|
```ruby
|
@@ -115,11 +112,11 @@ class User
|
|
115
112
|
end
|
116
113
|
|
117
114
|
# pay attention user_profile relation, wasn't included explicitly,
|
118
|
-
# but still rails needed to be preloaded to match and preload avatars
|
115
|
+
# but still rails needed them to be preloaded to be able to match and preload avatars
|
119
116
|
user = User.includes(:avatars)
|
120
117
|
.select(avatars: [:img_url, { user_profile: [:zip_code] }]).first
|
121
118
|
|
122
|
-
#
|
119
|
+
# user - loaded fully
|
123
120
|
# avatars - foreign and primary keys needed to establish relations + img_url
|
124
121
|
# user_profile - foreign and primary keys + zip_code
|
125
122
|
```
|
@@ -135,15 +132,24 @@ class User
|
|
135
132
|
has_many :through_avatar_images, through: :avatars, class_name: :Image, source: :images
|
136
133
|
end
|
137
134
|
|
138
|
-
# only through_avatar_images is matter here, and we want everything
|
135
|
+
# only through_avatar_images is matter here, and we want everything else to be as small as possible
|
139
136
|
user = User.includes(:through_avatar_images)
|
140
|
-
.select(through_avatar_images: [
|
137
|
+
.select(through_avatar_images: [avatars: [:id, user_profile: [:id]]]).first
|
141
138
|
|
142
139
|
# through_avatar_images -- loaded in full
|
143
140
|
# avatars, user_profile -- only relations columns id, user_profile_id e.t.c
|
144
141
|
```
|
145
|
-
|
146
|
-
|
142
|
+
|
143
|
+
**REM** There was an idea for through relations use a skinny approach: no nested attributes means,
|
144
|
+
only relation keys should be loaded:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
user = User.includes(:through_avatar_images)
|
148
|
+
.select(through_avatar_images: [avatars: :user_profile]).first
|
149
|
+
```
|
150
|
+
but that could be easily confused with normal flow behaviour, so I stick to basic default: no nested attributes,
|
151
|
+
means default behaviour, i.e. all attributes.
|
152
|
+
|
147
153
|
|
148
154
|
# Safety
|
149
155
|
How safe is the partial model loading? Earlier version of rails and activerecord would return nil in the case,
|
@@ -177,10 +183,6 @@ This needs a lot of monkey patching, and for now I decided not to go this way.
|
|
177
183
|
That means in case when nesting selects based on belongs_to reflections,
|
178
184
|
you'll need to select their foreign keys **EXPLICITLY!**
|
179
185
|
|
180
|
-
## will not work with ar_lazy_preload
|
181
|
-
Right now it will not work with ar_lazy_preload gem. nested_select relies on the includes_values definition
|
182
|
-
of a relation. If you are doing it in a lazy way, there weren't any explicit includes, that means it will not extract any nested selection.
|
183
|
-
|
184
186
|
```ruby
|
185
187
|
class Avatar < ApplicationRecord
|
186
188
|
belongs_to user
|
@@ -200,6 +202,10 @@ Image.includes(avatar: :user).select(avatar: [:size, { user: [:email] }]).load #
|
|
200
202
|
Image.includes(avatar: :user).select(avatar: [:size, :user_id, { user: [:email] }]).load
|
201
203
|
```
|
202
204
|
|
205
|
+
## will not work with ar_lazy_preload
|
206
|
+
Right now it will not work with ar_lazy_preload gem. nested_select relies on the includes_values definition
|
207
|
+
of a relation. If you are doing it in a lazy way, there weren't any explicit includes, that means it will not extract any nested selection.
|
208
|
+
|
203
209
|
## Testing
|
204
210
|
|
205
211
|
```bash
|
@@ -212,7 +218,7 @@ docker compose run test
|
|
212
218
|
- [x] Ensure primary key will be added
|
213
219
|
- [-] Ensure belongs_to will add a foreign_key column (Too hard to manage :(, its definitely not a low hanging fruit)
|
214
220
|
- [x] Optimize through relations ( since they loading a whole set of attributes )
|
215
|
-
- [
|
221
|
+
- [x] Separated rails version testing
|
216
222
|
- [x] Merge multiple nested selections
|
217
223
|
- [x] Don't apply any selection if blank ( allows to limit only part of subselection tree)
|
218
224
|
- [x] Allows to use custom attributes
|
data/docker-compose.yml
CHANGED
@@ -11,3 +11,11 @@ services:
|
|
11
11
|
- './Gemfile:/app/Gemfile'
|
12
12
|
- './Gemfile.lock:/app/Gemfile.lock'
|
13
13
|
|
14
|
+
appraisal:
|
15
|
+
build:
|
16
|
+
context: .
|
17
|
+
dockerfile: Dockerfile_apprsl
|
18
|
+
image: nested_select_appraisal
|
19
|
+
command: /bin/bash -c 'service postgresql start && appraisal rake test'
|
20
|
+
volumes:
|
21
|
+
- '.:/app'
|
@@ -7,22 +7,12 @@ module NestedSelect
|
|
7
7
|
merge!(other.except(*keys))
|
8
8
|
merge!(other.slice(*keys).map{ |key, value| [key, [self[key], value].flatten.deep_combine_elements]}.to_h )
|
9
9
|
end
|
10
|
-
|
11
|
-
def relation_nesting_depth
|
12
|
-
1 + [*values.grep(Hash), *values.grep(Array)].map(&:relation_nesting_depth).max.to_i
|
13
|
-
end
|
14
10
|
end
|
15
11
|
|
16
12
|
refine Array do
|
17
13
|
def deep_combine_elements
|
18
14
|
[*grep_v(Hash), grep(Hash).inject(&:deep_combine)].uniq.compact
|
19
15
|
end
|
20
|
-
# in terms of relation/selection nesting every array node
|
21
|
-
# should not add +1 to depth, that mean that array nesting
|
22
|
-
# does not count as nesting [[[]]] has depth equal 0.
|
23
|
-
def relation_nesting_depth
|
24
|
-
[*grep(Hash), *grep(Array)].map(&:relation_nesting_depth).max.to_i
|
25
|
-
end
|
26
16
|
end
|
27
17
|
end
|
28
18
|
end
|
@@ -4,17 +4,27 @@ module NestedSelect
|
|
4
4
|
attr_reader :nested_select_values
|
5
5
|
|
6
6
|
def build_scope
|
7
|
-
|
8
|
-
super.select(*nested_select_values.grep_v(Hash).map{_1.try(:to_s) }.uniq)
|
7
|
+
association_nested_select_values.blank? ? super : super.select(association_nested_select_values)
|
9
8
|
end
|
10
9
|
|
11
10
|
def apply_nested_select_values(partial_select_values)
|
11
|
+
@nested_select_values = [*partial_select_values]
|
12
|
+
ensure_nesting_selection_integrity!(association_nested_select_values)
|
13
|
+
end
|
14
|
+
def reflection_relation_keys_attributes
|
12
15
|
foreign_key = reflection.foreign_key unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
13
|
-
|
14
|
-
|
16
|
+
[*foreign_key, *reflection.klass.primary_key].map(&:to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
def association_nested_select_values
|
20
|
+
this_association_select_values = nested_select_values&.grep_v(Hash)&.map {_1.try(:to_s) }
|
21
|
+
return if this_association_select_values.blank?
|
22
|
+
|
23
|
+
[*this_association_select_values, *reflection_relation_keys_attributes].uniq
|
15
24
|
end
|
16
25
|
|
17
|
-
|
26
|
+
# ensure that different preloading branches will match nested selected attributes
|
27
|
+
def ensure_nesting_selection_integrity!(nested_select_final_values)
|
18
28
|
single_owner = owners.first
|
19
29
|
# do nothing unless not yet loaded
|
20
30
|
return unless single_owner.association(reflection.name).loaded?
|
@@ -25,7 +35,7 @@ module NestedSelect
|
|
25
35
|
single_reflection_record = single_reflection_record.first if single_reflection_record.is_a?(Enumerable)
|
26
36
|
|
27
37
|
attributes_loaded = single_reflection_record.attributes.keys.map(&:to_s)
|
28
|
-
current_selection =
|
38
|
+
current_selection = nested_select_final_values.grep_v(Hash).map(&:to_s)
|
29
39
|
|
30
40
|
basic_attributes_matched = (attributes_loaded & reflection.klass.column_names).tally ==
|
31
41
|
(current_selection & reflection.klass.column_names).tally
|
@@ -23,7 +23,7 @@ module NestedSelect
|
|
23
23
|
|
24
24
|
raise ActiveModel::MissingAttributeError, <<~ERR
|
25
25
|
Parent reflection #{parent.association} was missing foreign key #{reflection.foreign_key} in nested selection,
|
26
|
-
while trying to preload belongs_to reflection named #{reflection.name}.
|
26
|
+
while trying to preload belongs_to reflection named #{reflection.name}.
|
27
27
|
Hint: didn't you forgot to add #{reflection.foreign_key} inside #{parent.nested_select_values}?
|
28
28
|
ERR
|
29
29
|
end
|
@@ -1,20 +1,6 @@
|
|
1
1
|
module NestedSelect
|
2
2
|
module Preloader
|
3
3
|
module ThroughAssociation
|
4
|
-
# this preloader root will preload intermediate records, so here we should apply 'through'
|
5
|
-
# selection limitation AS A BASIC nested selection and it wuold be either __minimize_through_selection sym OR
|
6
|
-
# nested_selection tree
|
7
|
-
# def source_preloaders
|
8
|
-
# @source_preloaders ||= ActiveRecord::Associations::Preloader.new(
|
9
|
-
# records: middle_records,
|
10
|
-
# associations: source_reflection.name,
|
11
|
-
# scope: scope,
|
12
|
-
# associate_by_default: false
|
13
|
-
# ).tap {
|
14
|
-
# byebug
|
15
|
-
# _1.apply_nested_select_values([*@limit_through_selection])
|
16
|
-
# }.loaders
|
17
|
-
# end
|
18
4
|
def through_preloaders
|
19
5
|
@through_preloaders ||= ActiveRecord::Associations::Preloader.new(
|
20
6
|
records: owners,
|
@@ -26,58 +12,10 @@ module NestedSelect
|
|
26
12
|
end.loaders
|
27
13
|
end
|
28
14
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# through_selection = [*@limit_through_selection]
|
33
|
-
# through_selection = [*through_selection.grep_v(Hash),
|
34
|
-
# *through_selection.grep(Hash).first&.dig(through_reflection.source_reflection_name)]
|
35
|
-
# through_selection << through_reflection.foreign_key
|
36
|
-
# through_selection << through_reflection.klass.primary_key
|
37
|
-
# super.select(through_selection)
|
38
|
-
# else
|
39
|
-
# super
|
40
|
-
# end
|
41
|
-
# end
|
42
|
-
|
43
|
-
# def through_scope
|
44
|
-
# if @limit_through_selection.present?
|
45
|
-
# super.select(through_reflection.foreign_key.to_sym, through_reflection.klass.primary_key.to_sym)
|
46
|
-
# else
|
47
|
-
# super
|
48
|
-
# end
|
49
|
-
# end
|
50
|
-
# def through_selection_nesting
|
51
|
-
# return if @limit_through_selection.blank?
|
52
|
-
# through_limit_selection = [through_reflection.foreign_key.to_sym, through_reflection.klass.primary_key.to_sym]
|
53
|
-
# through_limit_selection << :__minimize_through_selection if through_reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
54
|
-
# through_limit_selection
|
55
|
-
# end
|
56
|
-
|
57
|
-
def apply_nested_select_values(partial_select_values)
|
58
|
-
|
59
|
-
if reflection.parent_reflection.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection)
|
60
|
-
# when parent reflection is a HasAndBelongsToManyReflection,
|
61
|
-
# then we don't need foreign_key to be included, as it does in super
|
62
|
-
@nested_select_values = partial_select_values
|
63
|
-
else
|
64
|
-
@limit_through_selection = partial_select_values.delete(:__minimize_through_selection)
|
65
|
-
super(partial_select_values)
|
66
|
-
end
|
15
|
+
def reflection_relation_keys_attributes
|
16
|
+
foreign_key = reflection.foreign_key unless reflection.parent_reflection.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection)
|
17
|
+
[*foreign_key, *reflection.klass.primary_key].map(&:to_s)
|
67
18
|
end
|
68
|
-
# def exract_through_selections(partial_select_values)
|
69
|
-
# # __minimize_through_selection: [ :user_id, user_profile: [] ]
|
70
|
-
# # there should not be more than one such limitation definition
|
71
|
-
# through_selection_rules, cleaned_partial_select_values = partial_select_values&.partition do
|
72
|
-
# _1 == :__minimize_through_selection || _1.is_a?(Hash) && _1[:__minimize_through_selection].present?
|
73
|
-
# end
|
74
|
-
# @limit_through_selection = through_selection_rules.map do
|
75
|
-
# _1.is_a?(Hash) && _1[:__minimize_through_selection] || _1
|
76
|
-
# end.first
|
77
|
-
#
|
78
|
-
# byebug
|
79
|
-
# cleaned_partial_select_values
|
80
|
-
# end
|
81
19
|
|
82
20
|
end
|
83
21
|
end
|
@@ -18,13 +18,6 @@ module NestedSelect
|
|
18
18
|
distribute_nested_select_over_loading_tree(@tree, nested_select_values)
|
19
19
|
end
|
20
20
|
|
21
|
-
# this one either subtree roots only when its first, OR current branch selection root
|
22
|
-
# + its branches in array
|
23
|
-
# 1) [:__minimize_through_selection, ...]
|
24
|
-
# 2) []
|
25
|
-
# Логика следующая:
|
26
|
-
# если мы передаем __minimize_through_selection как символ, то его надо пробросить до всех бранчей, они с этим разберутся, там где надо
|
27
|
-
# если у нас там хеш, то его надо оставить на текущем уровне и дальше если это through, то оно превратит это в нестед селект для вложенных
|
28
21
|
def distribute_nested_select_over_loading_tree(sub_tree, nested_select_values)
|
29
22
|
# nested_select_values = [:id, :title, comments: [:id, :body], cover: [:id, img: [:url]]]
|
30
23
|
return if nested_select_values.blank?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nested_select
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alekseyl
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: appraisal
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
181
195
|
description: ActiveRecord improved select on nested models, allows partial instantiation
|
182
196
|
on nested models, easy one step improvements on performance and memory
|
183
197
|
email:
|
@@ -188,15 +202,13 @@ extra_rdoc_files: []
|
|
188
202
|
files:
|
189
203
|
- ".idea/.gitignore"
|
190
204
|
- ".idea/inspectionProfiles/profiles_settings.xml"
|
191
|
-
- ".idea/misc.xml"
|
192
|
-
- ".idea/modules.xml"
|
193
|
-
- ".idea/nested_select.iml"
|
194
|
-
- ".idea/vcs.xml"
|
195
205
|
- ".rubocop.yml"
|
196
206
|
- ABOUT_NESTED_SELECT.md
|
207
|
+
- Appraisals
|
197
208
|
- CHANGELOG.md
|
198
209
|
- CODE_OF_CONDUCT.md
|
199
210
|
- Dockerfile
|
211
|
+
- Dockerfile_apprsl
|
200
212
|
- LICENSE.txt
|
201
213
|
- README.md
|
202
214
|
- Rakefile
|
@@ -210,7 +222,6 @@ files:
|
|
210
222
|
- lib/nested_select/relation.rb
|
211
223
|
- lib/nested_select/version.rb
|
212
224
|
- sig/nested_select.rbs
|
213
|
-
- test_habtm_reflections.rb
|
214
225
|
homepage: https://github.com/alekseyl/nested_select
|
215
226
|
licenses:
|
216
227
|
- MIT
|
data/.idea/misc.xml
DELETED
data/.idea/modules.xml
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<project version="4">
|
3
|
-
<component name="ProjectModuleManager">
|
4
|
-
<modules>
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/nested_select.iml" filepath="$PROJECT_DIR$/.idea/nested_select.iml" />
|
6
|
-
</modules>
|
7
|
-
</component>
|
8
|
-
</project>
|
data/.idea/nested_select.iml
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<module type="RUBY_MODULE" version="4">
|
3
|
-
<component name="ModuleRunConfigurationManager">
|
4
|
-
<shared />
|
5
|
-
</component>
|
6
|
-
<component name="NewModuleRootManager">
|
7
|
-
<content url="file://$MODULE_DIR$">
|
8
|
-
<sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
|
9
|
-
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
10
|
-
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
11
|
-
</content>
|
12
|
-
<orderEntry type="jdk" jdkName="RVM: ruby-3.3.4 [ns1]" jdkType="RUBY_SDK" />
|
13
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
14
|
-
<orderEntry type="library" scope="PROVIDED" name="actionpack (v8.0.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
15
|
-
<orderEntry type="library" scope="PROVIDED" name="actionview (v8.0.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
16
|
-
<orderEntry type="library" scope="PROVIDED" name="activemodel (v8.0.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
17
|
-
<orderEntry type="library" scope="PROVIDED" name="activerecord (v8.0.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
18
|
-
<orderEntry type="library" scope="PROVIDED" name="activesupport (v8.0.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
19
|
-
<orderEntry type="library" scope="PROVIDED" name="amazing_print (v1.7.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
20
|
-
<orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
21
|
-
<orderEntry type="library" scope="PROVIDED" name="benchmark (v0.4.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
22
|
-
<orderEntry type="library" scope="PROVIDED" name="bigdecimal (v3.1.9, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
23
|
-
<orderEntry type="library" scope="PROVIDED" name="builder (v3.3.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
24
|
-
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.5.11, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
25
|
-
<orderEntry type="library" scope="PROVIDED" name="byebug (v11.1.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
26
|
-
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.4, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
27
|
-
<orderEntry type="library" scope="PROVIDED" name="connection_pool (v2.5.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
28
|
-
<orderEntry type="library" scope="PROVIDED" name="crass (v1.0.6, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
29
|
-
<orderEntry type="library" scope="PROVIDED" name="date (v3.4.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
30
|
-
<orderEntry type="library" scope="PROVIDED" name="drb (v2.2.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
31
|
-
<orderEntry type="library" scope="PROVIDED" name="erubi (v1.13.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
32
|
-
<orderEntry type="library" scope="PROVIDED" name="i18n (v1.14.6, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
33
|
-
<orderEntry type="library" scope="PROVIDED" name="io-console (v0.8.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
34
|
-
<orderEntry type="library" scope="PROVIDED" name="irb (v1.14.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
35
|
-
<orderEntry type="library" scope="PROVIDED" name="json (v2.9.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
36
|
-
<orderEntry type="library" scope="PROVIDED" name="language_server-protocol (v3.17.0.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
37
|
-
<orderEntry type="library" scope="PROVIDED" name="logger (v1.6.5, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
38
|
-
<orderEntry type="library" scope="PROVIDED" name="loofah (v2.24.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
39
|
-
<orderEntry type="library" scope="PROVIDED" name="minitest (v5.25.4, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
40
|
-
<orderEntry type="library" scope="PROVIDED" name="niceql (v0.6.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
41
|
-
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.18.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
42
|
-
<orderEntry type="library" scope="PROVIDED" name="parallel (v1.26.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
43
|
-
<orderEntry type="library" scope="PROVIDED" name="parser (v3.3.6.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
44
|
-
<orderEntry type="library" scope="PROVIDED" name="psych (v5.2.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
45
|
-
<orderEntry type="library" scope="PROVIDED" name="racc (v1.8.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
46
|
-
<orderEntry type="library" scope="PROVIDED" name="rack (v3.1.8, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
47
|
-
<orderEntry type="library" scope="PROVIDED" name="rack-session (v2.1.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
48
|
-
<orderEntry type="library" scope="PROVIDED" name="rack-test (v2.2.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
49
|
-
<orderEntry type="library" scope="PROVIDED" name="rackup (v2.2.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
50
|
-
<orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.2.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
51
|
-
<orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.6.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
52
|
-
<orderEntry type="library" scope="PROVIDED" name="rails-i18n (v8.0.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
53
|
-
<orderEntry type="library" scope="PROVIDED" name="rails_sql_prettifier (v7.0.4, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
54
|
-
<orderEntry type="library" scope="PROVIDED" name="railties (v8.0.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
55
|
-
<orderEntry type="library" scope="PROVIDED" name="rainbow (v3.1.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
56
|
-
<orderEntry type="library" scope="PROVIDED" name="rake (v13.2.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
57
|
-
<orderEntry type="library" scope="PROVIDED" name="rdoc (v6.11.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
58
|
-
<orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.10.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
59
|
-
<orderEntry type="library" scope="PROVIDED" name="reline (v0.6.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
60
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop (v1.70.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
61
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.37.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
62
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-shopify (v2.15.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
63
|
-
<orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.13.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
64
|
-
<orderEntry type="library" scope="PROVIDED" name="securerandom (v0.4.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
65
|
-
<orderEntry type="library" scope="PROVIDED" name="sqlite3 (v2.5.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
66
|
-
<orderEntry type="library" scope="PROVIDED" name="stringio (v3.1.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
67
|
-
<orderEntry type="library" scope="PROVIDED" name="stubberry (v0.3.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
68
|
-
<orderEntry type="library" scope="PROVIDED" name="thor (v1.3.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
69
|
-
<orderEntry type="library" scope="PROVIDED" name="timeout (v0.4.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
70
|
-
<orderEntry type="library" scope="PROVIDED" name="tzinfo (v2.0.6, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
71
|
-
<orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v3.1.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
72
|
-
<orderEntry type="library" scope="PROVIDED" name="unicode-emoji (v4.0.4, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
73
|
-
<orderEntry type="library" scope="PROVIDED" name="uri (v1.0.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
74
|
-
<orderEntry type="library" scope="PROVIDED" name="useragent (v0.16.11, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
75
|
-
<orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.7.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
|
76
|
-
</component>
|
77
|
-
<component name="RakeTasksCache-v2">
|
78
|
-
<option name="myRootTask">
|
79
|
-
<RakeTaskImpl id="rake">
|
80
|
-
<subtasks>
|
81
|
-
<RakeTaskImpl description="Build nested_select-0.3.0.gem into the pkg directory" fullCommand="build" id="build" />
|
82
|
-
<RakeTaskImpl id="build">
|
83
|
-
<subtasks>
|
84
|
-
<RakeTaskImpl description="Generate SHA512 checksum of nested_select-0.3.0.gem into the checksums directory" fullCommand="build:checksum" id="checksum" />
|
85
|
-
</subtasks>
|
86
|
-
</RakeTaskImpl>
|
87
|
-
<RakeTaskImpl description="Remove any temporary products" fullCommand="clean" id="clean" />
|
88
|
-
<RakeTaskImpl description="Remove any generated files" fullCommand="clobber" id="clobber" />
|
89
|
-
<RakeTaskImpl description="Build and install nested_select-0.3.0.gem into system gems" fullCommand="install" id="install" />
|
90
|
-
<RakeTaskImpl id="install">
|
91
|
-
<subtasks>
|
92
|
-
<RakeTaskImpl description="Build and install nested_select-0.3.0.gem into system gems without network access" fullCommand="install:local" id="local" />
|
93
|
-
</subtasks>
|
94
|
-
</RakeTaskImpl>
|
95
|
-
<RakeTaskImpl description="Create tag v0.3.0 and build and push nested_select-0.3.0.gem to https://rubygems.org" fullCommand="release[remote]" id="release[remote]" />
|
96
|
-
<RakeTaskImpl description="Run RuboCop" fullCommand="rubocop" id="rubocop" />
|
97
|
-
<RakeTaskImpl id="rubocop">
|
98
|
-
<subtasks>
|
99
|
-
<RakeTaskImpl description="Autocorrect RuboCop offenses (only when it's safe)" fullCommand="rubocop:autocorrect" id="autocorrect" />
|
100
|
-
<RakeTaskImpl description="Autocorrect RuboCop offenses (safe and unsafe)" fullCommand="rubocop:autocorrect_all" id="autocorrect_all" />
|
101
|
-
<RakeTaskImpl description="" fullCommand="rubocop:auto_correct" id="auto_correct" />
|
102
|
-
</subtasks>
|
103
|
-
</RakeTaskImpl>
|
104
|
-
<RakeTaskImpl description="Run the test suite" fullCommand="test" id="test" />
|
105
|
-
<RakeTaskImpl id="test">
|
106
|
-
<subtasks>
|
107
|
-
<RakeTaskImpl description="Print out the test command" fullCommand="test:cmd" id="cmd" />
|
108
|
-
<RakeTaskImpl description="Show which test files fail when run in isolation" fullCommand="test:isolated" id="isolated" />
|
109
|
-
<RakeTaskImpl description="Run the test suite and report the slowest 25 tests" fullCommand="test:slow" id="slow" />
|
110
|
-
<RakeTaskImpl description="" fullCommand="test:deps" id="deps" />
|
111
|
-
</subtasks>
|
112
|
-
</RakeTaskImpl>
|
113
|
-
<RakeTaskImpl description="" fullCommand="default" id="default" />
|
114
|
-
<RakeTaskImpl description="" fullCommand="release" id="release" />
|
115
|
-
<RakeTaskImpl id="release">
|
116
|
-
<subtasks>
|
117
|
-
<RakeTaskImpl description="" fullCommand="release:guard_clean" id="guard_clean" />
|
118
|
-
<RakeTaskImpl description="" fullCommand="release:rubygem_push" id="rubygem_push" />
|
119
|
-
<RakeTaskImpl description="" fullCommand="release:source_control_push" id="source_control_push" />
|
120
|
-
</subtasks>
|
121
|
-
</RakeTaskImpl>
|
122
|
-
</subtasks>
|
123
|
-
</RakeTaskImpl>
|
124
|
-
</option>
|
125
|
-
</component>
|
126
|
-
</module>
|
data/.idea/vcs.xml
DELETED
data/test_habtm_reflections.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "test_helper"
|
3
|
-
|
4
|
-
class TestHabtmReflections < ActiveSupport::TestCase
|
5
|
-
include ActiveRecord::TestFixtures
|
6
|
-
|
7
|
-
self.use_instantiated_fixtures = true
|
8
|
-
|
9
|
-
|
10
|
-
test "select allows nesting attribute selection" do
|
11
|
-
item = Item.includes(users: [user_profile: :avatars])
|
12
|
-
.select("items.*", users: [:id, :name, user_profile: [:id, :bio, avatars: [:img_url]]])
|
13
|
-
.find(identify(:mug))
|
14
|
-
|
15
|
-
user = item.users.first
|
16
|
-
assert_equal(user.name, "Frodo")
|
17
|
-
assert_raises(ActiveModel::MissingAttributeError) { user.membership }
|
18
|
-
assert_equal(user.reload.membership, "basic")
|
19
|
-
|
20
|
-
assert_includes(user.user_profile.avatars.map(&:img_url), avatars(:frodo_avatar).img_url)
|
21
|
-
end
|
22
|
-
|
23
|
-
test "works fine with inverse_of basic reflection" do
|
24
|
-
user = User.includes(user_profile: :avatars)
|
25
|
-
.select("users.*", user_profile: [:id, :user_id, avatars: [:id, :user_profile_id]])
|
26
|
-
.find(identify(:frodo))
|
27
|
-
|
28
|
-
# NestedSelect::Preloader::Branch#preloaders_for_reflection
|
29
|
-
assert_equal(user.user_profile.user.object_id, user.object_id)
|
30
|
-
assert_equal(user.user_profile.object_id, user.user_profile.avatars.first.user_profile.object_id)
|
31
|
-
end
|
32
|
-
|
33
|
-
test "works fine with through reflection" do
|
34
|
-
user = User.includes(:avatars)
|
35
|
-
.select("users.*", avatars: [:id, :user_profile_id])
|
36
|
-
.find(identify(:frodo))
|
37
|
-
|
38
|
-
assert_raises(ActiveModel::MissingAttributeError) { user.avatars.first.img_url }
|
39
|
-
assert_equal(user.avatars, [avatars(:frodo_avatar)])
|
40
|
-
assert_equal(user.avatars.first.reload.img_url, "https://api.rubyonrails.org/")
|
41
|
-
end
|
42
|
-
|
43
|
-
test "partial selection always includes foreign keys" do
|
44
|
-
user = User.includes(user_profile: :avatars)
|
45
|
-
.select("users.*", user_profile: [:id, :bio])
|
46
|
-
.find(identify(:frodo))
|
47
|
-
|
48
|
-
assert_equal(user.user_profile.user_id, user.id)
|
49
|
-
end
|
50
|
-
|
51
|
-
test "partial selection always includes foreign keys also for through reflection bb" do
|
52
|
-
user = User.includes(:avatars)
|
53
|
-
.select("users.*", avatars: [:id, :img_url])
|
54
|
-
.find(identify(:frodo))
|
55
|
-
|
56
|
-
assert_equal(user.avatars, [avatars(:frodo_avatar)])
|
57
|
-
assert_not_nil(user.avatars.first.user_profile_id)
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|