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 +4 -4
- data/.idea/modules.xml +8 -0
- data/.idea/nested_select.iml +125 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +83 -0
- data/ABOUT_NESTED_SELECT.md +88 -0
- data/CHANGELOG.md +14 -1
- data/Dockerfile +12 -0
- data/README.md +124 -43
- data/docker-compose.yml +13 -0
- data/lib/nested_select/deep_merger.rb +18 -0
- data/lib/nested_select/preloader/association.rb +4 -4
- data/lib/nested_select/preloader/branch.rb +18 -0
- data/lib/nested_select/preloader/through_association.rb +1 -1
- data/lib/nested_select/preloader.rb +7 -7
- data/lib/nested_select/relation.rb +5 -2
- data/lib/nested_select/version.rb +1 -1
- data/lib/nested_select.rb +0 -1
- metadata +149 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7df4328a84a6701ef2081be1b647cd9f1c07442b6dfd48c580126daa369ef80
|
4
|
+
data.tar.gz: 17fd02a9b6f0bf767b55bae9243534030a4ccd7d524ff0c72cd3f62ab4e7b46c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
|
-
## [
|
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
data/README.md
CHANGED
@@ -1,54 +1,71 @@
|
|
1
|
-
|
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
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
14
|
+
**Single course**, a real prod set of data used by current UI (~ x33 times less RAM):
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
24
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
56
|
+
Total allocated: 2.79 MB (30702 objects)
|
57
|
+
Total retained: 2.05 MB (16431 objects)
|
42
58
|
|
43
|
-
|
59
|
+
------ Full preloading memory consumption for root_collection_size: 100 ----
|
44
60
|
|
45
|
-
|
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
|
-
|
66
|
+
```
|
48
67
|
|
49
|
-
|
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
|
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
|
-
|
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
|
-
##
|
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
|
-
|
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/
|
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
|
|
data/docker-compose.yml
ADDED
@@ -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(
|
12
|
-
|
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, "
|
7
|
-
autoload :ThroughAssociation, "
|
8
|
-
autoload :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(
|
13
|
-
ActiveRecord::Associations::Preloader::Association.prepend(
|
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(
|
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
|
-
|
9
|
-
|
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:
|
data/lib/nested_select.rb
CHANGED
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
|
+
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
|
+
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
|