nested_select 0.1.0 → 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
  SHA256:
3
- metadata.gz: d4307dec9a6ddcdc24842d6c3f155b2c2d9e64cc6a1f66049134ad80ef2cde41
4
- data.tar.gz: 7c8fca2b94e90e6361d54c4dfe51d444a42fb60d0be48c487978a98ddf1eb1a0
3
+ metadata.gz: f7df4328a84a6701ef2081be1b647cd9f1c07442b6dfd48c580126daa369ef80
4
+ data.tar.gz: 17fd02a9b6f0bf767b55bae9243534030a4ccd7d524ff0c72cd3f62ab4e7b46c
5
5
  SHA512:
6
- metadata.gz: 40421aa19cf4f21d42326e9e350f83282fa51d82f49945367265864e2761af9d4c72499cabf82be93cdb2d80486c2593f5df3dd6d218b49c3868b72b8f13a701
7
- data.tar.gz: 4133532cf6a4884b0195212cb2f1eb36a0b326bda5defd5d2498def327d89692d9df86487df40e59c7becfceddde3124ca02db0babd930c5b4f19314a8c6f09e
6
+ metadata.gz: 413915e07ba1daa75689c3863f8b6084aaaacace89afe075aaf115a61b6c34245acee01c4eae87ec9509bf7ce0d0b38e9f4fa4a6e17d341177a71d1cb5e31d42
7
+ data.tar.gz: d955c8d791e58df7025998c69a43bf459c03897384c2c68658e03600ba63158678012c90fded55f35fb8acc64fa652d255381d7164077dbe9c01febddf23d1e8
data/.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
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>
@@ -0,0 +1,125 @@
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="ruby-progressbar (v1.13.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
63
+ <orderEntry type="library" scope="PROVIDED" name="securerandom (v0.4.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
64
+ <orderEntry type="library" scope="PROVIDED" name="sqlite3 (v2.5.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
65
+ <orderEntry type="library" scope="PROVIDED" name="stringio (v3.1.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
66
+ <orderEntry type="library" scope="PROVIDED" name="stubberry (v0.3.0, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
67
+ <orderEntry type="library" scope="PROVIDED" name="thor (v1.3.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
68
+ <orderEntry type="library" scope="PROVIDED" name="timeout (v0.4.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
69
+ <orderEntry type="library" scope="PROVIDED" name="tzinfo (v2.0.6, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
70
+ <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v3.1.3, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
71
+ <orderEntry type="library" scope="PROVIDED" name="unicode-emoji (v4.0.4, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
72
+ <orderEntry type="library" scope="PROVIDED" name="uri (v1.0.2, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
73
+ <orderEntry type="library" scope="PROVIDED" name="useragent (v0.16.11, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
74
+ <orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.7.1, RVM: ruby-3.3.4 [ns1]) [gem]" level="application" />
75
+ </component>
76
+ <component name="RakeTasksCache-v2">
77
+ <option name="myRootTask">
78
+ <RakeTaskImpl id="rake">
79
+ <subtasks>
80
+ <RakeTaskImpl description="Build nested_select-0.2.0.gem into the pkg directory" fullCommand="build" id="build" />
81
+ <RakeTaskImpl id="build">
82
+ <subtasks>
83
+ <RakeTaskImpl description="Generate SHA512 checksum of nested_select-0.2.0.gem into the checksums directory" fullCommand="build:checksum" id="checksum" />
84
+ </subtasks>
85
+ </RakeTaskImpl>
86
+ <RakeTaskImpl description="Remove any temporary products" fullCommand="clean" id="clean" />
87
+ <RakeTaskImpl description="Remove any generated files" fullCommand="clobber" id="clobber" />
88
+ <RakeTaskImpl description="Build and install nested_select-0.2.0.gem into system gems" fullCommand="install" id="install" />
89
+ <RakeTaskImpl id="install">
90
+ <subtasks>
91
+ <RakeTaskImpl description="Build and install nested_select-0.2.0.gem into system gems without network access" fullCommand="install:local" id="local" />
92
+ </subtasks>
93
+ </RakeTaskImpl>
94
+ <RakeTaskImpl description="Create tag v0.2.0 and build and push nested_select-0.2.0.gem to https://rubygems.org" fullCommand="release[remote]" id="release[remote]" />
95
+ <RakeTaskImpl description="Run RuboCop" fullCommand="rubocop" id="rubocop" />
96
+ <RakeTaskImpl id="rubocop">
97
+ <subtasks>
98
+ <RakeTaskImpl description="Autocorrect RuboCop offenses (only when it's safe)" fullCommand="rubocop:autocorrect" id="autocorrect" />
99
+ <RakeTaskImpl description="Autocorrect RuboCop offenses (safe and unsafe)" fullCommand="rubocop:autocorrect_all" id="autocorrect_all" />
100
+ <RakeTaskImpl description="" fullCommand="rubocop:auto_correct" id="auto_correct" />
101
+ </subtasks>
102
+ </RakeTaskImpl>
103
+ <RakeTaskImpl description="Run the test suite" fullCommand="test" id="test" />
104
+ <RakeTaskImpl id="test">
105
+ <subtasks>
106
+ <RakeTaskImpl description="Print out the test command" fullCommand="test:cmd" id="cmd" />
107
+ <RakeTaskImpl description="Show which test files fail when run in isolation" fullCommand="test:isolated" id="isolated" />
108
+ <RakeTaskImpl description="Run the test suite and report the slowest 25 tests" fullCommand="test:slow" id="slow" />
109
+ <RakeTaskImpl description="" fullCommand="test:deps" id="deps" />
110
+ </subtasks>
111
+ </RakeTaskImpl>
112
+ <RakeTaskImpl description="" fullCommand="default" id="default" />
113
+ <RakeTaskImpl description="" fullCommand="release" id="release" />
114
+ <RakeTaskImpl id="release">
115
+ <subtasks>
116
+ <RakeTaskImpl description="" fullCommand="release:guard_clean" id="guard_clean" />
117
+ <RakeTaskImpl description="" fullCommand="release:rubygem_push" id="rubygem_push" />
118
+ <RakeTaskImpl description="" fullCommand="release:source_control_push" id="source_control_push" />
119
+ </subtasks>
120
+ </RakeTaskImpl>
121
+ </subtasks>
122
+ </RakeTaskImpl>
123
+ </option>
124
+ </component>
125
+ </module>
data/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
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>
data/.rubocop.yml CHANGED
@@ -6,3 +6,86 @@ Style/StringLiterals:
6
6
 
7
7
  Style/StringLiteralsInInterpolation:
8
8
  EnforcedStyle: double_quotes
9
+
10
+ Style/SingleLineMethods:
11
+ Description: 'Avoid single-line methods.'
12
+ StyleGuide: '#no-single-line-methods'
13
+ Enabled: false
14
+ VersionAdded: '0.9'
15
+ VersionChanged: '1.8'
16
+ AllowIfMethodIsEmpty: true
17
+
18
+ Style/AsciiComments:
19
+ Description: 'Use only ascii symbols in comments.'
20
+ StyleGuide: '#english-comments'
21
+ Enabled: false
22
+ VersionAdded: '0.9'
23
+ VersionChanged: '1.21'
24
+ AllowedChars:
25
+ - ©
26
+
27
+ Layout/LineLength:
28
+ Description: 'Checks that line length does not exceed the configured limit.'
29
+ StyleGuide: '#max-line-length'
30
+ Enabled: true
31
+ VersionAdded: '0.25'
32
+ VersionChanged: '1.4'
33
+ Max: 120
34
+ # To make it possible to copy or click on URIs in the code, we allow lines
35
+ # containing a URI to be longer than Max.
36
+ AllowHeredoc: true
37
+ AllowURI: true
38
+ URISchemes:
39
+ - http
40
+ - https
41
+ # The IgnoreCopDirectives option causes the LineLength rule to ignore cop
42
+ # directives like '# rubocop: enable ...' when calculating a line's length.
43
+ IgnoreCopDirectives: true
44
+ # The AllowedPatterns option is a list of !ruby/regexp and/or string
45
+ # elements. Strings will be converted to Regexp objects. A line that matches
46
+ # any regular expression listed in this option will be ignored by LineLength.
47
+ AllowedPatterns: []
48
+ IgnoredPatterns: [] # deprecated
49
+ Exclude:
50
+ - "./test/**/*"
51
+
52
+ Metrics/ClassLength:
53
+ Description: 'Avoid classes longer than 100 lines of code.'
54
+ Enabled: false
55
+ VersionAdded: '0.25'
56
+ VersionChanged: '0.87'
57
+ CountComments: false # count full line comments?
58
+ Max: 100
59
+ CountAsOne: []
60
+
61
+ Lint/MissingCopEnableDirective:
62
+ Description: 'Checks for a `# rubocop:enable` after `# rubocop:disable`.'
63
+ Enabled: true
64
+ VersionAdded: '0.52'
65
+ # Maximum number of consecutive lines the cop can be disabled for.
66
+ # 0 allows only single-line disables
67
+ # 1 would mean the maximum allowed is the following:
68
+ # # rubocop:disable SomeCop
69
+ # a = 1
70
+ # # rubocop:enable SomeCop
71
+ # .inf for any size
72
+ MaximumRangeSize: .inf
73
+
74
+ Style/MethodCallWithArgsParentheses:
75
+ Enabled: true
76
+ IgnoredMethods:
77
+ - require
78
+ - require_relative
79
+ - require_dependency
80
+ - yield
81
+ - raise
82
+ - puts
83
+ Exclude:
84
+ - "/**/Gemfile"
85
+
86
+ Style/ClassAndModuleChildren:
87
+ Enabled: false
88
+
89
+ Lint/UnderscorePrefixedVariableName:
90
+ Exclude:
91
+ - "./test/**/**/*"
@@ -0,0 +1,88 @@
1
+ # A little bit of nested_select history
2
+ Awhile ago I've investigated the potential performance boost from partial instantiation
3
+ of database records in rails applications: [Rails nitro-fast collection rendering with PostgreSQL](https://medium.com/@leshchuk/rails-nitro-fast-collection-rendering-with-postgresql-a5fb07cc215f)
4
+
5
+ To be short among the others I've tested the idea of Partial instantiation:
6
+
7
+ > Sometimes different actions needs different set of columns per ORM object. You can speedup instantiation
8
+ > by creating sets of attributes specific for particular request.
9
+ > It can be done through the scopes and scoped relations inside your model.
10
+
11
+ **Pluses**
12
+ > It may be faster. How fast? Highly depends on data structure and ratio of used columns. I started from instantinating 75% of object columns and go to 1 or 2 columns being instantiated.
13
+ > In terms of instantiation results are: 1.3–4.2 times faster on simple type columns ( text, string, int, bool etc.), and 1.2–10 times faster when you exclude json/jsonb/store instantiation.
14
+ > Also all this numbers received without any instantiation callbacks like after_find.
15
+
16
+ Also during my investigations I've kinda missed to mention the other aspects of the problem: RAM, DB IOps, network throughput.
17
+ Requesting less columns improves¹ all that things.
18
+
19
+ [1] I have much less idea on how other than PotsgreSQL DB-engines are working with a disk in terms of partial tuples/records reading.
20
+ Postgres itself will read a whole page from a disk to retrieve the record, but then lesser columns could switch retrieval to an Index-Only scan decreasing IOps significantly
21
+
22
+ But that's a pretty obvious. There are lot of articles covering this problem and idea of a partial selection:
23
+
24
+ ActiveRecord select :id column over 1000 records in a different way:
25
+ https://samsaffron.com/archive/2018/06/01/an-analysis-of-memory-bloat-in-active-record-5-2
26
+
27
+ Just another simple and newbie technics on boosting ActiveRecord ( including partial selection ):
28
+ https://medium.com/@snapsheetclaims/11-ways-to-boost-your-activerecord-query-performance-32b9986f093f
29
+
30
+ Just partial selection article:
31
+ https://pawelurbanek.com/activerecord-memory-usage
32
+
33
+ And others.
34
+
35
+ But the real problem is: **in rails you can't do any selection on preloading models** (until nested_select of course :)) ).
36
+ Ths means that all that tree of preloaded object goes with ```SELECT table_name.*``` query.
37
+
38
+ Technically speaking you may solve this problem by defining custom scopes and defining custom tailored relation with scopes.
39
+ But that's a lot of a boilerplate code, creating scopes and nested relations for all kinds of requests looks like unreal solution,
40
+ no one will do such madness.
41
+
42
+ ## Nested Select patch
43
+
44
+ ### How preloading happens in rails and when is the best time to interfere
45
+
46
+ **Preloading** is a part of activerecord which tends to change pretty often.
47
+ Practically all major releases interfere preloader code, the majority of current implementation was introduced in the rails 7.0 version.
48
+ But if you get the idea of current implementation, you can traverse the earlier state and get the idea how to make them work with nested select:
49
+ Regardless of the major rails version and implementation, you will end up patching the `build_scope` method of
50
+ `ActiveRecord::Associations::Preloader::Association`!
51
+
52
+ So you just need to define a way to deliver select_values to instance of `ActiveRecord::Associations::Preloader::Association`
53
+
54
+ ### How preloading happens in rails >= 7.0
55
+ To be honest ( and opinionated :) ), current preloading implementation is messy,
56
+ and we need to adapt to this mess without delivering some more.
57
+
58
+ Let's look at the scopes example from a specs:
59
+ ```ruby
60
+ # user <-habtm-> bought_items
61
+ # user -> has_one -> user_profile -> has_many -> avatars
62
+ User.includes(:bought_items, user_profile: :avatars)
63
+ ```
64
+
65
+ Preloading will create instances of the `Preloader` class for:
66
+ - each isolated preloading `Branch` which started from the root, in this case: `:bought_items` and `user_profile: :avatars`
67
+ - each trough relation inside preloading tree, including hidden ones like habtm relation does.
68
+
69
+ Each `Preloader` object building it's own preloader tree from a set of `Branch` objects.
70
+ In a given case it might roughly look like this:
71
+ ```
72
+ Preloader(:bought_items) -> Branch(:root)
73
+ \__ Branch(:bought_items) -> Preloader::ThroughAssociation(:bought_items)
74
+
75
+ Preloader(user_profile: :avatars) -> Branch(:root)
76
+ \__ Branch(:user_profile) -> Preloader::Association(:user_profile)
77
+ \__Branch(:avatars) -> Preloader::Association(:avatars)
78
+ ```
79
+
80
+ Each `Branch` will create a set of loaders objects of `Preloader::Association` or `Preloader::ThroughAssociation`
81
+ Then running all of them will preload nested records and establish a connections between records.
82
+
83
+ To be able to select limited attributes sets, we need to deliver them to `Association` level objects, and patch `build_scope` method with it.
84
+
85
+ **_Rem:_** Each `Preloader::ThroughAssociation` object creates it's own `Preloader` and starts additional 'isolated' preloading process.
86
+
87
+ The implementation of nested_select adds a `nested_select_values` attributes into instances of `Preloader`, `Branch`, `Association` hierarchy
88
+ and some methods to populate corresponding select_values over the tree, trying to be as less invasive as it could be.
data/CHANGELOG.md CHANGED
@@ -1,4 +1,17 @@
1
- ## [Unreleased]
1
+ ## [0.3.0] - 2025-01-25
2
+
3
+ - nested_select belongs_to limitation now prevents accidental foreign_key absence
4
+ - primary keys are no longer need to be nested_selected
5
+ - nested selects will combine all selections on multiple select invocations like the usual select values does
6
+ - tests restructured
7
+ - test/README added
8
+ - removed biolerplates for basic selection ( you don't need to specify "table.*" in the root collection seletc )
9
+
10
+ ## [0.2.0] - 2025-01-25
11
+
12
+ - Tests are now a part of this repo
13
+ - Readme cleared out
14
+ - ABOUT_NESTED_SELECT md added.
2
15
 
3
16
  ## [0.1.0] - 2025-01-11
4
17
 
data/Dockerfile ADDED
@@ -0,0 +1,12 @@
1
+ FROM ruby:3.4
2
+
3
+ WORKDIR /app
4
+
5
+ RUN gem install bundler
6
+
7
+ COPY lib/nested_select/version.rb /app/lib/nested_select/version.rb
8
+ COPY nested_select.gemspec /app/
9
+ COPY Gemfile* /app/
10
+ COPY Rakefile /app/
11
+
12
+ RUN bundle install
data/README.md CHANGED
@@ -1,54 +1,71 @@
1
- ## Nested Select patch
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. Read **Safety** and **Limitations** sections before.
2
4
 
3
- ### How preloading happens in rails and when is the best time to interfere
5
+ # Nested select -- 7 times faster and 33 times less RAM on preloading relations with heavy columns!
6
+ nested_select allows the partial selection of the relations attributes during preloading process, leading to less RAM and CPU usage.
7
+ 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.
4
8
 
5
- **Preloading** is a part of activerecord which tends to change pretty often.
6
- Practically all major releases interfere preloader code, the current implementation was introduced starting rails 7.0 version.
7
- But if you get the idea of current implementation, you can traverse the earlier state and get the idea how to make them work with nested select:
8
- Regardless of the major rails version and implementation, you will end up patching the `build_scope` method of
9
- `ActiveRecord::Associations::Preloader::Association`!
9
+ Given:
10
+ - Models are Course, Topic, Lesson.
11
+ - Their relations has a following structure: course has_many topics, each topic has_many lessons.
12
+ - To display a single course you need its structure, minimum data needed: topic and lessons titles and ordering.
10
13
 
11
- So you just need to define a way to deliver select_values to instance of `ActiveRecord::Associations::Preloader::Association`
14
+ **Single course**, a real prod set of data used by current UI (~ x33 times less RAM):
12
15
 
13
- ### How preloading happens in rails >= 7.0
14
- To be honest ( and opinionated :) ), current preloading implementation is a kinda mess, and we need to adapt to this mess without delivering some more.
15
-
16
- Let's look at the scopes example:
17
- ```ruby
18
- # user <-habtm-> bought_items
19
- # user -> has_one -> user_profile -> has_many -> avatars
20
- User.includes( :bought_items, user_profile: :avatars)
16
+ ```
17
+ irb(main):216:0>compare_nested_select(ids, 1, silence_ar_logger_for_memory_profiling: false)
18
+
19
+ ------- CPU comparison, for root_collection_size: 1 ----
20
+ user system total real
21
+ nested_select 0.096008 0.002876 0.098884 ( 0.466985)
22
+ simple includes 0.209188 0.058340 0.267528 ( 0.903893)
23
+
24
+ ----------------- Memory comparison, for root_collection_size: 1 ---------
25
+
26
+ 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]]
27
+ 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]]
28
+ 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]]
29
+ 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]]
30
+
31
+ ------ Nested Select memory consumption for root_collection_size: 1 ------
32
+ Total allocated: 80.84 kB (972 objects)
33
+ Total retained: 34.67 kB (288 objects)
34
+
35
+ ------ Full preloading memory consumption for root_collection_size: 1 ----
36
+ Total allocated: 1.21 MB (1105 objects)
37
+ Total retained: 1.16 MB (432 objects)
38
+ RAM ratio improvements x33.54678126442086 on retain objects
39
+ RAM ratio improvements x15.002820281285949 on total_allocated objects
21
40
  ```
22
41
 
23
- Preloading will create instances `Preloader` objects for:
24
- - each isolated preloading `Branch` which started from the root, in this case: `:bought_items` and `user_profile: :avatars`
25
- - each trough relation inside preloading tree, including hidden ones like habtm relation does.
42
+ **100 courses**, this is kinda a synthetic example (there is no UI for multiple courses display with their structure)
43
+ on the real prod data, but the bigger than needed collection (x7 faster):
26
44
 
27
- Each `Preloader` object building it's own preloader tree from a set of `Branch` objects.
28
- In a given case it might roughly look like this:
29
- ```
30
- Preloader(:bought_items) -> Branch(:root)
31
- \__ Branch(:bought_items) -> Preloader::ThroughAssociation(:bought_items)
32
-
33
- Preloader(user_profile: :avatars) -> Branch(:root)
34
- \__ Branch(:user_profile) -> Preloader::Association(:user_profile)
35
- \__Branch(:avatars) -> Preloader::Association(:avatars)
36
45
  ```
46
+ irb(main):280:0> compare_nested_select(ids, 100)
37
47
 
38
- Each `Branch` will create a set of loaders objects of `Preloader::Association` or `Preloader::ThroughAssociation`
39
- Then running all of them will preload nested records and establish a connections between records.
48
+ ------- CPU comparison, for root_collection_size: 100 ----
49
+ user system total real
50
+ nested_select 1.571095 0.021778 1.592873 ( 2.263369)
51
+ simple includes 5.374909 1.704284 7.079193 ( 15.488579)
52
+
53
+ ----------------- Memory comparison, for root_collection_size: 100 ---------
54
+ ------ Nested Select memory consumption for root_collection_size: 100 ------
40
55
 
41
- To be able to select limited attributes sets, we need to deliver them to `Association` level objects, and patch `build_scope` method with it.
56
+ Total allocated: 2.79 MB (30702 objects)
57
+ Total retained: 2.05 MB (16431 objects)
42
58
 
43
- **_Rem:_** Each `Preloader::ThroughAssociation` object creates it's own `Preloader` and starts additional 'isolated' preloading process.
59
+ ------ Full preloading memory consumption for root_collection_size: 100 ----
44
60
 
45
- So implementation adds a `nested_select_values` attributes into instances of `Preloader`, `Branch`, `Association` hierarchy and some methods to populate corresponding select_values over the tree.
61
+ Total allocated: 33.05 MB (38332 objects)
62
+ Total retained: 32.00 MB (24057 objects)
63
+ RAM ratio improvements x15.57707431190517 on retain objects
64
+ RAM ratio improvements x11.836000856510193 on total_allocated objects
46
65
 
47
- ## Installation
66
+ ```
48
67
 
49
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org.
50
- Please do not do it earlier due to security reasons.
51
- Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
68
+ ## Installation
52
69
 
53
70
  Install the gem and add to the application's Gemfile by executing:
54
71
 
@@ -56,21 +73,85 @@ Install the gem and add to the application's Gemfile by executing:
56
73
 
57
74
  If bundler is not being used to manage dependencies, install the gem by executing:
58
75
 
59
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
76
+ $ gem install nested_select
60
77
 
61
78
  ## Usage
79
+ Assume you have a relation users <- profile, and you want to preview users in a paginated feed,
80
+ and you need only :photo_url attribute of a profile, with nested_select you can do it like this:
81
+
82
+ ```ruby
83
+ class User
84
+ has_one :profile
85
+ end
86
+
87
+ class Profile
88
+ belongs_to :user
89
+ end
90
+
91
+ # this will preload profile with exact attributes:
92
+ # :id -- since its a primary key,
93
+ # :user_id -- since its a foreign_key
94
+ # and the :photo_url as requested
95
+ User.includes(:profile).select(profile: :photo_url).limit(10)
96
+ ```
62
97
 
63
- TODO: Write usage instructions here
98
+ ## Safety
99
+ How safe is the partial model loading? Earlier version of rails and activerecord would return nil in the case,
100
+ when attribute wasn't selected from a DB, but rails 6 started to raise a ActiveModel::MissingAttributeError.
101
+ So the major problem is already solved -- your code will not operate based on falsy blank values, it will raise an exception.
64
102
 
65
- ## Development
103
+ ## belongs_to foreign keys limitations
104
+ Rails preloading happens from loaded record to their reflection step by step.
105
+ That's makes pretty easy to include foreign keys for has_* relations, and very hard for belongs_to,
106
+ to work this out you need to analyze includes based on the already loaded records, analyze and traverse their relations.
107
+ This needs a lot of monkey patching, and for now I decided not to gow this way.
108
+ That means in case when nesting selects based on belongs_to reflections,
109
+ you'll need to select their foreign keys **EXPLICITLY!**
110
+
111
+ ```ruby
112
+ class Avatar < ApplicationRecord
113
+ belongs_to user
114
+ has_one :image
115
+ end
116
+
117
+ class Image < ApplicationRecord
118
+ belongs_to :avatar
119
+ end
120
+
121
+ Image.includes(avatar: :user).select(avatar: [:id, :size, { user: [:email] }]).load # <--- will raise a Missing Attribute exception
122
+
123
+ #> ActiveModel::MissingAttributeError: Parent reflection avatar was missing foreign key user_id in nested selection
124
+ #> while trying to preload belongs_to reflection named user.
125
+ #> Hint: didn't you forgot to add user_id inside [:id, :size]?
126
+
127
+ Image.includes(avatar: :user).select(avatar: [:id, :size, :user_id, { user: [:email] }]).load
128
+ ```
66
129
 
67
- 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.
68
130
 
69
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
131
+ ## Testing
132
+
133
+ ```bash
134
+ docker compose run test
135
+ ```
136
+
137
+ ## TODO
138
+ - [ ] Cover all relation combinations and add missing functionality
139
+ - [x] Ensure relations foreign keys are present on the selection
140
+ - [x] Ensure primary key will be added
141
+ - [-] Ensure belongs_to will add a foreign_key column
142
+ - [ ] Optimize through relations ( since they loading a whole set of attributes )
143
+ - [ ] Separated rails version testing
144
+ - [x] Merge multiple nested selections
145
+ - [x] Don't apply any selection if blank ( allows to limit only part of subselection tree)
146
+ - [x] Allows to use custom attributes
147
+
148
+ ## Development
149
+
70
150
 
71
151
  ## Contributing
72
152
 
73
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/nested_select. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/nested_select/blob/master/CODE_OF_CONDUCT.md).
153
+ Bug reports and pull requests are welcome on GitHub at https://github.com/alekseyl/nested_select. This project is intended to be a safe,
154
+ welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/nested_select/blob/master/CODE_OF_CONDUCT.md).
74
155
 
75
156
  ## License
76
157
 
@@ -0,0 +1,13 @@
1
+ version: "3.7"
2
+
3
+ services:
4
+ test:
5
+ build: .
6
+ image: nested_select
7
+ command: rake test
8
+ volumes:
9
+ - './lib:/app/lib'
10
+ - './test:/app/test'
11
+ - './Gemfile:/app/Gemfile'
12
+ - './Gemfile.lock:/app/Gemfile.lock'
13
+
@@ -0,0 +1,18 @@
1
+ module NestedSelect
2
+ module DeepMerger
3
+
4
+ # {user_profile: [:zip_code]} + {user_profile: [:bio]} -> { user_profile: [:zip_code, :bio] }
5
+ refine Hash do
6
+ def deep_combine(other)
7
+ merge!(other.except(*keys))
8
+ merge!(other.slice(*keys).map{ |key, value| [key, [self[key], value].flatten.deep_combine_elements]}.to_h )
9
+ end
10
+ end
11
+
12
+ refine Array do
13
+ def deep_combine_elements
14
+ [*grep_v(Hash), grep(Hash).inject(&:deep_combine)].uniq.compact
15
+ end
16
+ end
17
+ end
18
+ end
@@ -4,12 +4,12 @@ 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)
7
+ nested_select_values.blank? ? super : super.select(*nested_select_values)
9
8
  end
10
9
 
11
- def apply_nested_select_values( partial_select_values )
12
- @nested_select_values = [*partial_select_values, reflection.foreign_key].uniq
10
+ def apply_nested_select_values(partial_select_values)
11
+ foreign_key = reflection.foreign_key unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
12
+ @nested_select_values = [*partial_select_values, *foreign_key, *reflection.klass.primary_key].uniq
13
13
  end
14
14
  end
15
15
  end
@@ -4,11 +4,29 @@ module NestedSelect
4
4
  module Branch
5
5
  attr_accessor :nested_select_values
6
6
  def preloaders_for_reflection(reflection, reflection_records)
7
+ prevent_belongs_to_foreign_key_absence!(reflection)
8
+
7
9
  super.tap do |ldrs|
8
10
  ldrs.each{ _1.apply_nested_select_values(nested_select_values) } if nested_select_values.present?
9
11
  end
10
12
  end
11
13
 
14
+ private
15
+ def prevent_belongs_to_foreign_key_absence!(reflection)
16
+ return unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
17
+
18
+ # ActiveRecord will not raise in case its missing, so we should prevent silent error here
19
+ if parent.nested_select_values.present? &&
20
+ !parent.nested_select_values.map(&:to_sym).include?( reflection.foreign_key.to_sym )
21
+
22
+ raise ActiveModel::MissingAttributeError, <<~ERR
23
+ Parent reflection #{parent.association} was missing foreign key #{reflection.foreign_key} in nested selection,
24
+ while trying to preload belongs_to reflection named #{reflection.name}.
25
+ Hint: didn't you forgot to add #{reflection.foreign_key} inside #{parent.nested_select_values}?
26
+ ERR
27
+ end
28
+ end
29
+
12
30
  end
13
31
  end
14
32
  end
@@ -11,7 +11,7 @@ module NestedSelect
11
11
  end
12
12
 
13
13
  def apply_nested_select_values( partial_select_values )
14
- return super unless reflection.parent_reflection.is_a?( ActiveRecord::Reflection::HasAndBelongsToManyReflection )
14
+ return super unless reflection.parent_reflection.is_a?( ActiveRecord::Reflection::HasAndBelongsToManyReflection)
15
15
 
16
16
  # when parent reflection is a HasAndBelongsToManyReflection,
17
17
  # then we don't need foreign_key to be included, as it does in super
@@ -3,14 +3,14 @@ module NestedSelect
3
3
  extend ActiveSupport::Autoload
4
4
  extend ActiveSupport::Concern
5
5
 
6
- autoload :Branch, "brest/nested_select/preloader/branch"
7
- autoload :ThroughAssociation, "brest/nested_select/preloader/through_association"
8
- autoload :Association, "brest/nested_select/preloader/association"
6
+ autoload :Branch, "nested_select/preloader/branch"
7
+ autoload :ThroughAssociation, "nested_select/preloader/through_association"
8
+ autoload :Association, "nested_select/preloader/association"
9
9
 
10
10
  included do
11
11
  ActiveRecord::Associations::Preloader::Branch.prepend(Branch)
12
- ActiveRecord::Associations::Preloader::ThroughAssociation.prepend( ThroughAssociation )
13
- ActiveRecord::Associations::Preloader::Association.prepend( Association )
12
+ ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(ThroughAssociation)
13
+ ActiveRecord::Associations::Preloader::Association.prepend(Association)
14
14
  end
15
15
  def apply_nested_select_values(nested_select_values)
16
16
  distribute_nested_select_over_loading_tree(@tree, nested_select_values)
@@ -26,8 +26,8 @@ module NestedSelect
26
26
  # it could be a case when selection tree is not that deep than Branch tree.
27
27
  return if sub_nested_select_values.blank?
28
28
 
29
- sub_tree.children.each do
30
- distribute_nested_select_over_loading_tree( _1, sub_nested_select_values[_1.association])
29
+ sub_tree.children.each do |chld_brnch|
30
+ distribute_nested_select_over_loading_tree(chld_brnch, sub_nested_select_values[chld_brnch.association])
31
31
  end
32
32
  end
33
33
  end
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
+ require_relative "deep_merger"
2
3
 
3
4
  module NestedSelect
4
5
  module Relation
6
+ using ::NestedSelect::DeepMerger
5
7
 
6
8
  attr_accessor :nested_select_values
7
9
  def select(*fields)
8
- @nested_select_values = fields.grep(Hash)
9
- super(*fields.grep_v(Hash))
10
+ # {user_profile: [:zip_code]} + {user_profile: [:bio]} -> { user_profile: [:zip_code, :bio] }
11
+ @nested_select_values = [*@nested_select_values, *fields.grep(Hash)].deep_combine_elements
12
+ fields.grep_v(Hash).present? ? super(*fields.grep_v(Hash)) : self
10
13
  end
11
14
 
12
15
  def preload_associations(records) # :nodoc:
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NestedSelect
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/nested_select.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  require_relative "nested_select/version"
4
3
 
5
4
  module NestedSelect
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.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - alekseyl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-11 00:00:00.000000000 Z
11
+ date: 2025-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -38,6 +38,146 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rails-i18n
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: stubberry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rails_sql_prettifier
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: amazing_print
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop-shopify
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
41
181
  description: ActiveRecord improved select on nested models, allows partial instantiation
42
182
  on nested models, easy one step improvements on performance and memory
43
183
  email:
@@ -49,13 +189,20 @@ files:
49
189
  - ".idea/.gitignore"
50
190
  - ".idea/inspectionProfiles/profiles_settings.xml"
51
191
  - ".idea/misc.xml"
192
+ - ".idea/modules.xml"
193
+ - ".idea/nested_select.iml"
194
+ - ".idea/vcs.xml"
52
195
  - ".rubocop.yml"
196
+ - ABOUT_NESTED_SELECT.md
53
197
  - CHANGELOG.md
54
198
  - CODE_OF_CONDUCT.md
199
+ - Dockerfile
55
200
  - LICENSE.txt
56
201
  - README.md
57
202
  - Rakefile
203
+ - docker-compose.yml
58
204
  - lib/nested_select.rb
205
+ - lib/nested_select/deep_merger.rb
59
206
  - lib/nested_select/preloader.rb
60
207
  - lib/nested_select/preloader/association.rb
61
208
  - lib/nested_select/preloader/branch.rb