occams-record 1.1.6 → 1.3.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +79 -78
- data/lib/occams-record/eager_loaders/belongs_to.rb +1 -1
- data/lib/occams-record/eager_loaders/builder.rb +10 -10
- data/lib/occams-record/eager_loaders/through.rb +1 -1
- data/lib/occams-record/measureable.rb +6 -6
- data/lib/occams-record/query.rb +6 -6
- data/lib/occams-record/raw_query.rb +21 -10
- data/lib/occams-record/results/results.rb +14 -8
- data/lib/occams-record/results/row.rb +1 -1
- data/lib/occams-record/ugly.rb +4 -4
- data/lib/occams-record/version.rb +1 -1
- metadata +24 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd827ce90c189d89558863ece800e233c3296f590f18e6b756d4f4983c673e7e
|
4
|
+
data.tar.gz: 0bfd98422a9e2a313aae5faef338e442979235377ba1ddad9f5b38e2ccee1b27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00352ecd359c2cd617f29d225aa3a566eed578a81f875714896b0e380931c9e5611b87971f2e68197225a59ebab5e7cefb96f65f4855f08481ca38b02d9f9c8c
|
7
|
+
data.tar.gz: 6f82a98e4790c92d86d44f4ea2e4babd8dde300a08a60106d8e1f8b0e1e0210351ff89915d1f56e44160779b358df8e8d4355ab69f14f81ee23d91e13f033f71
|
data/README.md
CHANGED
@@ -12,22 +12,22 @@ OccamsRecord is a high-efficiency, advanced query library for use alongside Acti
|
|
12
12
|
|
13
13
|
### 2) Supercharged querying & eager loading
|
14
14
|
|
15
|
-
Continue using ActiveRecord's query builder, but let Occams take over eager loading and raw SQL calls. None of the examples below are possible with ActiveRecord, but OccamsRecord
|
15
|
+
Continue using ActiveRecord's query builder, but let Occams take over running them, eager loading, and raw SQL calls. None of the examples below are possible with ActiveRecord, but OccamsRecord makes them trivial. (More complete examples are shown later, but these should whet your appetite.)
|
16
16
|
|
17
17
|
**Customize the SQL used to eager load associations**
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
OccamsRecord
|
21
|
-
query(User.active)
|
22
|
-
eager_load(:orders, ->(q) { q.where("created_at >= ?", date })
|
20
|
+
OccamsRecord
|
21
|
+
.query(User.active)
|
22
|
+
.eager_load(:orders, ->(q) { q.where("created_at >= ?", date).order("created_at DESC") })
|
23
23
|
```
|
24
24
|
|
25
25
|
**Use `ORDER BY` with `find_each`/`find_in_batches`**
|
26
26
|
|
27
27
|
```ruby
|
28
|
-
OccamsRecord
|
29
|
-
query(Order.order("created_at DESC"))
|
30
|
-
find_each { |order|
|
28
|
+
OccamsRecord
|
29
|
+
.query(Order.order("created_at DESC"))
|
30
|
+
.find_each { |order|
|
31
31
|
...
|
32
32
|
}
|
33
33
|
```
|
@@ -35,15 +35,15 @@ OccamsRecord.
|
|
35
35
|
**Use `find_each`/`find_in_batches` with raw SQL**
|
36
36
|
|
37
37
|
```ruby
|
38
|
-
OccamsRecord
|
39
|
-
sql("
|
38
|
+
OccamsRecord
|
39
|
+
.sql("
|
40
40
|
SELECT * FROM orders
|
41
41
|
WHERE created_at >= %{date}
|
42
42
|
LIMIT %{batch_limit}
|
43
43
|
OFFSET %{batch_offset}",
|
44
44
|
{date: 10.years.ago}
|
45
|
-
)
|
46
|
-
find_each { |order|
|
45
|
+
)
|
46
|
+
.find_each { |order|
|
47
47
|
...
|
48
48
|
}
|
49
49
|
```
|
@@ -51,13 +51,13 @@ OccamsRecord.
|
|
51
51
|
**Eager load associations when you're writing raw SQL**
|
52
52
|
|
53
53
|
```ruby
|
54
|
-
OccamsRecord
|
55
|
-
sql("
|
54
|
+
OccamsRecord
|
55
|
+
.sql("
|
56
56
|
SELECT * FROM users
|
57
57
|
LEFT OUTER JOIN ...
|
58
|
-
")
|
59
|
-
model(User)
|
60
|
-
eager_load(:orders)
|
58
|
+
")
|
59
|
+
.model(User)
|
60
|
+
.eager_load(:orders)
|
61
61
|
```
|
62
62
|
|
63
63
|
**Eager load "ad hoc associations" using raw SQL**
|
@@ -66,9 +66,9 @@ Relationships are complicated, and sometimes they can't be expressed in ActiveRe
|
|
66
66
|
(Don't worry, there's a full explanation later on.)
|
67
67
|
|
68
68
|
```ruby
|
69
|
-
OccamsRecord
|
70
|
-
query(User.all)
|
71
|
-
eager_load_many(:orders, {:id => :user_id}, "
|
69
|
+
OccamsRecord
|
70
|
+
.query(User.all)
|
71
|
+
.eager_load_many(:orders, {:id => :user_id}, "
|
72
72
|
SELECT user_id, orders.*
|
73
73
|
FROM orders INNER JOIN ...
|
74
74
|
WHERE user_id IN (%{ids})
|
@@ -97,17 +97,19 @@ gem 'occams-record'
|
|
97
97
|
|
98
98
|
Full documentation is available at [rubydoc.info/gems/occams-record](http://www.rubydoc.info/gems/occams-record).
|
99
99
|
|
100
|
+
Code lives at at [github.com/jhollinger/occams-record](https://github.com/jhollinger/occams-record). Contributions welcome!
|
101
|
+
|
100
102
|
Build your queries like normal, using ActiveRecord's excellent query builder. Then pass them off to Occams Record.
|
101
103
|
|
102
104
|
```ruby
|
103
|
-
q = Order
|
104
|
-
completed
|
105
|
-
where("order_date > ?", 30.days.ago)
|
106
|
-
order("order_date DESC")
|
107
|
-
|
108
|
-
orders = OccamsRecord
|
109
|
-
query(q)
|
110
|
-
run
|
105
|
+
q = Order
|
106
|
+
.completed
|
107
|
+
.where("order_date > ?", 30.days.ago)
|
108
|
+
.order("order_date DESC")
|
109
|
+
|
110
|
+
orders = OccamsRecord
|
111
|
+
.query(q)
|
112
|
+
.run
|
111
113
|
````
|
112
114
|
|
113
115
|
`each`, `map`, `reduce`, and other Enumerable methods may be used instead of *run*. `find_each` and `find_in_batches` are also supported, and unlike in ActiveRecord, `ORDER BY` works as you'd expect.
|
@@ -119,14 +121,14 @@ Occams Record has great support for raw SQL queries too, but we'll get to those
|
|
119
121
|
Eager loading is similiar to ActiveRecord's `preload`: each association is loaded in a separate query. Unlike ActiveRecord, nested associations use blocks instead of Hashes. More importantly, if you try to use an association you didn't eager load *an exception will be raised*. In other words, the N+1 query problem simply doesn't exist.
|
120
122
|
|
121
123
|
```ruby
|
122
|
-
OccamsRecord
|
123
|
-
query(q)
|
124
|
-
eager_load(:customer)
|
125
|
-
eager_load(:line_items) {
|
124
|
+
OccamsRecord
|
125
|
+
.query(q)
|
126
|
+
.eager_load(:customer)
|
127
|
+
.eager_load(:line_items) {
|
126
128
|
eager_load(:product)
|
127
129
|
eager_load(:something_else)
|
128
|
-
}
|
129
|
-
find_each { |order|
|
130
|
+
}
|
131
|
+
.find_each { |order|
|
130
132
|
puts order.customer.name
|
131
133
|
order.line_items.each { |line_item|
|
132
134
|
puts line_item.product.name
|
@@ -141,17 +143,17 @@ OccamsRecord.
|
|
141
143
|
Occams Record allows you to tweak the SQL of any eager load. Pull back only the columns you need, change the order, add a `WHERE` clause, etc.
|
142
144
|
|
143
145
|
```ruby
|
144
|
-
orders = OccamsRecord
|
145
|
-
query(q)
|
146
|
+
orders = OccamsRecord
|
147
|
+
.query(q)
|
146
148
|
# Only SELECT the columns you need. Your DBA will thank you.
|
147
|
-
eager_load(:customer, select: "id, name")
|
149
|
+
.eager_load(:customer, select: "id, name")
|
148
150
|
|
149
151
|
# A Proc can use ActiveRecord's query builder
|
150
|
-
eager_load(:line_items, ->(q) { q.active.order("created_at") }) {
|
152
|
+
.eager_load(:line_items, ->(q) { q.active.order("created_at") }) {
|
151
153
|
eager_load(:product)
|
152
154
|
eager_load(:something_else)
|
153
|
-
}
|
154
|
-
run
|
155
|
+
}
|
156
|
+
.run
|
155
157
|
```
|
156
158
|
|
157
159
|
Occams Record also supports loading ad hoc associations using raw SQL. We'll get to that in the next section.
|
@@ -165,8 +167,8 @@ ActiveRecord has raw SQL escape hatches like `find_by_sql` and `exec_query`, but
|
|
165
167
|
To use `find_each`/`find_in_batches` you must provide the limit and offset statements yourself; Occams will provide the values. Also, notice that the binding syntax is a bit different (it uses Ruby's built-in named string substitution).
|
166
168
|
|
167
169
|
```ruby
|
168
|
-
OccamsRecord
|
169
|
-
sql("
|
170
|
+
OccamsRecord
|
171
|
+
.sql("
|
170
172
|
SELECT * FROM orders
|
171
173
|
WHERE order_date > %{date}
|
172
174
|
ORDER BY order_date DESC, id
|
@@ -174,8 +176,8 @@ OccamsRecord.
|
|
174
176
|
OFFSET %{batch_offset}
|
175
177
|
", {
|
176
178
|
date: 10.years.ago
|
177
|
-
})
|
178
|
-
find_each(batch_size: 1000) do |order|
|
179
|
+
})
|
180
|
+
.find_each(batch_size: 1000) do |order|
|
179
181
|
...
|
180
182
|
end
|
181
183
|
```
|
@@ -185,17 +187,17 @@ OccamsRecord.
|
|
185
187
|
To use `eager_load` with a raw SQL query you must tell Occams what the base model is. (That doesn't apply if you're loading an ad hoc, raw SQL association. We'll get to those next.)
|
186
188
|
|
187
189
|
```ruby
|
188
|
-
orders = OccamsRecord
|
189
|
-
sql("
|
190
|
+
orders = OccamsRecord
|
191
|
+
.sql("
|
190
192
|
SELECT * FROM orders
|
191
193
|
WHERE order_date > %{date}
|
192
194
|
ORDER BY order_date DESC, id
|
193
195
|
", {
|
194
196
|
date: 30.days.ago
|
195
|
-
})
|
196
|
-
model(Order)
|
197
|
-
eager_load(:customer)
|
198
|
-
run
|
197
|
+
})
|
198
|
+
.model(Order)
|
199
|
+
.eager_load(:customer)
|
200
|
+
.run
|
199
201
|
```
|
200
202
|
|
201
203
|
## Raw SQL eager loading
|
@@ -203,14 +205,14 @@ orders = OccamsRecord.
|
|
203
205
|
Let's say we want to load each product with an array of all customers who've ordered it. We *could* do that by loading various nested associations:
|
204
206
|
|
205
207
|
```ruby
|
206
|
-
products_with_orders = OccamsRecord
|
207
|
-
query(Product.all)
|
208
|
-
eager_load(:line_items) {
|
208
|
+
products_with_orders = OccamsRecord
|
209
|
+
.query(Product.all)
|
210
|
+
.eager_load(:line_items) {
|
209
211
|
eager_load(:order) {
|
210
212
|
eager_load(:customer)
|
211
213
|
}
|
212
|
-
}
|
213
|
-
map { |product|
|
214
|
+
}
|
215
|
+
.map { |product|
|
214
216
|
customers = product.line_items.map(&:order).map(&:customer).uniq
|
215
217
|
[product, customers]
|
216
218
|
}
|
@@ -219,9 +221,9 @@ products_with_orders = OccamsRecord.
|
|
219
221
|
But that's very wasteful. Occams gives us better options: `eager_load_many` and `eager_load_one`.
|
220
222
|
|
221
223
|
```ruby
|
222
|
-
products = OccamsRecord
|
223
|
-
query(Product.all)
|
224
|
-
eager_load_many(:customers, {:id => :product_id}, "
|
224
|
+
products = OccamsRecord
|
225
|
+
.query(Product.all)
|
226
|
+
.eager_load_many(:customers, {:id => :product_id}, "
|
225
227
|
SELECT DISTINCT product_id, customers.*
|
226
228
|
FROM line_items
|
227
229
|
INNER JOIN orders ON line_items.order_id = orders.id
|
@@ -229,8 +231,8 @@ products = OccamsRecord.
|
|
229
231
|
WHERE line_items.product_id IN (%{ids})
|
230
232
|
", binds: {
|
231
233
|
# additional bind values (ids will be passed in for you)
|
232
|
-
})
|
233
|
-
run
|
234
|
+
})
|
235
|
+
.run
|
234
236
|
```
|
235
237
|
|
236
238
|
`eager_load_many` is declaring an ad hoc *has_many* association called *customers*. The `{:id => :product_id}` Hash defines the mapping: *id* in the parent record maps to *product_id* in the child records.
|
@@ -256,12 +258,12 @@ module MyProductMethods
|
|
256
258
|
end
|
257
259
|
end
|
258
260
|
|
259
|
-
orders = OccamsRecord
|
260
|
-
query(Order.all, use: MyOrderMethods)
|
261
|
-
eager_load(:line_items) {
|
261
|
+
orders = OccamsRecord
|
262
|
+
.query(Order.all, use: MyOrderMethods)
|
263
|
+
.eager_load(:line_items) {
|
262
264
|
eager_load(:product, use: [MyProductMethods, OtherMethods])
|
263
|
-
}
|
264
|
-
run
|
265
|
+
}
|
266
|
+
.run
|
265
267
|
```
|
266
268
|
|
267
269
|
---
|
@@ -290,30 +292,29 @@ On the other hand, Active Record makes it *very* easy to forget to eager load as
|
|
290
292
|
|
291
293
|
# Testing
|
292
294
|
|
293
|
-
To run the tests, simply run:
|
294
|
-
|
295
295
|
```bash
|
296
296
|
bundle install
|
297
|
+
|
298
|
+
# test against SQLite
|
297
299
|
bundle exec rake test
|
298
|
-
```
|
299
300
|
|
300
|
-
|
301
|
+
# test against Postgres
|
302
|
+
TEST_DATABASE_URL=postgres://postgres@localhost:5432/occams_record bundle exec rake test
|
303
|
+
```
|
301
304
|
|
302
|
-
|
305
|
+
**Test against all supported ActiveRecord versions**
|
303
306
|
|
304
307
|
```bash
|
305
|
-
|
306
|
-
AR=5.2 bundle exec rake test
|
307
|
-
```
|
308
|
+
bundle exec appraisal install
|
308
309
|
|
309
|
-
|
310
|
+
# test against all supported AR versions (defaults to SQLite)
|
311
|
+
bundle exec appraisal rake test
|
310
312
|
|
311
|
-
|
313
|
+
# test against a specific AR version
|
314
|
+
bundle exec appraisal ar-7.0 rake test
|
312
315
|
|
313
|
-
|
314
|
-
|
315
|
-
```bash
|
316
|
-
TEST_DATABASE_URL=postgres://postgres@localhost:5432/occams_record bundle exec rake test
|
316
|
+
# test against Postgres
|
317
|
+
TEST_DATABASE_URL=postgres://postgres@localhost:5432/occams_record bundle exec appraisal rake test
|
317
318
|
```
|
318
319
|
|
319
320
|
# License
|
@@ -34,7 +34,7 @@ module OccamsRecord
|
|
34
34
|
#
|
35
35
|
def merge!(assoc_rows, rows)
|
36
36
|
Merge.new(rows, name).
|
37
|
-
single!(assoc_rows, {@ref.foreign_key.to_s => @ref.
|
37
|
+
single!(assoc_rows, {@ref.foreign_key.to_s => @ref.association_primary_key.to_s})
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -56,14 +56,14 @@ module OccamsRecord
|
|
56
56
|
#
|
57
57
|
# The %{category_ids} bind param will be provided for you, and in this case will be all the category_id values from the Widget query.
|
58
58
|
#
|
59
|
-
# res = OccamsRecord
|
60
|
-
# query(Widget.order("name"))
|
61
|
-
# eager_load_one(:category, {:category_id => :id}, "
|
59
|
+
# res = OccamsRecord
|
60
|
+
# .query(Widget.order("name"))
|
61
|
+
# .eager_load_one(:category, {:category_id => :id}, "
|
62
62
|
# SELECT * FROM categories WHERE id IN (%{category_ids}) AND name != %{bad_name}
|
63
63
|
# ", binds: {
|
64
64
|
# bad_name: "Bad Category"
|
65
|
-
# })
|
66
|
-
# run
|
65
|
+
# })
|
66
|
+
# .run
|
67
67
|
#
|
68
68
|
# @param name [Symbol] name of attribute to load records into
|
69
69
|
# @param mapping [Hash] a Hash that defines the key mapping of the parent (widgets.category_id) to the child (categories.id).
|
@@ -89,14 +89,14 @@ module OccamsRecord
|
|
89
89
|
# The %{ids} bind param will be provided for you, and in this case will be all the id values from the Widget
|
90
90
|
# query.
|
91
91
|
#
|
92
|
-
# res = OccamsRecord
|
93
|
-
# query(Widget.order("name"))
|
94
|
-
# eager_load_many(:parts, {:id => :widget_id}, "
|
92
|
+
# res = OccamsRecord
|
93
|
+
# .query(Widget.order("name"))
|
94
|
+
# .eager_load_many(:parts, {:id => :widget_id}, "
|
95
95
|
# SELECT * FROM parts WHERE widget_id IN (%{ids}) AND sku NOT IN (%{bad_skus})
|
96
96
|
# ", binds: {
|
97
97
|
# bad_skus: ["G90023ASDf0"]
|
98
|
-
# })
|
99
|
-
# run
|
98
|
+
# })
|
99
|
+
# .run
|
100
100
|
#
|
101
101
|
# @param name [Symbol] name of attribute to load records into
|
102
102
|
# @param mapping [Hash] a Hash that defines the key mapping of the parent (widgets.id) to the children (parts.widget_id).
|
@@ -9,7 +9,7 @@ module OccamsRecord
|
|
9
9
|
#
|
10
10
|
# See documentation for OccamsRecord::EagerLoaders::Base.
|
11
11
|
#
|
12
|
-
def initialize(
|
12
|
+
def initialize(ref, scope = nil, use: nil, as: nil, optimizer: :select, &builder)
|
13
13
|
super
|
14
14
|
|
15
15
|
unless @ref.macro == :has_one or @ref.macro == :has_many
|
@@ -5,18 +5,18 @@ module OccamsRecord
|
|
5
5
|
#
|
6
6
|
# Measure the time each query takes. Useful for figuring out which query is the slow one when you're doing a bunch of eager loads.
|
7
7
|
#
|
8
|
-
# orders = OccamsRecord
|
9
|
-
# query(Order.all)
|
10
|
-
# eager_load(:customer)
|
8
|
+
# orders = OccamsRecord
|
9
|
+
# .query(Order.all)
|
10
|
+
# .eager_load(:customer)
|
11
11
|
# ...
|
12
|
-
# measure { |x|
|
12
|
+
# .measure { |x|
|
13
13
|
# puts "Total time: #{x.total_time} sec"
|
14
14
|
# x.queries.each { |q|
|
15
15
|
# puts "Table: #{q.table_name} (#{q.time} sec)"
|
16
16
|
# puts q.sql
|
17
17
|
# }
|
18
|
-
# }
|
19
|
-
# run
|
18
|
+
# }
|
19
|
+
# .run
|
20
20
|
#
|
21
21
|
module Measureable
|
22
22
|
#
|
data/lib/occams-record/query.rb
CHANGED
@@ -7,15 +7,15 @@ module OccamsRecord
|
|
7
7
|
# Instead use OccamsRecord::Query#eager_load. Finally, call `run` (or any Enumerable method) to run
|
8
8
|
# the query and get back an array of objects.
|
9
9
|
#
|
10
|
-
# results = OccamsRecord
|
11
|
-
# query(Widget.order("name"))
|
12
|
-
# eager_load(:category)
|
13
|
-
# eager_load(:order_items, ->(q) { q.select("widget_id, order_id") }) {
|
10
|
+
# results = OccamsRecord
|
11
|
+
# .query(Widget.order("name"))
|
12
|
+
# .eager_load(:category)
|
13
|
+
# .eager_load(:order_items, ->(q) { q.select("widget_id, order_id") }) {
|
14
14
|
# eager_load(:orders) {
|
15
15
|
# eager_load(:customer, ->(q) { q.select("name") })
|
16
16
|
# }
|
17
|
-
# }
|
18
|
-
# run
|
17
|
+
# }
|
18
|
+
# .run
|
19
19
|
#
|
20
20
|
# @param scope [ActiveRecord::Relation]
|
21
21
|
# @param use [Module] optional Module to include in the result class
|
@@ -4,7 +4,7 @@ module OccamsRecord
|
|
4
4
|
# a Hash of binds. While this doesn't offer an additional performance boost, it's a nice way to
|
5
5
|
# write safe, complicated SQL by hand while also supporting eager loading.
|
6
6
|
#
|
7
|
-
# results = OccamsRecord.sql(
|
7
|
+
# results = OccamsRecord.sql("
|
8
8
|
# SELECT * FROM widgets
|
9
9
|
# WHERE category_id = %{cat_id}
|
10
10
|
# ", {
|
@@ -13,22 +13,33 @@ module OccamsRecord
|
|
13
13
|
#
|
14
14
|
# If you want to do eager loading, you must first the define a model to pull the associations from (unless
|
15
15
|
# you're using the raw SQL eager loaders `eager_load_one` or `eager_load_many`).
|
16
|
-
# NOTE If you're using SQLite, you must *always* specify the model.
|
17
16
|
#
|
18
|
-
# results = OccamsRecord
|
19
|
-
# sql("
|
17
|
+
# results = OccamsRecord
|
18
|
+
# .sql("
|
20
19
|
# SELECT * FROM widgets
|
21
20
|
# WHERE category_id IN (%{cat_ids})
|
22
21
|
# ", {
|
23
22
|
# cat_ids: [5, 10]
|
24
|
-
# })
|
25
|
-
# model(Widget)
|
26
|
-
# eager_load(:category)
|
27
|
-
# run
|
23
|
+
# })
|
24
|
+
# .model(Widget)
|
25
|
+
# .eager_load(:category)
|
26
|
+
# .run
|
28
27
|
#
|
29
28
|
# NOTE To use find_each/find_in_batches, your SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}',
|
30
|
-
# and an ORDER BY is strongly recomended.
|
31
|
-
#
|
29
|
+
# and an ORDER BY is strongly recomended. OccamsRecord will provide the bind values for you.
|
30
|
+
#
|
31
|
+
# NOTE There is variation of the types of values returned (e.g. a Date object vs a date string) depending on the database
|
32
|
+
# and ActiveRecord version being used:
|
33
|
+
#
|
34
|
+
# Postgres always returns native Ruby types.
|
35
|
+
#
|
36
|
+
# SQLite will return native types for the following: integers, floats, string/text.
|
37
|
+
# For booleans it will return 0|1 for AR 6+, and "t|f" for AR 5-.
|
38
|
+
# Dates and times will be ISO8601 formatted strings.
|
39
|
+
# It is possible to coerce the SQLite adapter into returning native types for everything IF they're columns of a table
|
40
|
+
# that you have an AR model for. e.g. if you're selecting from the widgets, table: `OccamsRecord.sql("...").model(Widget)...`.
|
41
|
+
#
|
42
|
+
# MySQL ?
|
32
43
|
#
|
33
44
|
# @param sql [String] The SELECT statement to run. Binds should use Ruby's named string substitution.
|
34
45
|
# @param binds [Hash] Bind values (Symbol keys)
|
@@ -4,7 +4,7 @@ module OccamsRecord
|
|
4
4
|
# ActiveRecord's internal type casting API changes from version to version.
|
5
5
|
CASTER = case ActiveRecord::VERSION::MAJOR
|
6
6
|
when 4 then :type_cast_from_database
|
7
|
-
when 5, 6 then :deserialize
|
7
|
+
when 5, 6, 7 then :deserialize
|
8
8
|
else raise "OccamsRecord::Results::CASTER does yet support this version of ActiveRecord"
|
9
9
|
end
|
10
10
|
|
@@ -36,14 +36,20 @@ module OccamsRecord
|
|
36
36
|
attr_accessor(*association_names)
|
37
37
|
|
38
38
|
# Build a getter for each attribute returned by the query. The values will be type converted on demand.
|
39
|
-
model_column_types = model ? model.attributes_builder.types :
|
39
|
+
model_column_types = model ? model.attributes_builder.types : nil
|
40
40
|
self.columns.each_with_index do |col, idx|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
#
|
42
|
+
# NOTE there's lots of variation between DB adapters and AR versions here. Some notes:
|
43
|
+
# * Postgres AR < 6.1 `column_types` will contain entries for every column.
|
44
|
+
# * Postgres AR >= 6.1 `column_types` only contains entries for "exotic" types. Columns with "common" types have already been converted by the PG adapter.
|
45
|
+
# * SQLite `column_types` will always be empty. Some types will have already been convered by the SQLite adapter, but others will depend on
|
46
|
+
# `model_column_types` for converstion. See test/raw_query_test.rb#test_common_types for examples.
|
47
|
+
# * MySQL ?
|
48
|
+
#
|
49
|
+
type = column_types[col] || model_column_types&.[](col)
|
50
|
+
case type&.type
|
51
|
+
when nil
|
52
|
+
define_method(col) { @raw_values[idx] }
|
47
53
|
when :datetime
|
48
54
|
define_method(col) { @cast_values[idx] ||= type.send(CASTER, @raw_values[idx])&.in_time_zone }
|
49
55
|
when :boolean
|
@@ -152,7 +152,7 @@ module OccamsRecord
|
|
152
152
|
def define_ids_reader!(assoc)
|
153
153
|
model = self.class._model
|
154
154
|
ref = model.reflections[assoc]
|
155
|
-
pkey = ref.
|
155
|
+
pkey = ref.klass.primary_key.to_sym
|
156
156
|
|
157
157
|
self.class.class_eval do
|
158
158
|
define_method "#{assoc.singularize}_ids" do
|
data/lib/occams-record/ugly.rb
CHANGED
@@ -8,10 +8,10 @@ module OccamsRecord
|
|
8
8
|
# The ONLY reason to use this is if you absolutely need ActiveRecord objects but still want to use Occams's
|
9
9
|
# more advanced eager loading or find_each/find_in_batches features.
|
10
10
|
#
|
11
|
-
# OccamsRecord
|
12
|
-
# query(Order.order("created_at DESC"))
|
13
|
-
# eager_load(:line_items, ->(q) { q.order("price") })
|
14
|
-
# find_each do |o|
|
11
|
+
# OccamsRecord
|
12
|
+
# .query(Order.order("created_at DESC"))
|
13
|
+
# .eager_load(:line_items, ->(q) { q.order("price") })
|
14
|
+
# .find_each do |o|
|
15
15
|
# order = OccamsRecord::Ugly.active_record(o)
|
16
16
|
# ...
|
17
17
|
# 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.3.0.rc1
|
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: 2021-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '4.2'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '7.1'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,21 @@ dependencies:
|
|
29
29
|
version: '4.2'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '7.1'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: appraisal
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
33
47
|
description: A faster, lower-memory querying API for ActiveRecord that returns results
|
34
48
|
as unadorned, read-only objects.
|
35
49
|
email: jordan.hollinger@gmail.com
|
@@ -66,7 +80,7 @@ homepage: https://jhollinger.github.io/occams-record/
|
|
66
80
|
licenses:
|
67
81
|
- MIT
|
68
82
|
metadata: {}
|
69
|
-
post_install_message:
|
83
|
+
post_install_message:
|
70
84
|
rdoc_options: []
|
71
85
|
require_paths:
|
72
86
|
- lib
|
@@ -77,12 +91,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
77
91
|
version: 2.3.0
|
78
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - ">"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
96
|
+
version: 1.3.1
|
83
97
|
requirements: []
|
84
|
-
rubygems_version: 3.
|
85
|
-
signing_key:
|
98
|
+
rubygems_version: 3.1.6
|
99
|
+
signing_key:
|
86
100
|
specification_version: 4
|
87
101
|
summary: The missing high-efficiency query API for ActiveRecord
|
88
102
|
test_files: []
|