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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b07e539c2e4dd0ab26ae3991c3af0cf5943601dae0c538774cff62ac9709db21
4
- data.tar.gz: af8cdda70c7dfff3b6fe1feba72d39b5c8bcf372c285ef6110d4f45fa471fbee
3
+ metadata.gz: 5a119142073dc411c9af1581fa20953d0931293ab8d743e197c1fe8ab66b6d85
4
+ data.tar.gz: 2e33d0e18a6285d508198d32b8c7094eb4e438257d52c45d3de65f6fc5347733
5
5
  SHA512:
6
- metadata.gz: ebbc597b3ecf57cb80da6cff33852a7dff935cb8b8fcd30d2d5223dcfb54b31c415b80df0c05bb48a46ea6bd4f54d158e1a68c3516d3014622b3fc3ebaf81744
7
- data.tar.gz: 0aebc50d2cb3dcd2363dd6167f30dc479f0c415e6b5cc1786e623c0cb8be3544df9c4aae2426f77ed5c5fe335b1c2986a8587d5910ba7d8e30fe6cbb516ceb5d
6
+ metadata.gz: 58aa697961c7872373973bdbdde0f097e70643fd2c8d325b26df8e079bb5ce0d3be4ca5f1445e907e6a0ccf3f9caa8e21d5dc21a5d2f646f40d5bd649132952d
7
+ data.tar.gz: 402a314b8f3313f2431ec6b1f707408444ea26e5ee1f199f3de525afb85ba507297b2cf70aa9a90e54f6a9a01d3ff1038d842b81b753879533c295743c5b76b7
data/Appraisals ADDED
@@ -0,0 +1,11 @@
1
+ appraise "rails-7" do
2
+ gem "rails", "~> 7.1"
3
+ end
4
+
5
+ appraise "rails-7.2" do
6
+ gem "rails", "~> 7.1"
7
+ end
8
+
9
+ appraise "rails-8" do
10
+ gem "rails", ">= 8"
11
+ end
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, leading to less RAM and CPU usage.
8
- Here is a benchmark output for a [gist I've created](https://gist.github.com/alekseyl/5d08782808a29df6813f16965f70228a) to emulate real-life example: displaying a course with its structure.
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 prod set of data used by current UI (~ x33 times less RAM):
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
- # includes in full
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 (there is no UI for multiple courses display with their structure)
45
- on the real prod data, but the bigger than needed collection (x7 faster):
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
- Despite this little click bait it's pretty obvious that it might not be even the biggest numbers,
70
- if you have heavy relations instantiation for heavy views or reports generation,
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 preloading
106
- Whenever you are using through relations between models rails will fully load all intermediate objects under the hood,
107
- that is definitely wastes lots of RAM, CPU including those on the DB side.
108
- You can limit through objects only to relation columns.
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 here
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
- # Now user - loaded fully
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: ["images.*", avatars: [user_profile: [:id]]]).first
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
- **REM** As for version 0.4.0 for the earliest relation in a through chain you need to select something,
146
- otherwise nested_select will select everything ))
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
- - [ ] Separated rails version testing
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
- nested_select_values.blank? ? super :
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
- @nested_select_values = [*partial_select_values, *foreign_key, *reflection.klass.primary_key]
14
- ensure_nesting_selection_integrity!
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
- def ensure_nesting_selection_integrity!
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 = @nested_select_values.grep_v(Hash).map(&:to_s)
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
- # def through_scope
30
- # if @limit_through_selection.present?
31
- # # through_selection is either __minimize_through_selection symbol, or an array
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?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NestedSelect
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.2"
5
5
  end
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.0
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-02-09 00:00:00.000000000 Z
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
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="NodePackageJsonFileManager">
4
- <packageJsonPaths />
5
- </component>
6
- </project>
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>
@@ -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
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
- </component>
6
- </project>
@@ -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