activerecord-virtual_attributes 1.3.1 → 3.0.0
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/.travis.yml +10 -8
- data/Appraisals +11 -6
- data/CHANGELOG.md +41 -2
- data/README.md +5 -0
- data/activerecord-virtual_attributes.gemspec +2 -0
- data/bin/console +4 -7
- data/gemfiles/{virtual_attributes_50.gemfile → gemfile_50.gemfile} +1 -1
- data/gemfiles/gemfile_51.gemfile +10 -0
- data/gemfiles/{virtual_attributes_52.gemfile → gemfile_52.gemfile} +1 -1
- data/gemfiles/{virtual_attributes_51.gemfile → gemfile_60.gemfile} +2 -2
- data/init.rb +1 -1
- data/lib/active_record/virtual_attributes.rb +8 -8
- data/lib/active_record/virtual_attributes/arel_groups.rb +2 -1
- data/lib/active_record/virtual_attributes/rspec/have_virtual_attribute.rb +1 -5
- data/lib/active_record/virtual_attributes/version.rb +1 -1
- data/lib/active_record/virtual_attributes/virtual_arel.rb +10 -3
- data/lib/active_record/virtual_attributes/virtual_delegates.rb +9 -0
- data/lib/active_record/virtual_attributes/virtual_fields.rb +300 -86
- data/lib/active_record/virtual_attributes/virtual_total.rb +83 -51
- metadata +38 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f205435ff8ec8ecda2020356916188e8c145dedfd57e9e9a89c7c3c743afa57a
|
4
|
+
data.tar.gz: 3485ee96f78567ef4fd98eec35dc6350313c543c6e5606c1ad4191812a25d534
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 700653df253780ef026120bdaa639e14adc4ed2c5ee735575d6a42a8335833791ab8dff596de8195b3c07f9da68d55e535a70cd20edc7b3ed2d973cf23aab177
|
7
|
+
data.tar.gz: e4ae06737ca3973621e673b18388f0bc9a0012efbe2e569857de4b718a7a89ae3324f27ff3cbac91d0d7c8da6456bd36cee1161dec771b6af3f8bde0ced8f65b
|
data/.travis.yml
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
---
|
2
|
+
sudo: false
|
2
3
|
language: ruby
|
3
4
|
cache: bundler
|
4
5
|
rvm:
|
5
|
-
- 2.
|
6
|
-
- 2.
|
6
|
+
- 2.5.6
|
7
|
+
- 2.6.4
|
7
8
|
services:
|
8
|
-
- postgresql
|
9
9
|
- mysql
|
10
|
+
- postgresql
|
10
11
|
env:
|
11
|
-
- DB=sqlite3
|
12
|
-
- DB=pg
|
13
12
|
- DB=mysql2
|
13
|
+
- DB=pg
|
14
|
+
- DB=sqlite3
|
14
15
|
gemfile:
|
15
|
-
- gemfiles/
|
16
|
-
- gemfiles/
|
17
|
-
|
16
|
+
- gemfiles/gemfile_50.gemfile
|
17
|
+
- gemfiles/gemfile_51.gemfile
|
18
|
+
- gemfiles/gemfile_52.gemfile
|
19
|
+
- gemfiles/gemfile_60.gemfile
|
18
20
|
before_install:
|
19
21
|
- 'echo ''gem: --no-ri --no-rdoc --no-document'' > ~/.gemrc'
|
20
22
|
before_script:
|
data/Appraisals
CHANGED
@@ -1,18 +1,23 @@
|
|
1
|
-
%w(5.0.7 5.1.
|
2
|
-
|
3
|
-
appraise "#{db_gem}-#{ar_version.split('.').first(2).join}" do
|
1
|
+
%w(5.0.7 5.1.7 5.2.3 6.0.0).each do |ar_version|
|
2
|
+
appraise "gemfile-#{ar_version.split('.').first(2).join}" do
|
4
3
|
gem "activerecord", "~> #{ar_version}"
|
5
4
|
|
6
|
-
gem "pg"
|
7
5
|
if ar_version >= "5.0"
|
8
6
|
gem "mysql2"
|
7
|
+
elsif ar_version >= "4.2"
|
8
|
+
gem "mysql2", "~> 0.4.0"
|
9
|
+
end
|
10
|
+
|
11
|
+
if ar_version >= "5.0"
|
12
|
+
gem "pg"
|
9
13
|
else
|
10
|
-
gem "
|
14
|
+
gem "pg", "0.18.4"
|
11
15
|
end
|
16
|
+
|
12
17
|
if ar_version >= "5.2"
|
13
18
|
gem "sqlite3"
|
14
19
|
else
|
15
|
-
gem "sqlite3", "~> 1.3.
|
20
|
+
gem "sqlite3", "~> 1.3.13"
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
data/CHANGELOG.md
CHANGED
@@ -3,7 +3,41 @@
|
|
3
3
|
Doing our best at supporting [SemVer](http://semver.org/) with
|
4
4
|
a nice looking [Changelog](http://keepachangelog.com).
|
5
5
|
|
6
|
-
## Version [Unreleased]
|
6
|
+
## Version [Unreleased] <small>tbd</small>
|
7
|
+
|
8
|
+
## Version [3.0.0] <small>2020-09-28</small>
|
9
|
+
|
10
|
+
* fix virtual_aggregate to return a consistent 0 when calculating a sum of no records
|
11
|
+
* fix virtual delegate to include the type column when fetching associated models for polymorphism
|
12
|
+
* add virtual_average, virtual_minimum, and virtual_maximum
|
13
|
+
|
14
|
+
## Version [2.0.0] <small>2020-05-22</small>
|
15
|
+
|
16
|
+
* This is a trivial release, but because it modifies a public interface, the jump makes it look significant.
|
17
|
+
* removed legacy virtual_column parameter support. (it is not ruby 2.7 compatible)
|
18
|
+
* fixed warnings in ruby 2.7
|
19
|
+
|
20
|
+
## Version [1.6.0] <small>2019-12-02</small>
|
21
|
+
|
22
|
+
* rails 5.2 support
|
23
|
+
* fix Arel#name error
|
24
|
+
* Display deprecation notices for invalid associations (rather than throw an error)
|
25
|
+
|
26
|
+
## Version [1.5.0] <small>2019-12-02</small>
|
27
|
+
|
28
|
+
* `select()` no longer modifies `select_values`. It understands virtual attributes at a lower level.
|
29
|
+
* `includes()` can now handle all proper values presented.
|
30
|
+
* `virtual_total` added support for `has_many` `:through`
|
31
|
+
* `virtual_total` with a nil attribute value no longer executes an extra query
|
32
|
+
* rails 6.0 support, (rails 5.2 only fails `habtm` preloading)
|
33
|
+
* ruby 2.6.x support (no longer testing ruby 2.4)
|
34
|
+
|
35
|
+
## Version [1.4.0] <small>2019-07-13</small>
|
36
|
+
|
37
|
+
* fix includes to include all associations
|
38
|
+
* fix bin/console to now actually run
|
39
|
+
* select no longer munges field attribute
|
40
|
+
* support virtual attributes in left_outer_joins
|
7
41
|
|
8
42
|
## Version [1.3.1] <small>2019-06-06</small>
|
9
43
|
|
@@ -38,7 +72,12 @@ a nice looking [Changelog](http://keepachangelog.com).
|
|
38
72
|
* Initial Release
|
39
73
|
* Extracted from ManageIQ/manageiq
|
40
74
|
|
41
|
-
[Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/
|
75
|
+
[Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v3.0.0...HEAD
|
76
|
+
[3.0.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v2.0.0...v3.0.0
|
77
|
+
[2.0.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.6.0...v2.0.0
|
78
|
+
[1.6.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.5.0...v1.6.0
|
79
|
+
[1.5.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.4.0...v1.5.0
|
80
|
+
[1.4.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.3.1...v1.4.0
|
42
81
|
[1.3.1]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.3.0...v1.3.1
|
43
82
|
[1.3.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.2.0...v1.3.0
|
44
83
|
[1.2.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.1.0...v1.2.0
|
data/README.md
CHANGED
@@ -42,6 +42,11 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
42
42
|
|
43
43
|
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).
|
44
44
|
|
45
|
+
|
46
|
+
To test with different versions of ruby, use `wwtd` gem or
|
47
|
+
|
48
|
+
DB=pg BUNDLE_GEMFILE=gemfiles/gemfile_${version-52}.gemfile beer "$@"
|
49
|
+
|
45
50
|
## Contributing
|
46
51
|
|
47
52
|
Bug reports and pull requests are welcome on GitHub at https://github.com/ManageIQ/activerecord-virtual_attributes .
|
@@ -28,6 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_runtime_dependency "activerecord", ">= 5.0"
|
29
29
|
|
30
30
|
spec.add_development_dependency "appraisal"
|
31
|
+
spec.add_development_dependency "byebug"
|
32
|
+
spec.add_development_dependency "db-query-matchers", "~>0.10"
|
31
33
|
spec.add_development_dependency "rake", "~> 10.0"
|
32
34
|
spec.add_development_dependency "rspec", "~> 3.0"
|
33
35
|
spec.add_development_dependency "simplecov"
|
data/bin/console
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "bundler/setup"
|
4
|
-
require "virtual_attributes"
|
4
|
+
require "active_record-virtual_attributes"
|
5
5
|
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
6
|
+
# models for local testing
|
7
|
+
require "rspec"
|
8
|
+
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
12
9
|
|
13
10
|
require "irb"
|
14
11
|
IRB.start(__FILE__)
|
data/init.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require 'virtual_attributes'
|
1
|
+
require 'active_record-virtual_attributes'
|
@@ -48,13 +48,9 @@ module ActiveRecord
|
|
48
48
|
#
|
49
49
|
|
50
50
|
# Compatibility method: `virtual_attribute` is a more accurate name
|
51
|
-
def virtual_column(name,
|
52
|
-
|
53
|
-
|
54
|
-
type = options.delete(:type)
|
55
|
-
else
|
56
|
-
type = type_or_options
|
57
|
-
end
|
51
|
+
def virtual_column(name, **options)
|
52
|
+
type = options.delete(:type)
|
53
|
+
raise ArgumentError, "missing :type attribute" unless type
|
58
54
|
|
59
55
|
virtual_attribute(name, type, **options)
|
60
56
|
end
|
@@ -93,7 +89,11 @@ module ActiveRecord
|
|
93
89
|
# change necessary for rails 5.0 and 5.1 - (changed/introduced in https://github.com/rails/rails/pull/31894)
|
94
90
|
defaults = defaults.except(*virtual_attribute_names)
|
95
91
|
# end change
|
96
|
-
@attributes_builder = ActiveRecord
|
92
|
+
@attributes_builder = if ActiveRecord.version.to_s >= "5.2"
|
93
|
+
ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
|
94
|
+
else
|
95
|
+
ActiveRecord::AttributeSet::Builder.new(attribute_types, defaults)
|
96
|
+
end
|
97
97
|
end
|
98
98
|
@attributes_builder
|
99
99
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# this is from https://github.com/rails/arel/pull/435
|
2
2
|
# this allows sorting and where clauses to work with virtual_attribute columns
|
3
|
-
|
3
|
+
# no longer needed for rails 6.0 and up (change was merged)
|
4
|
+
if ActiveRecord.version.to_s < "6.0" && defined?(Arel::Nodes::Grouping)
|
4
5
|
module Arel
|
5
6
|
module Nodes
|
6
7
|
class Grouping
|
@@ -2,7 +2,7 @@ RSpec::Matchers.define :have_virtual_attribute do |name, type|
|
|
2
2
|
match do |klass|
|
3
3
|
expect(klass.has_attribute?(name)).to be_truthy
|
4
4
|
expect(klass.virtual_attribute?(name)).to be_truthy
|
5
|
-
expect(klass.type_for_attribute(name).type).to
|
5
|
+
expect(klass.type_for_attribute(name.to_s).type).to(eq(type)) if type
|
6
6
|
klass.instance_methods.include?(name.to_sym)
|
7
7
|
end
|
8
8
|
|
@@ -13,10 +13,6 @@ RSpec::Matchers.define :have_virtual_attribute do |name, type|
|
|
13
13
|
failure_message_when_negated do |klass|
|
14
14
|
"expected #{klass.name} to not have virtual column #{name.inspect} with type #{type.inspect}"
|
15
15
|
end
|
16
|
-
|
17
|
-
description do
|
18
|
-
"expect the object to have the virtual column"
|
19
|
-
end
|
20
16
|
end
|
21
17
|
|
22
18
|
RSpec::Matchers.alias_matcher(:have_virtual_column, :have_virtual_attribute)
|
@@ -2,13 +2,17 @@ module ActiveRecord
|
|
2
2
|
module VirtualAttributes
|
3
3
|
# VirtualArel associates arel with an attribute
|
4
4
|
#
|
5
|
-
# Model.virtual_attribute :field, :string, :arel => ->
|
5
|
+
# Model.virtual_attribute :field, :string, :arel => ->(t) { t.grouping(t[:field2]) } }
|
6
6
|
# Model.select(:field)
|
7
7
|
#
|
8
8
|
# is equivalent to:
|
9
9
|
#
|
10
10
|
# Model.select(Model.arel_table.grouping(Model.arel_table[:field2]).as(:field))
|
11
11
|
# Model.attribute_supported_by_sql?(:field) # => true
|
12
|
+
class Arel::Nodes::Grouping
|
13
|
+
attr_accessor :name
|
14
|
+
end
|
15
|
+
|
12
16
|
module VirtualArel
|
13
17
|
extend ActiveSupport::Concern
|
14
18
|
|
@@ -21,8 +25,11 @@ module ActiveRecord
|
|
21
25
|
def arel_attribute(column_name, arel_table = self.arel_table)
|
22
26
|
load_schema
|
23
27
|
if virtual_attribute?(column_name) && !attribute_alias?(column_name)
|
24
|
-
col = _virtual_arel[column_name.to_s]
|
25
|
-
|
28
|
+
if (col = _virtual_arel[column_name.to_s])
|
29
|
+
arel = col.call(arel_table)
|
30
|
+
arel.name = column_name if arel.kind_of?(Arel::Nodes::Grouping)
|
31
|
+
arel
|
32
|
+
end
|
26
33
|
else
|
27
34
|
super
|
28
35
|
end
|
@@ -240,6 +240,7 @@ module ActiveRecord
|
|
240
240
|
# (SELECT "vms_sub"."name" FROM "vms" AS "vms_ss" WHERE "vms_ss"."id" = "vms"."src_template_id")
|
241
241
|
#
|
242
242
|
|
243
|
+
# Based upon ActiveRecord AssociationScope.scope
|
243
244
|
def self.select_from_alias(to_ref, col, to_model_col_name, src_model_id)
|
244
245
|
query = if to_ref.scope
|
245
246
|
to_ref.klass.instance_exec(nil, &to_ref.scope)
|
@@ -247,6 +248,7 @@ module ActiveRecord
|
|
247
248
|
to_ref.klass.all
|
248
249
|
end
|
249
250
|
|
251
|
+
src_model = to_ref.active_record
|
250
252
|
to_table = select_from_alias_table(to_ref.klass, src_model_id.relation)
|
251
253
|
to_model_id = to_ref.klass.arel_attribute(to_model_col_name, to_table)
|
252
254
|
to_column = to_ref.klass.arel_attribute(col, to_table)
|
@@ -254,6 +256,13 @@ module ActiveRecord
|
|
254
256
|
.from(to_table)
|
255
257
|
.where(to_model_id.eq(src_model_id))
|
256
258
|
|
259
|
+
# :type is in the reflection definition (meaning it is polymorphic)
|
260
|
+
if to_ref.type
|
261
|
+
# get the class name (e.g. "Host")
|
262
|
+
polymorphic_type = src_model.base_class.name
|
263
|
+
arel = arel.where(to_ref.klass.arel_attribute(to_ref.type).eq(polymorphic_type))
|
264
|
+
end
|
265
|
+
|
257
266
|
yield arel if block_given?
|
258
267
|
|
259
268
|
Arel.sql("(#{arel.to_sql})")
|
@@ -48,20 +48,46 @@ module ActiveRecord
|
|
48
48
|
def replace_virtual_field_hash(associations)
|
49
49
|
associations.each_with_object({}) do |(parent, child), h|
|
50
50
|
if virtual_field?(parent) # form virtual_attribute => {}
|
51
|
-
|
52
|
-
when String, Symbol
|
53
|
-
h[new_includes] = {}
|
54
|
-
when Array
|
55
|
-
new_includes.each { |association| h[association] = {} }
|
56
|
-
when Hash
|
57
|
-
h.deep_merge!(new_includes)
|
58
|
-
end
|
51
|
+
merge_includes(h, replace_virtual_fields(virtual_includes(parent)))
|
59
52
|
else
|
60
53
|
reflection = reflect_on_association(parent.to_sym)
|
61
|
-
|
54
|
+
if reflection.nil?
|
55
|
+
merge_includes(h, parent)
|
56
|
+
elsif reflection.options[:polymorphic]
|
57
|
+
merge_includes(h, parent => child)
|
58
|
+
else
|
59
|
+
merge_includes(h, parent => reflection.klass.replace_virtual_fields(child) || {})
|
60
|
+
end
|
62
61
|
end
|
63
62
|
end
|
64
63
|
end
|
64
|
+
|
65
|
+
# @param [Hash, Array, String, Symbol] value
|
66
|
+
# @return [Hash]
|
67
|
+
def include_to_hash(value)
|
68
|
+
case value
|
69
|
+
when String, Symbol
|
70
|
+
{value => {}}
|
71
|
+
when Array
|
72
|
+
value.flatten.each_with_object({}) { |k, h| h[k] = {} }
|
73
|
+
when nil
|
74
|
+
{}
|
75
|
+
else
|
76
|
+
value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param [Hash] hash1
|
81
|
+
# @param [Hash] hash2
|
82
|
+
def merge_includes(hash1, hash2)
|
83
|
+
return hash1 if hash2.blank?
|
84
|
+
|
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)
|
89
|
+
end
|
90
|
+
end
|
65
91
|
end
|
66
92
|
end
|
67
93
|
end
|
@@ -75,20 +101,142 @@ module ActiveRecord
|
|
75
101
|
module Associations
|
76
102
|
class Preloader
|
77
103
|
prepend(Module.new {
|
78
|
-
|
79
|
-
|
104
|
+
if ActiveRecord.version.to_s >= "6.0"
|
105
|
+
# preloader.rb active record 6.0
|
106
|
+
# changed:
|
107
|
+
# since grouped_records can return a hash/array, we need to handle those 2 new cases
|
108
|
+
def preloaders_for_reflection(reflection, records, scope, polymorphic_parent)
|
109
|
+
case reflection
|
110
|
+
when Array
|
111
|
+
reflection.flat_map { |ref| preloaders_on(ref, records, scope, polymorphic_parent) }
|
112
|
+
when Hash
|
113
|
+
preloaders_on(reflection, records, scope, polymorphic_parent)
|
114
|
+
else
|
115
|
+
super(reflection, records, scope)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
elsif ActiveRecord.version.to_s >= "5.2" # < 6.0
|
119
|
+
# preloader.rb active record 6.0
|
120
|
+
# else block changed to reflect how 5.2 preloaders_for_one works
|
121
|
+
def preloaders_for_reflection(reflection, records, scope, polymorphic_parent)
|
122
|
+
case reflection
|
123
|
+
when Array
|
124
|
+
reflection.flat_map { |ref| preloaders_on(ref, records, scope, polymorphic_parent) }
|
125
|
+
when Hash
|
126
|
+
preloaders_on(reflection, records, scope, polymorphic_parent)
|
127
|
+
else
|
128
|
+
records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
|
129
|
+
loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
|
130
|
+
loader.run(self)
|
131
|
+
loader
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
80
135
|
|
81
|
-
|
82
|
-
|
83
|
-
|
136
|
+
# preloader.rb active record 6.0
|
137
|
+
# since this deals with polymorphic_parent, it makes everything easier to just define it
|
138
|
+
def preloaders_on(association, records, scope, polymorphic_parent = false)
|
139
|
+
case association
|
140
|
+
when Hash
|
141
|
+
preloaders_for_hash(association, records, scope, polymorphic_parent)
|
142
|
+
when Symbol, String
|
143
|
+
preloaders_for_one(association.to_sym, records, scope, polymorphic_parent)
|
144
|
+
else
|
145
|
+
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if ActiveRecord.version.to_s >= "5.2"
|
151
|
+
# rubocop:disable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
|
152
|
+
# preloader.rb active record 6.0
|
153
|
+
# changed:
|
154
|
+
# passing polymorphic around (and makes 5.2 more similar to 6.0)
|
155
|
+
def preloaders_for_hash(association, records, scope, polymorphic_parent)
|
156
|
+
association.flat_map { |parent, child|
|
157
|
+
grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
|
158
|
+
loaders = preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
|
159
|
+
recs = loaders.flat_map(&:preloaded_records).uniq
|
160
|
+
child_polymorphic_parent = reflection && reflection.respond_to?(:options) && reflection.options[:polymorphic]
|
161
|
+
loaders.concat Array.wrap(child).flat_map { |assoc|
|
162
|
+
preloaders_on assoc, recs, scope, child_polymorphic_parent
|
163
|
+
}
|
164
|
+
loaders
|
165
|
+
end
|
166
|
+
}
|
84
167
|
end
|
85
168
|
|
86
|
-
|
87
|
-
|
88
|
-
|
169
|
+
# preloader.rb active record 6.0
|
170
|
+
# changed:
|
171
|
+
# passing polymorphic_parent to preloaders_for_reflection
|
172
|
+
def preloaders_for_one(association, records, scope, polymorphic_parent)
|
173
|
+
grouped_records(association, records, polymorphic_parent)
|
174
|
+
.flat_map do |reflection, reflection_records|
|
175
|
+
preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
|
176
|
+
end
|
89
177
|
end
|
90
178
|
|
91
|
-
|
179
|
+
# preloader.rb active record 6.0
|
180
|
+
# changed:
|
181
|
+
# different from 5.2. But not called outside these redefined methods here, so it works fine
|
182
|
+
# did add compact to fix a 5.2 double preload nil bug
|
183
|
+
def grouped_records(orig_association, records, polymorphic_parent)
|
184
|
+
h = {}
|
185
|
+
records.compact.each do |record|
|
186
|
+
# each class can resolve virtual_{attributes,includes} differently
|
187
|
+
association = record.class.replace_virtual_fields(orig_association)
|
188
|
+
# 1 line optimization for single element array:
|
189
|
+
association = association.first if association.kind_of?(Array) && association.size == 1
|
190
|
+
|
191
|
+
case association
|
192
|
+
when Symbol, String
|
193
|
+
# 4/24/20 we want to revert #67 once we handle all these error cases in our codebase.
|
194
|
+
reflection = record.class._reflect_on_association(association)
|
195
|
+
display_virtual_attribute_deprecation("#{record.class.name}.#{association} does not exist") if !reflection && !polymorphic_parent
|
196
|
+
next if !reflection || !record.association(association).klass
|
197
|
+
when nil
|
198
|
+
next
|
199
|
+
else # need parent (preloaders_for_{hash,one}) to handle this Array/Hash
|
200
|
+
reflection = association
|
201
|
+
end
|
202
|
+
(h[reflection] ||= []) << record
|
203
|
+
end
|
204
|
+
h
|
205
|
+
end
|
206
|
+
# rubocop:enable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
|
207
|
+
|
208
|
+
def display_virtual_attribute_deprecation(str)
|
209
|
+
short_caller = caller
|
210
|
+
# if debugging is turned on, don't prune the backtrace.
|
211
|
+
# if debugging is off, prune down to the line where the sql is executed
|
212
|
+
# this defaults to false and only displays 1 line number.
|
213
|
+
unless ActiveSupport::Deprecation.debug
|
214
|
+
bc = ActiveSupport::BacktraceCleaner.new
|
215
|
+
bc.add_silencer { |line| line =~ /virtual_fields/ }
|
216
|
+
bc.add_silencer { |line| line =~ /active_record/ }
|
217
|
+
short_caller = bc.clean(caller)
|
218
|
+
end
|
219
|
+
|
220
|
+
ActiveSupport::Deprecation.warn(str, short_caller)
|
221
|
+
end
|
222
|
+
else
|
223
|
+
def preloaders_for_one(association, records, scope)
|
224
|
+
klass_map = records.compact.group_by(&:class)
|
225
|
+
|
226
|
+
# new logic: preload virtual fields / virtual includes
|
227
|
+
loaders = klass_map.keys.group_by { |klass| klass.virtual_includes(association) }.flat_map do |virtuals, klasses|
|
228
|
+
subset = klasses.flat_map { |klass| klass_map[klass] }
|
229
|
+
preload(subset, virtuals)
|
230
|
+
end
|
231
|
+
# /new logic
|
232
|
+
|
233
|
+
records_with_association = klass_map.select { |k, _rs| k.reflect_on_association(association) }.flat_map { |_k, rs| rs }
|
234
|
+
if records_with_association.any?
|
235
|
+
loaders.concat(super(association, records_with_association, scope))
|
236
|
+
end
|
237
|
+
|
238
|
+
loaders
|
239
|
+
end
|
92
240
|
end
|
93
241
|
})
|
94
242
|
end
|
@@ -100,7 +248,7 @@ module ActiveRecord
|
|
100
248
|
# syntax from the original codebase.
|
101
249
|
#
|
102
250
|
# rubocop:disable Style/BlockDelimiters, Layout/SpaceAfterComma, Style/HashSyntax
|
103
|
-
# rubocop:disable Layout/AlignHash
|
251
|
+
# rubocop:disable Layout/AlignHash
|
104
252
|
class JoinDependency
|
105
253
|
def instantiate(result_set, *_, &block)
|
106
254
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
@@ -116,48 +264,7 @@ module ActiveRecord
|
|
116
264
|
column_aliases = aliases.column_aliases(join_root)
|
117
265
|
|
118
266
|
# New Code
|
119
|
-
|
120
|
-
# This monkey patches the ActiveRecord::Associations::JoinDependency to
|
121
|
-
# include columns into the main record that might have been added
|
122
|
-
# through a `select` clause.
|
123
|
-
#
|
124
|
-
# This can be seen with the following:
|
125
|
-
#
|
126
|
-
# Vm.select(Vm.arel_table[Arel.star]).select(:some_vm_virtual_col)
|
127
|
-
# .includes(:tags => {}).references(:tags => {})
|
128
|
-
#
|
129
|
-
# Which will produce a SQL SELECT statement kind of like this:
|
130
|
-
#
|
131
|
-
# SELECT "vms".*,
|
132
|
-
# (<virtual_attribute_arel>) AS some_vm_virtual_col,
|
133
|
-
# "vms"."id" AS t0_r0
|
134
|
-
# "vms"."vendor" AS t0_r1
|
135
|
-
# "vms"."format" AS t0_r1
|
136
|
-
# "vms"."version" AS t0_r1
|
137
|
-
# ...
|
138
|
-
# "tags"."id" AS t1_r0
|
139
|
-
# "tags"."name" AS t1_r1
|
140
|
-
#
|
141
|
-
# This is because rails is trying to reduce the number of queries
|
142
|
-
# needed to fetch all of the records in the include, so it grabs the
|
143
|
-
# columns for both of the tables together to do it. Unfortuantely (or
|
144
|
-
# fortunately... depending on how you look at it), it does not remove
|
145
|
-
# any `.select` columns from the query that is run in the process, so
|
146
|
-
# that is brought along for the ride, but never used when this method
|
147
|
-
# instanciates the objects.
|
148
|
-
#
|
149
|
-
# The "New Code" here simply also instanciates any extra rows that
|
150
|
-
# might have been included in the select (virtual_columns) as well and
|
151
|
-
# brought back with the result set.
|
152
|
-
unless result_set.empty?
|
153
|
-
join_dep_keys = aliases.columns.map(&:right)
|
154
|
-
join_root_aliases = column_aliases.map(&:first)
|
155
|
-
additional_attributes = result_set.first.keys
|
156
|
-
.reject { |k| join_dep_keys.include?(k) }
|
157
|
-
.reject { |k| join_root_aliases.include?(k) }
|
158
|
-
.map { |k| [k, k] }
|
159
|
-
column_aliases += additional_attributes
|
160
|
-
end
|
267
|
+
column_aliases += select_values_from_references(column_aliases, result_set) if result_set.present?
|
161
268
|
# End of New Code
|
162
269
|
|
163
270
|
message_bus = ActiveSupport::Notifications.instrumenter
|
@@ -171,14 +278,64 @@ module ActiveRecord
|
|
171
278
|
result_set.each { |row_hash|
|
172
279
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
173
280
|
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
174
|
-
|
281
|
+
if ActiveRecord.version.to_s < "6.0"
|
282
|
+
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
283
|
+
else
|
284
|
+
construct(parent, join_root, row_hash, seen, model_cache)
|
285
|
+
end
|
175
286
|
}
|
176
287
|
end
|
177
288
|
|
178
289
|
parents.values
|
179
290
|
end
|
180
291
|
# rubocop:enable Style/BlockDelimiters, Layout/SpaceAfterComma, Style/HashSyntax
|
181
|
-
# rubocop:enable Layout/AlignHash
|
292
|
+
# rubocop:enable Layout/AlignHash
|
293
|
+
|
294
|
+
#
|
295
|
+
# This monkey patches the ActiveRecord::Associations::JoinDependency to
|
296
|
+
# include columns into the main record that might have been added
|
297
|
+
# through a `select` clause.
|
298
|
+
#
|
299
|
+
# This can be seen with the following:
|
300
|
+
#
|
301
|
+
# Vm.select(Vm.arel_table[Arel.star]).select(:some_vm_virtual_col)
|
302
|
+
# .includes(:tags => {}).references(:tags)
|
303
|
+
#
|
304
|
+
# Which will produce a SQL SELECT statement kind of like this:
|
305
|
+
#
|
306
|
+
# SELECT "vms".*,
|
307
|
+
# (<virtual_attribute_arel>) AS some_vm_virtual_col,
|
308
|
+
# "vms"."id" AS t0_r0
|
309
|
+
# "vms"."vendor" AS t0_r1
|
310
|
+
# "vms"."format" AS t0_r1
|
311
|
+
# "vms"."version" AS t0_r1
|
312
|
+
# ...
|
313
|
+
# "tags"."id" AS t1_r0
|
314
|
+
# "tags"."name" AS t1_r1
|
315
|
+
#
|
316
|
+
# This is because rails is trying to reduce the number of queries
|
317
|
+
# needed to fetch all of the records in the include, so it grabs the
|
318
|
+
# columns for both of the tables together to do it. Unfortunately (or
|
319
|
+
# fortunately... depending on how you look at it), it does not remove
|
320
|
+
# any `.select` columns from the query that is run in the process, so
|
321
|
+
# that is brought along for the ride, but never used when this method
|
322
|
+
# instanciates the objects.
|
323
|
+
#
|
324
|
+
# The "New Code" here simply also instanciates any extra rows that
|
325
|
+
# might have been included in the select (virtual_columns) as well and
|
326
|
+
# brought back with the result set.
|
327
|
+
def select_values_from_references(column_aliases, result_set)
|
328
|
+
join_dep_keys = aliases.columns.map(&:right)
|
329
|
+
join_root_aliases = column_aliases.map(&:first)
|
330
|
+
additional_attributes = result_set.first.keys
|
331
|
+
.reject { |k| join_dep_keys.include?(k) }
|
332
|
+
.reject { |k| join_root_aliases.include?(k) }
|
333
|
+
if ActiveRecord.version.to_s >= "6.0"
|
334
|
+
additional_attributes.map { |k| Aliases::Column.new(k, k) }
|
335
|
+
else
|
336
|
+
additional_attributes.map { |k| [k, k] }
|
337
|
+
end
|
338
|
+
end
|
182
339
|
end
|
183
340
|
end
|
184
341
|
|
@@ -194,43 +351,100 @@ module ActiveRecord
|
|
194
351
|
|
195
352
|
include(Module.new {
|
196
353
|
# From ActiveRecord::FinderMethods
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
354
|
+
if ActiveRecord.version.to_s >= "5.2"
|
355
|
+
def apply_join_dependency(*args, &block)
|
356
|
+
real = without_virtual_includes
|
357
|
+
if real.equal?(self)
|
358
|
+
super
|
359
|
+
else
|
360
|
+
real.apply_join_dependency(*args, &block)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
else
|
364
|
+
def find_with_associations(&block)
|
365
|
+
real = without_virtual_includes
|
366
|
+
if real.equal?(self)
|
367
|
+
super
|
368
|
+
else
|
369
|
+
real.find_with_associations(&block)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# From ActiveRecord::QueryMethods (rails 5.2 - 6.0)
|
375
|
+
def build_select(arel)
|
376
|
+
if select_values.any?
|
377
|
+
arel.project(*arel_columns(select_values.uniq, true))
|
378
|
+
elsif klass.ignored_columns.any?
|
379
|
+
arel.project(*klass.column_names.map { |field| arel_attribute(field) })
|
201
380
|
else
|
202
|
-
|
381
|
+
arel.project(table[Arel.star])
|
203
382
|
end
|
204
383
|
end
|
205
384
|
|
206
|
-
#
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
385
|
+
# from ActiveRecord::QueryMethods (rails 5.2 - 6.0)
|
386
|
+
def arel_columns(columns, allow_alias = false)
|
387
|
+
columns.flat_map do |field|
|
388
|
+
case field
|
389
|
+
when Symbol
|
390
|
+
arel_column(field.to_s, allow_alias) do |attr_name|
|
391
|
+
connection.quote_table_name(attr_name)
|
392
|
+
end
|
393
|
+
when String
|
394
|
+
arel_column(field, allow_alias, &:itself)
|
395
|
+
when Proc
|
396
|
+
field.call
|
216
397
|
else
|
217
398
|
field
|
218
399
|
end
|
219
400
|
end
|
220
|
-
|
221
|
-
|
401
|
+
end
|
402
|
+
|
403
|
+
# from ActiveRecord::QueryMethods (rails 5.2 - 6.0)
|
404
|
+
def arel_column(field, allow_alias = false, &block)
|
405
|
+
field = klass.attribute_aliases[field] || field
|
406
|
+
from = from_clause.name || from_clause.value
|
407
|
+
|
408
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
409
|
+
arel_attribute(field)
|
410
|
+
elsif virtual_attribute?(field)
|
411
|
+
virtual_attribute_arel_column(field, allow_alias, &block)
|
412
|
+
else
|
413
|
+
yield field
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def virtual_attribute_arel_column(field, allow_alias)
|
418
|
+
arel = arel_attribute(field)
|
419
|
+
if arel.nil?
|
420
|
+
yield field
|
421
|
+
elsif allow_alias && arel && arel.respond_to?(:as) && !arel.kind_of?(Arel::Nodes::As) && !arel.try(:alias)
|
422
|
+
arel.as(connection.quote_column_name(field.to_s))
|
423
|
+
else
|
424
|
+
arel
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# From ActiveRecord::QueryMethods
|
429
|
+
def table_name_matches?(from)
|
430
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
|
431
|
+
end
|
432
|
+
|
433
|
+
# From ActiveRecord::QueryMethods
|
434
|
+
def build_left_outer_joins(manager, outer_joins, *rest)
|
435
|
+
outer_joins = klass.replace_virtual_fields(outer_joins)
|
436
|
+
super if outer_joins.present?
|
222
437
|
end
|
223
438
|
|
224
439
|
# From ActiveRecord::Calculations
|
225
440
|
def calculate(operation, attribute_name)
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
441
|
+
if ActiveRecord.version.to_s < "5.1"
|
442
|
+
if (arel = klass.arel_attribute(attribute_name)) && virtual_attribute?(attribute_name)
|
443
|
+
attribute_name = arel
|
444
|
+
end
|
230
445
|
end
|
231
|
-
# end work around 1
|
232
446
|
|
233
|
-
# allow calculate to work
|
447
|
+
# allow calculate to work with includes and a virtual attribute
|
234
448
|
real = without_virtual_includes
|
235
449
|
return super if real.equal?(self)
|
236
450
|
|
@@ -5,14 +5,9 @@ module VirtualAttributes
|
|
5
5
|
module ClassMethods
|
6
6
|
private
|
7
7
|
|
8
|
-
# define an attribute to
|
9
|
-
def virtual_total(name, relation, options = {})
|
10
|
-
virtual_aggregate(name, relation, :size, nil, options)
|
11
|
-
end
|
12
|
-
|
13
|
-
# define an attribute to calculating the total of a child
|
8
|
+
# define an attribute to calculate the total of a has many relationship
|
14
9
|
#
|
15
|
-
# example
|
10
|
+
# example:
|
16
11
|
#
|
17
12
|
# class ExtManagementSystem
|
18
13
|
# has_many :vms
|
@@ -29,18 +24,46 @@ module VirtualAttributes
|
|
29
24
|
#
|
30
25
|
# # arel == (SELECT COUNT(*) FROM vms where ems.id = vms.ems_id)
|
31
26
|
#
|
32
|
-
|
27
|
+
def virtual_total(name, relation, options = {})
|
28
|
+
define_virtual_aggregate_attribute(name, relation, :count, Arel.star, options)
|
29
|
+
define_method(name) { (has_attribute?(name) ? self[name] : send(relation).try(:size)) || 0 }
|
30
|
+
end
|
31
|
+
|
32
|
+
def virtual_sum(name, relation, column, options = {})
|
33
|
+
define_virtual_aggregate_attribute(name, relation, :sum, column, options)
|
34
|
+
define_virtual_aggregate_method(name, relation, column, :sum)
|
35
|
+
end
|
36
|
+
|
37
|
+
def virtual_minimum(name, relation, column, options = {})
|
38
|
+
define_virtual_aggregate_attribute(name, relation, :minimum, column, options)
|
39
|
+
define_virtual_aggregate_method(name, relation, column, :min, :minimum)
|
40
|
+
end
|
41
|
+
|
42
|
+
def virtual_maximum(name, relation, column, options = {})
|
43
|
+
define_virtual_aggregate_attribute(name, relation, :maximum, column, options)
|
44
|
+
define_virtual_aggregate_method(name, relation, column, :max, :maximum)
|
45
|
+
end
|
46
|
+
|
47
|
+
def virtual_average(name, relation, column, options = {})
|
48
|
+
define_virtual_aggregate_attribute(name, relation, :average, column, options)
|
49
|
+
define_virtual_aggregate_method(name, relation, column, :average) { |values| values.count == 0 ? 0 : values.sum / values.count }
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param method_name
|
53
|
+
# :count :average :minimum :maximum :sum
|
54
|
+
#
|
55
|
+
# example:
|
33
56
|
#
|
34
57
|
# class Hardware
|
35
58
|
# has_many :disks
|
36
|
-
#
|
59
|
+
# virtual_sum :allocated_disk_storage, :disks, :size
|
37
60
|
# end
|
38
61
|
#
|
39
62
|
# generates:
|
40
63
|
#
|
41
64
|
# def allocated_disk_storage
|
42
65
|
# if disks.loaded?
|
43
|
-
# disks.
|
66
|
+
# disks.map(&:size).compact.sum
|
44
67
|
# else
|
45
68
|
# disks.sum(:size) || 0
|
46
69
|
# end
|
@@ -51,7 +74,11 @@ module VirtualAttributes
|
|
51
74
|
# # arel => (SELECT sum("disks"."size") where "hardware"."id" = "disks"."hardware_id")
|
52
75
|
|
53
76
|
def virtual_aggregate(name, relation, method_name = :sum, column = nil, options = {})
|
54
|
-
|
77
|
+
return virtual_total(name, relation, options) if method_name == :size
|
78
|
+
return virtual_sum(name, relation, column, options) if method_name == :sum
|
79
|
+
end
|
80
|
+
|
81
|
+
def define_virtual_aggregate_attribute(name, relation, method_name, column, options)
|
55
82
|
reflection = reflect_on_association(relation)
|
56
83
|
|
57
84
|
if options.key?(:arel)
|
@@ -69,54 +96,59 @@ module VirtualAttributes
|
|
69
96
|
end
|
70
97
|
end
|
71
98
|
|
72
|
-
def define_virtual_aggregate_method(name, relation,
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
# aggregates are not smart enough to handle virtual attributes
|
86
|
-
arel_column = rel.klass.arel_attribute(column)
|
87
|
-
rel.try(method_name, arel_column) || 0
|
88
|
-
end
|
89
|
-
end
|
99
|
+
def define_virtual_aggregate_method(name, relation, column, ruby_method_name, arel_method_name = ruby_method_name)
|
100
|
+
define_method(name) do
|
101
|
+
if has_attribute?(name)
|
102
|
+
self[name] || 0
|
103
|
+
elsif (rel = send(relation)).loaded?
|
104
|
+
values = rel.map { |t| t.send(column) }.compact
|
105
|
+
if block_given?
|
106
|
+
yield values
|
107
|
+
else
|
108
|
+
values.blank? ? nil : values.send(ruby_method_name)
|
109
|
+
end
|
110
|
+
else
|
111
|
+
rel.try(arel_method_name, column) || 0
|
90
112
|
end
|
91
113
|
end
|
92
114
|
end
|
93
115
|
|
94
116
|
def virtual_aggregate_arel(reflection, method_name, column)
|
95
|
-
return unless reflection && reflection.macro == :has_many
|
117
|
+
return unless reflection && reflection.macro == :has_many
|
118
|
+
|
119
|
+
# need db access for the reflection join_keys, so delaying all this key lookup until call time
|
96
120
|
lambda do |t|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
121
|
+
# strings and symbols are converted across, arel objects are not
|
122
|
+
column = reflection.klass.arel_attribute(column) unless column.respond_to?(:count)
|
123
|
+
|
124
|
+
# query: SELECT COUNT(*) FROM main_table JOIN foreign_table ON main_table.id = foreign_table.id JOIN ...
|
125
|
+
relation_query = joins(reflection.name).select(column.send(method_name))
|
126
|
+
query = relation_query.arel
|
127
|
+
|
128
|
+
# algorithm:
|
129
|
+
# - remove main_table from this sub query. (it is already in the primary query)
|
130
|
+
# - move the foreign_table from the JOIN to the FROM clause
|
131
|
+
# - move the main_table.id = foreign_table.id from the ON clause to the WHERE clause
|
132
|
+
|
133
|
+
# query: SELECT COUNT(*) FROM main_table [ ] JOIN ...
|
134
|
+
join = query.source.right.shift
|
135
|
+
# query: SELECT COUNT(*) FROM [foreign_table] JOIN ...
|
136
|
+
query.source.left = join.left
|
137
|
+
# query: SELECT COUNT(*) FROM foreign_table JOIN ... [WHERE main_table.id = foreign_table.id]
|
138
|
+
query.where(join.right.expr)
|
111
139
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
140
|
+
# convert bind variables from ? to actual values. otherwise, sql is incomplete
|
141
|
+
conn = connection
|
142
|
+
sql = if ActiveRecord.version.to_s >= "5.2"
|
143
|
+
conn.unprepared_statement { conn.to_sql(query) }
|
144
|
+
else
|
145
|
+
conn.unprepared_statement { conn.to_sql(query, relation_query.bound_attributes) }
|
146
|
+
end
|
118
147
|
|
119
|
-
|
148
|
+
# add () around query
|
149
|
+
query = t.grouping(Arel::Nodes::SqlLiteral.new(sql))
|
150
|
+
# add coalesce to ensure correct value comes out
|
151
|
+
t.grouping(Arel::Nodes::NamedFunction.new('COALESCE', [query, Arel::Nodes::SqlLiteral.new("0")]))
|
120
152
|
end
|
121
153
|
end
|
122
154
|
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:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keenan Brock
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: db-query-matchers
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.10'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rake
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,9 +132,10 @@ files:
|
|
104
132
|
- activerecord-virtual_attributes.gemspec
|
105
133
|
- bin/console
|
106
134
|
- bin/setup
|
107
|
-
- gemfiles/
|
108
|
-
- gemfiles/
|
109
|
-
- gemfiles/
|
135
|
+
- gemfiles/gemfile_50.gemfile
|
136
|
+
- gemfiles/gemfile_51.gemfile
|
137
|
+
- gemfiles/gemfile_52.gemfile
|
138
|
+
- gemfiles/gemfile_60.gemfile
|
110
139
|
- init.rb
|
111
140
|
- lib/active_record-virtual_attributes.rb
|
112
141
|
- lib/active_record/virtual_attributes.rb
|
@@ -128,7 +157,7 @@ metadata:
|
|
128
157
|
homepage_uri: https://github.com/ManageIQ/activerecord-virtual_attributes
|
129
158
|
source_code_uri: https://github.com/ManageIQ/activerecord-virtual_attributes
|
130
159
|
changelog_uri: https://github.com/ManageIQ/activerecord-virtual_attributes/blob/master/CHANGELOG.md
|
131
|
-
post_install_message:
|
160
|
+
post_install_message:
|
132
161
|
rdoc_options: []
|
133
162
|
require_paths:
|
134
163
|
- lib
|
@@ -143,9 +172,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
172
|
- !ruby/object:Gem::Version
|
144
173
|
version: '0'
|
145
174
|
requirements: []
|
146
|
-
rubyforge_project:
|
175
|
+
rubyforge_project:
|
147
176
|
rubygems_version: 2.7.6.2
|
148
|
-
signing_key:
|
177
|
+
signing_key:
|
149
178
|
specification_version: 4
|
150
179
|
summary: Access non-sql attributes from sql
|
151
180
|
test_files: []
|