occams-record 1.8.0 → 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 -25
- data/lib/occams-record/pluck.rb +13 -34
- data/lib/occams-record/results/results.rb +12 -50
- 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
|
|
@@ -298,32 +298,40 @@ On the other hand, Active Record makes it *very* easy to forget to eager load as
|
|
298
298
|
|
299
299
|
# Testing
|
300
300
|
|
301
|
-
|
302
|
-
bundle install
|
303
|
-
|
304
|
-
# test against SQLite
|
305
|
-
bundle exec rake test
|
306
|
-
|
307
|
-
# test against Postgres
|
308
|
-
TEST_DATABASE_URL=postgresql://postgres@localhost:5432/occams_record bundle exec rake test
|
309
|
-
|
310
|
-
# test against MySQL
|
311
|
-
TEST_DATABASE_URL=mysql2://root:@127.0.0.1:3306/occams_record bundle exec rake test
|
312
|
-
```
|
313
|
-
|
314
|
-
**Test against all supported ActiveRecord versions**
|
301
|
+
Tests are run with `appraisal` in Docker Compose using the `bin/test` or `bin/testall` scripts.
|
315
302
|
|
316
303
|
```bash
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
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 +
|
327
335
|
```
|
328
336
|
|
329
337
|
# License
|
data/lib/occams-record/pluck.rb
CHANGED
@@ -12,45 +12,24 @@ module OccamsRecord
|
|
12
12
|
|
13
13
|
# returns an array of values
|
14
14
|
def pluck_results_single(results, col, model: nil)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
else
|
23
|
-
# micro-optimization for when there are no enums
|
24
|
-
results.map { |r| r[col] }
|
25
|
-
end
|
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
|
+
}
|
26
22
|
end
|
27
23
|
|
28
24
|
# returns an array of arrays
|
29
25
|
def pluck_results_multi(results, cols, model: nil)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
}
|
36
|
-
|
37
|
-
if any_enums
|
38
|
-
results.map { |row|
|
39
|
-
cols_with_enums.map { |(col, enum, inv_enum)|
|
40
|
-
if enum
|
41
|
-
val = row[col]
|
42
|
-
enum.has_key?(val) ? val : inv_enum[val]
|
43
|
-
else
|
44
|
-
row[col]
|
45
|
-
end
|
46
|
-
}
|
47
|
-
}
|
48
|
-
else
|
49
|
-
# micro-optimization for when there are no enums
|
50
|
-
results.map { |row|
|
51
|
-
cols.map { |col| row[col] }
|
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
|
52
31
|
}
|
53
|
-
|
32
|
+
}
|
54
33
|
end
|
55
34
|
end
|
56
35
|
end
|
@@ -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,53 +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
|
-
# NOTE there's lots of variation between DB adapters and AR versions here. Some notes:
|
50
|
-
# * Postgres AR < 6.1 `column_types` will contain entries for every column.
|
51
|
-
# * Postgres AR >= 6.1 `column_types` only contains entries for "exotic" types. Columns with "common" types have already been converted by the PG adapter.
|
52
|
-
# * SQLite `column_types` will always be empty. Some types will have already been convered by the SQLite adapter, but others will depend on
|
53
|
-
# `model_column_types` for converstion. See test/raw_query_test.rb#test_common_types for examples.
|
54
|
-
# * MySQL ?
|
55
|
-
#
|
56
|
-
type = column_types[col] || model_column_types&.[](col)
|
57
|
-
|
58
|
-
#
|
59
|
-
# NOTE is also some variation in when enum values are mapped in different AR versions.
|
60
|
-
# In >=5.0, <=7.0, ActiveRecord::Result objects contain the human-readable values. In 4.2 and
|
61
|
-
# pre-release versions of 7.1, they instead have the RAW values (e.g. integers) which we must map ourselves.
|
62
|
-
#
|
63
|
-
enum = model&.defined_enums&.[](col)
|
64
|
-
inv_enum = enum&.invert
|
41
|
+
caster = casters[col]
|
65
42
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
enum.has_key?(val) ? val : inv_enum[val]
|
72
|
-
}
|
73
|
-
else
|
74
|
-
define_method(col) { @raw_values[idx] }
|
75
|
-
end
|
76
|
-
when :datetime
|
77
|
-
define_method(col) { @cast_values[idx] ||= type.send(CASTER, @raw_values[idx])&.in_time_zone }
|
78
|
-
when :boolean
|
79
|
-
define_method(col) { @cast_values[idx] ||= type.send(CASTER, @raw_values[idx]) }
|
80
|
-
define_method("#{col}?") { !!send(col) }
|
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
|
+
}
|
81
48
|
else
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
val = type.send(CASTER, @raw_values[idx])
|
86
|
-
enum.has_key?(val) ? val : inv_enum[val]
|
87
|
-
)
|
88
|
-
}
|
89
|
-
else
|
90
|
-
define_method(col) { @cast_values[idx] ||= type.send(CASTER, @raw_values[idx]) }
|
91
|
-
end
|
49
|
+
define_method(col) {
|
50
|
+
@raw_values[idx]
|
51
|
+
}
|
92
52
|
end
|
53
|
+
|
54
|
+
define_method("#{col}?") { !!send(col) }
|
93
55
|
end
|
94
56
|
end
|
95
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.8.
|
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-07-
|
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/
|