occams-record 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 846af974c8bc267fe8e901995d0aa3341b018d135eeb267c11d9add447cd0155
4
- data.tar.gz: 763f8a038908cb9a990bcf305644661736dd9133e51a9d8fb8ece2b8ff018fd2
3
+ metadata.gz: 2778b16aee6b725c967c4877b9905eed75b37b64bf8c5fca7077918ea2a4a53a
4
+ data.tar.gz: b8948ddd57573efcbeb90e96235626e482cc557e9b92d4e5fdf0237c182e39df
5
5
  SHA512:
6
- metadata.gz: 24e1c5dfb09cb35b2b7116cb40aafcb1eb215ac96e3c8bab7ff6558598b31e8abb77b49c2b711392567862b182e1aed705888e939f8b66f15285f2ca352dbc89
7
- data.tar.gz: 04f78d14765b6ae4d9ccee5f01633132ed1e39662071dfc39f6e33583e9cd3e6214841225eb35ce2f678472ce34bf45e27e23660114f28cede6a27cdb38613b0
6
+ metadata.gz: e436f30e97f0a256d146539902b3f6fe9b54c241d7b49a51da63242f59505793c14f6ceef601343660750d0030ddf21c196e40a4aec41b1b5f1490b71b255ca6
7
+ data.tar.gz: 14095b912856efe2a097471046e518c0d3040320e72198ae28cf8b76c84040ca3319f16916ef2c70c357d8661e6465a36d7b80c49109896c828a2cc36dd777ee
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Occams Record [![Build Status](https://travis-ci.org/jhollinger/occams-record.svg?branch=master)](https://travis-ci.org/jhollinger/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
- ```bash
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
- bundle exec appraisal install
318
-
319
- # test against all supported AR versions (defaults to SQLite)
320
- bundle exec appraisal rake test
321
-
322
- # test against a specific AR version
323
- bundle exec appraisal ar-7.0 rake test
324
-
325
- # test against Postgres
326
- TEST_DATABASE_URL=postgresql://postgres@localhost:5432/occams_record bundle exec appraisal rake test
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
@@ -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
- enum = model&.defined_enums&.[](col)
16
- inv_enum = enum&.invert
17
- if enum
18
- results.map { |r|
19
- val = r[col]
20
- enum.has_key?(val) ? val : inv_enum[val]
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
- any_enums = false
31
- cols_with_enums = cols.map { |col|
32
- enum = model&.defined_enums&.[](col)
33
- any_enums ||= !!enum
34
- [col, enum, enum&.invert]
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
- end
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
- model_column_types = model ? model.attributes_builder.types : nil
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
- case type&.type
67
- when nil
68
- if enum
69
- define_method(col) {
70
- val = @raw_values[idx]
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
- if enum
83
- define_method(col) {
84
- @cast_values[idx] ||= (
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
@@ -3,5 +3,5 @@
3
3
  #
4
4
  module OccamsRecord
5
5
  # @private
6
- VERSION = "1.8.0".freeze
6
+ VERSION = "1.8.1".freeze
7
7
  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.0
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-01 00:00:00.000000000 Z
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/