activerecord-virtual_attributes 6.1.0 → 6.1.2

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: eb60079c2b76281f9ace2d7f4a25c9956d9c439c408b6d0df615cb8c4512a31f
4
- data.tar.gz: 6eb95ac5ea930cd875c73e8fcfaca826e3f69370417489f9c074c2c90795e71e
3
+ metadata.gz: e19652ea9b4883f4acae661b63dcc69b4fa4b0bdd0b286a0eb8b17a87892278e
4
+ data.tar.gz: 2ba220783720e1bc5b67e0af787c1396127b3fef2f2e0813deb7e34b766579b8
5
5
  SHA512:
6
- metadata.gz: e0d8221e8ee5ce87f5082c7e3e7b821375e0da1c6109bd772c1ee01fb396de12169c3f6ac53cdddd267d7974785b81ca0047ccf4b0ed43278b79bdb84c2cd557
7
- data.tar.gz: 0b980f5df648db206618fd304ac54fd69f84ed53e004226629ddcc389775686934291baaf106cf196b0fe5204107046bf437f6db12b41a632a62dba88c71b0a0
6
+ metadata.gz: 2b60fcf44b86210f1dbe3ab9be5585158c027bc9c81c0f2ccad77a0079a77b0918bc58feca99f550f4ad2aff4e61defaf7229b125f6091dd770df0242af645fe
7
+ data.tar.gz: e691ea9679a9fda6db72fc3f057dd7d5aee163e9fd1d4aff035292da1e03c23eb711920bad8cf79ad41054bb28c2ba29ac9a03a4a38d7b44a52b9ca322245e16
@@ -1,6 +1,10 @@
1
1
  name: CI
2
2
 
3
- on: [push, pull_request]
3
+ on:
4
+ push:
5
+ pull_request:
6
+ schedule:
7
+ - cron: '0 0 * * 0'
4
8
 
5
9
  jobs:
6
10
  ci:
@@ -9,33 +13,28 @@ jobs:
9
13
  matrix:
10
14
  ruby-version:
11
15
  - '2.7'
16
+ - '3.0'
17
+ - '3.1'
12
18
  services:
13
19
  postgres:
14
20
  image: postgres:13
15
21
  env:
16
22
  POSTGRES_PASSWORD: password
17
23
  POSTGRES_DB: virtual_attributes
24
+ options: --health-cmd pg_isready --health-interval 2s --health-timeout 5s --health-retries 5
18
25
  ports:
19
26
  - 5432:5432
20
- options: >-
21
- --health-cmd pg_isready
22
- --health-interval 2s
23
- --health-timeout 5s
24
- --health-retries 5
25
27
  mysql:
26
28
  image: mysql:8.0
27
29
  env:
28
30
  MYSQL_ROOT_PASSWORD: password
29
31
  MYSQL_DATABASE: virtual_attributes
32
+ options: --health-cmd="mysqladmin ping -h 127.0.0.1 -P 3306 --silent" --health-interval 10s --health-timeout 5s --health-retries 3
30
33
  ports:
31
34
  - 3306:3306
32
- options: >-
33
- --health-cmd="mysqladmin ping -h 127.0.0.1 -P 3306 --silent"
34
- --health-interval 10s
35
- --health-timeout 5s
36
- --health-retries 3
37
35
  env:
38
- # for the pg cli (psql, pg_isready) and possibly rails
36
+ CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
37
+ # for the pg cli (psql, pg_isready) and rails
39
38
  PGHOST: localhost
40
39
  PGPORT: 5432
41
40
  PGUSER: postgres
@@ -44,31 +43,29 @@ jobs:
44
43
  MYSQL_HOST: 127.0.0.1
45
44
  MYSQL_PWD: password
46
45
  steps:
47
- - uses: actions/checkout@v2
46
+ - uses: actions/checkout@v4
48
47
  - name: Set up Ruby
49
48
  uses: ruby/setup-ruby@v1
50
49
  with:
51
50
  ruby-version: ${{ matrix.ruby-version }}
52
51
  bundler-cache: true
52
+ timeout-minutes: 30
53
53
  - name: Run SQLite tests
54
54
  env:
55
55
  DB: sqlite3
56
- CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
57
56
  run: bundle exec rake
58
- - name: Run Postgres tests
57
+ - name: Run PostgreSQL tests
59
58
  env:
60
- DB: pg
59
+ DB: postgresql
61
60
  COLLATE_SYMBOLS: false
62
- CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
63
61
  run: bundle exec rake
64
62
  - name: Run MySQL tests
65
63
  env:
66
64
  DB: mysql2
67
- CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
68
65
  run: bundle exec rake
69
- - if: ${{ github.ref == 'refs/heads/master' && matrix.ruby-version == '2.7' }}
70
- name: Report code coverage
66
+ - name: Report code coverage
67
+ if: ${{ github.ref == 'refs/heads/master' && matrix.ruby-version == '3.1' }}
71
68
  continue-on-error: true
72
- uses: paambaati/codeclimate-action@v3.0.0
69
+ uses: paambaati/codeclimate-action@v5
73
70
  env:
74
71
  CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
data/CHANGELOG.md CHANGED
@@ -1,12 +1,19 @@
1
1
  # Change Log
2
2
 
3
- Versioning of this gem follows ActiveRecord versioning, and does not follow SemVer.
3
+ The versioning of this gem follows ActiveRecord versioning, and does not follow SemVer. See the [README](./README.md) for more details.
4
4
 
5
- e.g.: virtual attributes 6.1.x supports all versions of rails 6.1.
5
+ ## [Unreleased]
6
6
 
7
- Use the latest version of both this gem and rails where the first 2 digits match.
7
+ ## [6.1.2] - 2023-10-26
8
8
 
9
- ## [Unreleased]
9
+ * Fix bind variables for joins with static strings [#124](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/124)
10
+ * Add `virtual_total` for `habtm` [#123](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/123)
11
+ * Fix: `:uses` clause now works with an array and nested hashes. [#120](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/120)
12
+ * Uses symbols in the `includes()` clause. defined by `virtual_attribute :uses` and virtual_delegate. [#128](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/128)
13
+
14
+ ## [6.1.1] - 2022-08-09
15
+
16
+ * fix HomogeneousIn clauses [#111](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/111)
10
17
 
11
18
  ## [6.1.0] - 2022-02-03
12
19
 
@@ -86,7 +93,9 @@ Use the latest version of both this gem and rails where the first 2 digits match
86
93
  * Initial Release
87
94
  * Extracted from ManageIQ/manageiq
88
95
 
89
- [Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.0...HEAD
96
+ [Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.2...HEAD
97
+ [6.1.2]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.1...v6.1.2
98
+ [6.1.1]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.0...v6.1.1
90
99
  [6.1.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v3.0.0...v6.1.0
91
100
  [3.0.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v2.0.0...v3.0.0
92
101
  [2.0.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.6.0...v2.0.0
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # VirtualAttributes
2
2
 
3
3
  [![CI](https://github.com/ManageIQ/activerecord-virtual_attributes/actions/workflows/ci.yaml/badge.svg)](https://github.com/ManageIQ/activerecord-virtual_attributes/actions/workflows/ci.yaml)
4
- [![Maintainability](https://api.codeclimate.com/v1/badges/e1a0c26941c00f4edb55/maintainability)](https://codeclimate.com/github/ManageIQ/activerecord-virtual_attributes/maintainability)
5
- [![Test Coverage](https://api.codeclimate.com/v1/badges/e1a0c26941c00f4edb55/test_coverage)](https://codeclimate.com/github/ManageIQ/activerecord-virtual_attributes/test_coverage)
4
+ [![Maintainability](https://codeclimate.com/github/ManageIQ/activerecord-virtual_attributes.svg)](https://codeclimate.com/github/ManageIQ/activerecord-virtual_attributes/maintainability)
5
+ [![Test Coverage](https://codeclimate.com/github/ManageIQ/activerecord-virtual_attributes/coverage.svg)](https://codeclimate.com/github/ManageIQ/activerecord-virtual_attributes/test_coverage)
6
6
 
7
7
  VirtualAttributes allows you to define a ruby method that acts like an attribute or relation.
8
8
 
@@ -12,6 +12,12 @@ This gem allows you to represent these attributes in sql so `ORDER BY` `WHERE` c
12
12
 
13
13
  This also allows you to calculate counts and treat those as a field accessible with `select(:child_count)` to get rid of the N+1 problem of running a `count(*)` on a subcollection for each row.
14
14
 
15
+ ## Versioning
16
+
17
+ As of v6.1.0, the versioning of this gem follows ActiveRecord versioning, and does not follow SemVer (e.g. virtual attributes v6.1.x supports all versions of Rails 6.1). Version v3.0.0 supports Rails 6.0 and lower.
18
+
19
+ Use the latest version of both this gem and Rails where the first 2 digits match.
20
+
15
21
  ## Installation
16
22
 
17
23
  Add this line to your application's Gemfile:
@@ -34,14 +40,15 @@ TODO: Write usage instructions here
34
40
 
35
41
  ## Development
36
42
 
37
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
43
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
38
44
 
39
45
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
46
 
47
+ To test with different database adapters, set the DB environment variable:
41
48
 
42
- To test with different versions of ruby, use `wwtd` gem or
43
-
44
- DB=pg BUNDLE_GEMFILE=gemfiles/gemfile_${version-52}.gemfile bundle exec rake
49
+ DB=postgresql bundle exec rake
50
+ DB=mysql bundle exec rake
51
+ DB=sqlite3 bundle exec rake
45
52
 
46
53
  ## Contributing
47
54
 
@@ -50,4 +57,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Manage
50
57
  ## License
51
58
 
52
59
  This project is available as open source under the terms of the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
53
-
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_runtime_dependency "activerecord", "~> 6.1.0"
29
29
 
30
30
  spec.add_development_dependency "byebug"
31
+ spec.add_development_dependency "database_cleaner-active_record", "~> 2.1"
31
32
  spec.add_development_dependency "db-query-matchers"
32
33
  spec.add_development_dependency "manageiq-style"
33
34
  spec.add_development_dependency "rake", "~> 13.0"
data/bin/setup CHANGED
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
  IFS=$'\n\t'
4
- set -vx
4
+ set -e
5
5
 
6
6
  bundle install
7
+ echo
7
8
 
8
- # Do any other automated setup that you need to do here
9
+ echo "Setting up the postgres database for specs..."
10
+ echo "SELECT 'CREATE DATABASE virtual_attributes' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'virtual_attributes')\gexec" | psql -U postgres
11
+
12
+ echo "Setting up the mysql database for specs..."
13
+ mysql -u root -e 'CREATE SCHEMA IF NOT EXISTS 'virtual_attributes';'
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module VirtualAttributes
3
- VERSION = "6.1.0".freeze
3
+ VERSION = "6.1.2".freeze
4
4
  end
5
5
  end
@@ -12,14 +12,32 @@ module ActiveRecord
12
12
 
13
13
  # in essence, this is our Arel::Nodes::VirtualAttribute
14
14
  class Arel::Nodes::Grouping
15
- attr_accessor :name
15
+ attr_accessor :name, :relation
16
+
17
+ # methods from Arel::Nodes::Attribute
18
+ def type_caster
19
+ relation.type_for_attribute(name)
20
+ end
21
+
22
+ # Create a node for lowering this attribute
23
+ def lower
24
+ relation.lower(self)
25
+ end
26
+
27
+ def type_cast_for_database(value)
28
+ relation.type_cast_for_database(name, value)
29
+ end
30
+
31
+ # rubocop:disable Rails/Delegate
32
+ def able_to_type_cast?
33
+ relation.able_to_type_cast?
34
+ end
35
+ # rubocop:enable Rails/Delegate
16
36
  end
17
37
 
18
38
  module VirtualArel
19
- # This arel table proxy is our shim to get our functionality into rails
39
+ # This arel table proxy. This allows WHERE clauses to use virtual attributes
20
40
  class ArelTableProxy < Arel::Table
21
- attr_accessor :klass
22
-
23
41
  # overrides Arel::Table#[]
24
42
  # adds aliases and virtual attribute arel (aka sql)
25
43
  #
@@ -50,16 +68,9 @@ module ActiveRecord
50
68
  end
51
69
 
52
70
  module ClassMethods
53
- if ActiveRecord.version.to_s < "6.1"
54
- # ActiveRecord::Core 6.0 (every version of active record seems to do this differently)
55
- def arel_table
56
- @arel_table ||= ArelTableProxy.new(table_name, :type_caster => type_caster).tap { |t| t.klass = self }
57
- end
58
- else
59
- # ActiveRecord::Core 6.1
60
- def arel_table
61
- @arel_table ||= ArelTableProxy.new(table_name, :klass => self)
62
- end
71
+ # ActiveRecord::Core 6.1
72
+ def arel_table
73
+ @arel_table ||= ArelTableProxy.new(table_name, :klass => self)
63
74
  end
64
75
 
65
76
  # supported by sql if any are true:
@@ -86,6 +97,7 @@ module ActiveRecord
86
97
  arel = arel_lambda.call(table)
87
98
  arel = Arel::Nodes::Grouping.new(arel) unless arel.kind_of?(Arel::Nodes::Grouping)
88
99
  arel.name = column_name
100
+ arel.relation = table
89
101
  arel
90
102
  end
91
103
 
@@ -98,3 +110,43 @@ module ActiveRecord
98
110
  end
99
111
  end
100
112
  end
113
+
114
+ module Arel # :nodoc: all
115
+ # rubocop:disable Naming/MethodName
116
+ # rubocop:disable Naming/MethodParameterName
117
+ # rubocop:disable Style/ConditionalAssignment
118
+ module Visitors
119
+ # rails 6.1...
120
+ class ToSql
121
+ private
122
+
123
+ def visit_Arel_Nodes_HomogeneousIn(o, collector)
124
+ collector.preparable = false
125
+
126
+ # change:
127
+ # See https://github.com/rails/rails/pull/45642
128
+ visit(o.left, collector)
129
+ # /change
130
+
131
+ if o.type == :in
132
+ collector << " IN ("
133
+ else
134
+ collector << " NOT IN ("
135
+ end
136
+
137
+ values = o.casted_values
138
+
139
+ if values.empty?
140
+ collector << @connection.quote(nil)
141
+ else
142
+ collector.add_binds(values, o.proc_for_binds, &bind_block)
143
+ end
144
+
145
+ collector << ")"
146
+ end
147
+ end
148
+ end
149
+ # rubocop:enable Naming/MethodName
150
+ # rubocop:enable Naming/MethodParameterName
151
+ # rubocop:enable Style/ConditionalAssignment
152
+ end
@@ -43,7 +43,7 @@ module ActiveRecord
43
43
  method_prefix = virtual_delegate_name_prefix(options[:prefix], to)
44
44
  method_name = "#{method_prefix}#{method}"
45
45
  if to.include?(".") # to => "target.method"
46
- to, method = to.split(".")
46
+ to, method = to.split(".").map(&:to_sym)
47
47
  options[:to] = to
48
48
  end
49
49
 
@@ -260,8 +260,7 @@ module ActiveRecord
260
260
 
261
261
  yield arel if block_given?
262
262
 
263
- # convert arel to sql to populate with bind variables
264
- ::Arel::Nodes::Grouping.new(Arel.sql(arel.to_sql))
263
+ ::Arel::Nodes::Grouping.new(arel)
265
264
  end
266
265
 
267
266
  # determine table reference to use for a sub query
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
 
36
36
  case associations
37
37
  when String, Symbol
38
- virtual_field?(associations) ? replace_virtual_fields(virtual_includes(associations)) : associations
38
+ virtual_field?(associations) ? replace_virtual_fields(virtual_includes(associations)) : associations.to_sym
39
39
  when Array
40
40
  associations.collect { |association| replace_virtual_fields(association) }.compact
41
41
  when Hash
@@ -67,9 +67,11 @@ module ActiveRecord
67
67
  def include_to_hash(value)
68
68
  case value
69
69
  when String, Symbol
70
- {value => {}}
70
+ {value.to_sym => {}}
71
71
  when Array
72
- value.flatten.each_with_object({}) { |k, h| h[k] = {} }
72
+ value.flatten.each_with_object({}) do |k, h|
73
+ merge_includes(h, k)
74
+ end
73
75
  when nil
74
76
  {}
75
77
  else
@@ -77,15 +79,21 @@ module ActiveRecord
77
79
  end
78
80
  end
79
81
 
80
- # @param [Hash] hash1
81
- # @param [Hash] hash2
82
+ # @param [Hash] hash1 (incoming hash is modified and returned)
83
+ # @param [Hash|Symbol|nil] hash2 (this hash will not be modified)
82
84
  def merge_includes(hash1, hash2)
83
85
  return hash1 if hash2.blank?
84
86
 
85
- hash1 = include_to_hash(hash1)
86
- hash2 = include_to_hash(hash2)
87
- hash1.deep_merge!(hash2) do |_k, v1, v2|
88
- merge_includes(v1, v2)
87
+ # very common case.
88
+ # optimization to skip deep_merge and hash creation
89
+ if hash2.kind_of?(Symbol)
90
+ hash1[hash2] ||= {}
91
+ return hash1
92
+ end
93
+
94
+ hash1.deep_merge!(include_to_hash(hash2)) do |_k, v1, v2|
95
+ # this block is conflict resolution when a key has 2 values
96
+ merge_includes(include_to_hash(v1), v2)
89
97
  end
90
98
  end
91
99
  end
@@ -114,7 +114,7 @@ module VirtualAttributes
114
114
  end
115
115
 
116
116
  def virtual_aggregate_arel(reflection, method_name, column)
117
- return unless reflection && reflection.macro == :has_many
117
+ return unless reflection && [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
118
118
 
119
119
  # need db access for the reflection join_keys, so delaying all this key lookup until call time
120
120
  lambda do |t|
@@ -137,14 +137,8 @@ module VirtualAttributes
137
137
  # query: SELECT COUNT(*) FROM foreign_table JOIN ... [WHERE main_table.id = foreign_table.id]
138
138
  query.where(join.right.expr)
139
139
 
140
- # convert bind variables from ? to actual values. otherwise, sql is incomplete
141
- conn = connection
142
- sql = conn.unprepared_statement { conn.to_sql(query) }
143
-
144
- # add () around query
145
- query = t.grouping(Arel::Nodes::SqlLiteral.new(sql))
146
140
  # add coalesce to ensure correct value comes out
147
- t.grouping(Arel::Nodes::NamedFunction.new('COALESCE', [query, Arel::Nodes::SqlLiteral.new("0")]))
141
+ t.grouping(Arel::Nodes::NamedFunction.new('COALESCE', [t.grouping(query), Arel::Nodes::SqlLiteral.new("0")]))
148
142
  end
149
143
  end
150
144
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-virtual_attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 6.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keenan Brock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-03 00:00:00.000000000 Z
11
+ date: 2023-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: database_cleaner-active_record
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: db-query-matchers
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -167,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
181
  - !ruby/object:Gem::Version
168
182
  version: '0'
169
183
  requirements: []
170
- rubygems_version: 3.3.5
184
+ rubygems_version: 3.2.33
171
185
  signing_key:
172
186
  specification_version: 4
173
187
  summary: Access non-sql attributes from sql