occams-record 1.7.1 → 1.8.1
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/README.md +33 -26
- data/lib/occams-record/pluck.rb +25 -5
- data/lib/occams-record/query.rb +1 -1
- data/lib/occams-record/raw_query.rb +1 -1
- data/lib/occams-record/results/results.rb +13 -26
- data/lib/occams-record/results/row.rb +1 -1
- data/lib/occams-record/type_caster.rb +60 -0
- data/lib/occams-record/version.rb +1 -1
- data/lib/occams-record.rb +1 -0
- metadata +3 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2778b16aee6b725c967c4877b9905eed75b37b64bf8c5fca7077918ea2a4a53a
|
|
4
|
+
data.tar.gz: b8948ddd57573efcbeb90e96235626e482cc557e9b92d4e5fdf0237c182e39df
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e436f30e97f0a256d146539902b3f6fe9b54c241d7b49a51da63242f59505793c14f6ceef601343660750d0030ddf21c196e40a4aec41b1b5f1490b71b255ca6
|
|
7
|
+
data.tar.gz: 14095b912856efe2a097471046e518c0d3040320e72198ae28cf8b76c84040ca3319f16916ef2c70c357d8661e6465a36d7b80c49109896c828a2cc36dd777ee
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Occams Record
|
|
1
|
+
# Occams Record
|
|
2
2
|
|
|
3
3
|
> Do not multiply entities beyond necessity. -- Occam's Razor
|
|
4
4
|
|
|
@@ -284,7 +284,6 @@ The following ActiveRecord features are under consideration, but not high priori
|
|
|
284
284
|
The following ActiveRecord features are not supported, and likely never will be. Pull requests are still welcome, though.
|
|
285
285
|
|
|
286
286
|
* Eager loading `through` associations that involve a polymorphic association.
|
|
287
|
-
* ActiveRecord enum types
|
|
288
287
|
* ActiveRecord serialized types
|
|
289
288
|
|
|
290
289
|
---
|
|
@@ -299,32 +298,40 @@ On the other hand, Active Record makes it *very* easy to forget to eager load as
|
|
|
299
298
|
|
|
300
299
|
# Testing
|
|
301
300
|
|
|
302
|
-
|
|
303
|
-
bundle install
|
|
304
|
-
|
|
305
|
-
# test against SQLite
|
|
306
|
-
bundle exec rake test
|
|
307
|
-
|
|
308
|
-
# test against Postgres
|
|
309
|
-
TEST_DATABASE_URL=postgresql://postgres@localhost:5432/occams_record bundle exec rake test
|
|
310
|
-
|
|
311
|
-
# test against MySQL
|
|
312
|
-
TEST_DATABASE_URL=mysql2://root:@127.0.0.1:3306/occams_record bundle exec rake test
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
**Test against all supported ActiveRecord versions**
|
|
301
|
+
Tests are run with `appraisal` in Docker Compose using the `bin/test` or `bin/testall` scripts.
|
|
316
302
|
|
|
317
303
|
```bash
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
304
|
+
# Run tests against all supported ActiveRecord versions, Ruby versions, and databases
|
|
305
|
+
bin/testall
|
|
306
|
+
|
|
307
|
+
# Run tests for Ruby vX only
|
|
308
|
+
bin/testall ruby-3.1
|
|
309
|
+
|
|
310
|
+
# Run tests for ActiveRecord vX only
|
|
311
|
+
bin/testall ar-6.1
|
|
312
|
+
|
|
313
|
+
# Run tests against a specific database
|
|
314
|
+
bin/testall sqlite|postgres-14|mysql-8
|
|
315
|
+
|
|
316
|
+
# Run exactly one set of tests
|
|
317
|
+
bin/test ruby-3.1 ar-7.0 postgres-14
|
|
318
|
+
|
|
319
|
+
# If all tests complete successfully, you'll be rewarded by an ASCII Nyancat!
|
|
320
|
+
|
|
321
|
+
+ o + o
|
|
322
|
+
+ o + +
|
|
323
|
+
o +
|
|
324
|
+
o + + +
|
|
325
|
+
+ o o + o
|
|
326
|
+
-_-_-_-_-_-_-_,------, o
|
|
327
|
+
_-_-_-_-_-_-_-| /\_/\
|
|
328
|
+
-_-_-_-_-_-_-~|__( ^ .^) + +
|
|
329
|
+
_-_-_-_-_-_-_-"" ""
|
|
330
|
+
+ o o + o
|
|
331
|
+
+ +
|
|
332
|
+
o o o o +
|
|
333
|
+
o +
|
|
334
|
+
+ + o o +
|
|
328
335
|
```
|
|
329
336
|
|
|
330
337
|
# License
|
data/lib/occams-record/pluck.rb
CHANGED
|
@@ -2,14 +2,34 @@ module OccamsRecord
|
|
|
2
2
|
module Pluck
|
|
3
3
|
private
|
|
4
4
|
|
|
5
|
-
def pluck_results(results, cols)
|
|
5
|
+
def pluck_results(results, cols, model: nil)
|
|
6
6
|
if cols.size == 1
|
|
7
|
-
|
|
8
|
-
results.map { |r| r[col] }
|
|
7
|
+
pluck_results_single(results, cols[0].to_s, model: model)
|
|
9
8
|
else
|
|
10
|
-
|
|
11
|
-
results.map { |r| r.values_at(*cols) }
|
|
9
|
+
pluck_results_multi(results, cols.map(&:to_s), model: model)
|
|
12
10
|
end
|
|
13
11
|
end
|
|
12
|
+
|
|
13
|
+
# returns an array of values
|
|
14
|
+
def pluck_results_single(results, col, model: nil)
|
|
15
|
+
casters = TypeCaster.generate(results.columns, results.column_types, model: model)
|
|
16
|
+
col = results.columns[0]
|
|
17
|
+
caster = casters[col]
|
|
18
|
+
results.map { |row|
|
|
19
|
+
val = row[col]
|
|
20
|
+
caster ? caster.(val) : val
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# returns an array of arrays
|
|
25
|
+
def pluck_results_multi(results, cols, model: nil)
|
|
26
|
+
casters = TypeCaster.generate(results.columns, results.column_types, model: model)
|
|
27
|
+
results.map { |row|
|
|
28
|
+
row.map { |col, val|
|
|
29
|
+
caster = casters[col]
|
|
30
|
+
caster ? caster.(val) : val
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
end
|
|
14
34
|
end
|
|
15
35
|
end
|
data/lib/occams-record/query.rb
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
module OccamsRecord
|
|
2
2
|
# Classes and methods for handing query results.
|
|
3
3
|
module Results
|
|
4
|
-
# ActiveRecord's internal type casting API changes from version to version.
|
|
5
|
-
CASTER = case ActiveRecord::VERSION::MAJOR
|
|
6
|
-
when 4 then :type_cast_from_database
|
|
7
|
-
when 5, 6, 7 then :deserialize
|
|
8
|
-
else raise "OccamsRecord::Results::CASTER does yet support this version of ActiveRecord"
|
|
9
|
-
end
|
|
10
|
-
|
|
11
4
|
#
|
|
12
5
|
# Dynamically build a class for a specific set of result rows. It inherits from OccamsRecord::Results::Row, and optionall prepends
|
|
13
6
|
# user-defined modules.
|
|
@@ -43,28 +36,22 @@ module OccamsRecord
|
|
|
43
36
|
attr_accessor(*association_names)
|
|
44
37
|
|
|
45
38
|
# Build a getter for each attribute returned by the query. The values will be type converted on demand.
|
|
46
|
-
|
|
39
|
+
casters = TypeCaster.generate(column_names, column_types, model: model)
|
|
47
40
|
self.columns.each_with_index do |col, idx|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
#
|
|
56
|
-
type = column_types[col] || model_column_types&.[](col)
|
|
57
|
-
case type&.type
|
|
58
|
-
when nil
|
|
59
|
-
define_method(col) { @raw_values[idx] }
|
|
60
|
-
when :datetime
|
|
61
|
-
define_method(col) { @cast_values[idx] ||= type.send(CASTER, @raw_values[idx])&.in_time_zone }
|
|
62
|
-
when :boolean
|
|
63
|
-
define_method(col) { @cast_values[idx] ||= type.send(CASTER, @raw_values[idx]) }
|
|
64
|
-
define_method("#{col}?") { !!send(col) }
|
|
41
|
+
caster = casters[col]
|
|
42
|
+
|
|
43
|
+
if caster
|
|
44
|
+
define_method(col) {
|
|
45
|
+
@cast_values[idx] = caster.(@raw_values[idx]) if @cast_values[idx].nil?
|
|
46
|
+
@cast_values[idx]
|
|
47
|
+
}
|
|
65
48
|
else
|
|
66
|
-
define_method(col) {
|
|
49
|
+
define_method(col) {
|
|
50
|
+
@raw_values[idx]
|
|
51
|
+
}
|
|
67
52
|
end
|
|
53
|
+
|
|
54
|
+
define_method("#{col}?") { !!send(col) }
|
|
68
55
|
end
|
|
69
56
|
end
|
|
70
57
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module OccamsRecord
|
|
2
|
+
# @private
|
|
3
|
+
module TypeCaster
|
|
4
|
+
CASTER =
|
|
5
|
+
case ActiveRecord::VERSION::MAJOR
|
|
6
|
+
when 4 then :type_cast_from_database
|
|
7
|
+
when 5, 6, 7 then :deserialize
|
|
8
|
+
else raise "OccamsRecord::TypeCaster::CASTER does yet support this version of ActiveRecord"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# @param column_names [Array<String>] the column names in the result set. The order MUST match the order returned by the query.
|
|
13
|
+
# @param column_types [Hash] Column name => type from an ActiveRecord::Result
|
|
14
|
+
# @param model [ActiveRecord::Base] the AR model representing the table (it holds column & type info).
|
|
15
|
+
# @return [Hash<Proc>] a Hash of casting Proc's keyed by column
|
|
16
|
+
#
|
|
17
|
+
def self.generate(column_names, column_types, model: nil)
|
|
18
|
+
column_names.each_with_object({}) { |col, memo|
|
|
19
|
+
#
|
|
20
|
+
# NOTE there's lots of variation between DB adapters and AR versions here. Some notes:
|
|
21
|
+
# * Postgres AR < 6.1 `column_types` will contain entries for every column.
|
|
22
|
+
# * Postgres AR >= 6.1 `column_types` only contains entries for "exotic" types. Columns with "common" types have already been converted by the PG adapter.
|
|
23
|
+
# * SQLite `column_types` will always be empty. Some types will have already been convered by the SQLite adapter, but others will depend on
|
|
24
|
+
# `model_column_types` for converstion. See test/raw_query_test.rb#test_common_types for examples.
|
|
25
|
+
# * MySQL ?
|
|
26
|
+
#
|
|
27
|
+
type = column_types[col] || model&.attributes_builder&.types&.[](col)
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# NOTE is also some variation in when enum values are mapped in different AR versions.
|
|
31
|
+
# In >=5.0, <=7.0, ActiveRecord::Result objects *usually* contain the human-readable values. In 4.2 and
|
|
32
|
+
# pre-release versions of 7.1, they instead have the RAW values (e.g. integers) which we must map ourselves.
|
|
33
|
+
#
|
|
34
|
+
enum = model&.defined_enums&.[](col)
|
|
35
|
+
inv_enum = enum&.invert
|
|
36
|
+
|
|
37
|
+
memo[col] =
|
|
38
|
+
case type&.type
|
|
39
|
+
when nil
|
|
40
|
+
if enum
|
|
41
|
+
->(val) { enum.has_key?(val) ? val : inv_enum[val] }
|
|
42
|
+
end
|
|
43
|
+
when :datetime
|
|
44
|
+
->(val) { type.send(CASTER, val)&.in_time_zone }
|
|
45
|
+
when :boolean
|
|
46
|
+
->(val) { type.send(CASTER, val) }
|
|
47
|
+
else
|
|
48
|
+
if enum
|
|
49
|
+
->(val) {
|
|
50
|
+
val = type.send(CASTER, val)
|
|
51
|
+
enum.has_key?(val) ? val : inv_enum[val]
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
->(val) { type.send(CASTER, val) }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/occams-record.rb
CHANGED
|
@@ -3,6 +3,7 @@ require 'occams-record/version'
|
|
|
3
3
|
require 'occams-record/merge'
|
|
4
4
|
require 'occams-record/measureable'
|
|
5
5
|
require 'occams-record/eager_loaders/eager_loaders'
|
|
6
|
+
require 'occams-record/type_caster'
|
|
6
7
|
require 'occams-record/results/results'
|
|
7
8
|
require 'occams-record/results/row'
|
|
8
9
|
require 'occams-record/pluck'
|
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.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jordan Hollinger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-07-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -30,20 +30,6 @@ dependencies:
|
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
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'
|
|
47
33
|
description: A faster, lower-memory, fuller-featured querying API for ActiveRecord
|
|
48
34
|
that returns results as unadorned, read-only objects.
|
|
49
35
|
email: jordan.hollinger@gmail.com
|
|
@@ -79,6 +65,7 @@ files:
|
|
|
79
65
|
- lib/occams-record/raw_query.rb
|
|
80
66
|
- lib/occams-record/results/results.rb
|
|
81
67
|
- lib/occams-record/results/row.rb
|
|
68
|
+
- lib/occams-record/type_caster.rb
|
|
82
69
|
- lib/occams-record/ugly.rb
|
|
83
70
|
- lib/occams-record/version.rb
|
|
84
71
|
homepage: https://jhollinger.github.io/occams-record/
|