occams-record 1.4.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -14
- data/lib/occams-record/eager_loaders/ad_hoc_base.rb +7 -1
- data/lib/occams-record/eager_loaders/base.rb +23 -3
- data/lib/occams-record/eager_loaders/builder.rb +6 -0
- data/lib/occams-record/eager_loaders/context.rb +1 -1
- data/lib/occams-record/eager_loaders/polymorphic_belongs_to.rb +23 -3
- data/lib/occams-record/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a367407e2af3e4110327d26438fcb2a590b7fbd34e6b60dd4bd9396665c495fe
|
4
|
+
data.tar.gz: 8cf6fb913ac787126a80fe38baff3a56940cea8afc80e1b05e3c864ae8368e11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bc45d11bc974e5a5ec6b2b9ffab8e3f069048ee8335122ad81a8b49cc852fa4bdb27116865000e4b70e4f6d617b3f870861288e00a43b273e3ec14d0dfdb6b6
|
7
|
+
data.tar.gz: af1dac7260e93ed3778ff022c3d6113819528e37d77c275b9cada6cd5e731593bbe9e4f24ae283ed572a4876672126df4cb002ab8f9c863e3de4943f95acdbc2
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@ OccamsRecord is a high-efficiency, advanced query library for use alongside Acti
|
|
9
9
|
* 3x-5x faster than ActiveRecord queries, *minimum*.
|
10
10
|
* Uses 1/3 the memory of ActiveRecord query results.
|
11
11
|
* Eliminates the N+1 query problem. (This often exceeds the baseline 3x-5x gain.)
|
12
|
-
* Support for cursors (Postgres only, new in v1.4.0
|
12
|
+
* Support for cursors (Postgres only, new in v1.4.0)
|
13
13
|
|
14
14
|
### 2) Supercharged querying & eager loading
|
15
15
|
|
@@ -20,7 +20,9 @@ Continue using ActiveRecord's query builder, but let Occams take over running th
|
|
20
20
|
```ruby
|
21
21
|
OccamsRecord
|
22
22
|
.query(User.active)
|
23
|
-
.eager_load(:orders
|
23
|
+
.eager_load(:orders) { |l|
|
24
|
+
l.scope { |q| q.where("created_at >= ?", date).order("created_at DESC") }
|
25
|
+
}
|
24
26
|
```
|
25
27
|
|
26
28
|
**Use `ORDER BY` with `find_each`/`find_in_batches`**
|
@@ -135,9 +137,9 @@ Eager loading is similiar to ActiveRecord's `preload`: each association is loade
|
|
135
137
|
OccamsRecord
|
136
138
|
.query(q)
|
137
139
|
.eager_load(:customer)
|
138
|
-
.eager_load(:line_items) {
|
139
|
-
eager_load(:product)
|
140
|
-
eager_load(:something_else)
|
140
|
+
.eager_load(:line_items) { |l|
|
141
|
+
l.eager_load(:product)
|
142
|
+
l.eager_load(:something_else)
|
141
143
|
}
|
142
144
|
.find_each { |order|
|
143
145
|
puts order.customer.name
|
@@ -158,11 +160,14 @@ orders = OccamsRecord
|
|
158
160
|
.query(q)
|
159
161
|
# Only SELECT the columns you need. Your DBA will thank you.
|
160
162
|
.eager_load(:customer, select: "id, name")
|
161
|
-
|
162
|
-
#
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
|
164
|
+
# Or use 'scope' to access the full power of ActiveRecord's query builder.
|
165
|
+
# Here, only 'active' line items will be returned, and in a specific order.
|
166
|
+
.eager_load(:line_items) { |l|
|
167
|
+
l.scope { |q| q.active.order("created_at") }
|
168
|
+
|
169
|
+
l.eager_load(:product)
|
170
|
+
l.eager_load(:something_else)
|
166
171
|
}
|
167
172
|
.run
|
168
173
|
```
|
@@ -262,9 +267,9 @@ Let's say we want to load each product with an array of all customers who've ord
|
|
262
267
|
```ruby
|
263
268
|
products_with_orders = OccamsRecord
|
264
269
|
.query(Product.all)
|
265
|
-
.eager_load(:line_items) {
|
266
|
-
eager_load(:order) {
|
267
|
-
eager_load(:customer)
|
270
|
+
.eager_load(:line_items) { |l|
|
271
|
+
l.eager_load(:order) { |l|
|
272
|
+
l.eager_load(:customer)
|
268
273
|
}
|
269
274
|
}
|
270
275
|
.map { |product|
|
@@ -354,7 +359,7 @@ bundle install
|
|
354
359
|
bundle exec rake test
|
355
360
|
|
356
361
|
# test against Postgres
|
357
|
-
TEST_DATABASE_URL=
|
362
|
+
TEST_DATABASE_URL=postgresql://postgres@localhost:5432/occams_record bundle exec rake test
|
358
363
|
|
359
364
|
# test against MySQL
|
360
365
|
TEST_DATABASE_URL=mysql2://root:@127.0.0.1:3306/occams_record bundle exec rake test
|
@@ -26,7 +26,13 @@ module OccamsRecord
|
|
26
26
|
@name, @mapping = name.to_s, mapping
|
27
27
|
@sql, @binds, @use, @model = sql, binds, use, model
|
28
28
|
@eager_loaders = EagerLoaders::Context.new(@model)
|
29
|
-
|
29
|
+
if builder
|
30
|
+
if builder.arity > 0
|
31
|
+
builder.call(self)
|
32
|
+
else
|
33
|
+
instance_exec(&builder)
|
34
|
+
end
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
38
|
#
|
@@ -19,12 +19,32 @@ module OccamsRecord
|
|
19
19
|
# @yield perform eager loading on *this* association (optional)
|
20
20
|
#
|
21
21
|
def initialize(ref, scope = nil, use: nil, as: nil, optimizer: :select, &builder)
|
22
|
-
@ref, @
|
22
|
+
@ref, @scopes, @use, @as = ref, Array(scope), use, as
|
23
23
|
@model = ref.klass
|
24
24
|
@name = (as || ref.name).to_s
|
25
25
|
@eager_loaders = EagerLoaders::Context.new(@model)
|
26
26
|
@optimizer = optimizer
|
27
|
-
|
27
|
+
if builder
|
28
|
+
if builder.arity > 0
|
29
|
+
builder.call(self)
|
30
|
+
else
|
31
|
+
instance_exec(&builder)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# An alternative to passing a "scope" lambda to the constructor. Your block is passed the query
|
38
|
+
# so you can call select, where, order, etc on it.
|
39
|
+
#
|
40
|
+
# If you call scope multiple times, the results will be additive.
|
41
|
+
#
|
42
|
+
# @yield [ActiveRecord::Relation] a relation to modify with select, where, order, etc
|
43
|
+
# @return self
|
44
|
+
#
|
45
|
+
def scope(&scope)
|
46
|
+
@scopes << scope if scope
|
47
|
+
self
|
28
48
|
end
|
29
49
|
|
30
50
|
#
|
@@ -71,7 +91,7 @@ module OccamsRecord
|
|
71
91
|
def base_scope
|
72
92
|
q = @ref.klass.all
|
73
93
|
q = q.instance_exec(&@ref.scope) if @ref.scope
|
74
|
-
q = @
|
94
|
+
q = @scopes.reduce(q) { |acc, scope| scope.(acc) }
|
75
95
|
q
|
76
96
|
end
|
77
97
|
end
|
@@ -10,6 +10,12 @@ module OccamsRecord
|
|
10
10
|
# Specify an association to be eager-loaded. For maximum memory savings, only SELECT the
|
11
11
|
# colums you actually need.
|
12
12
|
#
|
13
|
+
# If you pass a block to nest more eager loads, you may call it with one of two forms: with an argument and without:
|
14
|
+
#
|
15
|
+
# If you ommit the block argument, the "self" inside the block will be the eager loader. You can call "eager_load" and "scope" directly.
|
16
|
+
#
|
17
|
+
# If you include the block argument, the "self" inside the block is the same as the self outside the block. The argument will be the eager loader, which you can use to make additional "eager_load" or "scope" calls.
|
18
|
+
#
|
13
19
|
# @param assoc [Symbol] name of association
|
14
20
|
# @param scope [Proc] a scope to apply to the query (optional). It will be passed an
|
15
21
|
# ActiveRecord::Relation on which you may call all the normal query hethods (select, where, etc) as well as any scopes you've defined on the model.
|
@@ -115,7 +115,7 @@ module OccamsRecord
|
|
115
115
|
|
116
116
|
def build_loader!(assoc, custom_name, scope, select, use, optimizer, builder)
|
117
117
|
build_loader(assoc, custom_name, scope, select, use, optimizer, builder) ||
|
118
|
-
raise("OccamsRecord: No
|
118
|
+
raise("OccamsRecord: No association `:#{assoc}` on `#{@model.name}` or subclasses")
|
119
119
|
end
|
120
120
|
|
121
121
|
def build_loader(assoc, custom_name, scope, select, use, optimizer, builder)
|
@@ -17,12 +17,32 @@ module OccamsRecord
|
|
17
17
|
# @yield perform eager loading on *this* association (optional)
|
18
18
|
#
|
19
19
|
def initialize(ref, scope = nil, use: nil, as: nil, optimizer: nil, &builder)
|
20
|
-
@ref, @
|
20
|
+
@ref, @scopes, @use = ref, Array(scope), use
|
21
21
|
@name = (as || ref.name).to_s
|
22
22
|
@foreign_type = @ref.foreign_type.to_sym
|
23
23
|
@foreign_key = @ref.foreign_key.to_sym
|
24
24
|
@eager_loaders = EagerLoaders::Context.new(nil, polymorphic: true)
|
25
|
-
|
25
|
+
if builder
|
26
|
+
if builder.arity > 0
|
27
|
+
builder.call(self)
|
28
|
+
else
|
29
|
+
instance_exec(&builder)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# An alternative to passing a "scope" lambda to the constructor. Your block is passed the query
|
36
|
+
# so you can call select, where, order, etc on it.
|
37
|
+
#
|
38
|
+
# If you call scope multiple times, the results will be additive.
|
39
|
+
#
|
40
|
+
# @yield [ActiveRecord::Relation] a relation to modify with select, where, order, etc
|
41
|
+
# @return self
|
42
|
+
#
|
43
|
+
def scope(&scope)
|
44
|
+
@scopes << scope if scope
|
45
|
+
self
|
26
46
|
end
|
27
47
|
|
28
48
|
#
|
@@ -83,7 +103,7 @@ module OccamsRecord
|
|
83
103
|
def base_scope(model)
|
84
104
|
q = model.all
|
85
105
|
q = q.instance_exec(&@ref.scope) if @ref.scope
|
86
|
-
q = @
|
106
|
+
q = @scopes.reduce(q) { |acc, scope| scope.(acc) }
|
87
107
|
q
|
88
108
|
end
|
89
109
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: occams-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Hollinger
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -83,7 +83,7 @@ homepage: https://jhollinger.github.io/occams-record/
|
|
83
83
|
licenses:
|
84
84
|
- MIT
|
85
85
|
metadata: {}
|
86
|
-
post_install_message:
|
86
|
+
post_install_message:
|
87
87
|
rdoc_options: []
|
88
88
|
require_paths:
|
89
89
|
- lib
|
@@ -98,8 +98,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
98
|
- !ruby/object:Gem::Version
|
99
99
|
version: '0'
|
100
100
|
requirements: []
|
101
|
-
rubygems_version: 3.
|
102
|
-
signing_key:
|
101
|
+
rubygems_version: 3.4.1
|
102
|
+
signing_key:
|
103
103
|
specification_version: 4
|
104
104
|
summary: The missing high-efficiency query API for ActiveRecord
|
105
105
|
test_files: []
|