active_record-mti 0.2.1 → 0.3.0.pre.rc1
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 +31 -9
- data/CHANGELOG.md +35 -0
- data/Gemfile +17 -0
- data/README.md +9 -7
- data/Rakefile +4 -0
- data/active_record-mti.gemspec +10 -13
- data/gemfiles/activerecord-4.0.Gemfile +3 -0
- data/gemfiles/activerecord-4.1.Gemfile +3 -0
- data/gemfiles/activerecord-4.2.Gemfile +3 -0
- data/gemfiles/activerecord-5.0.Gemfile +3 -0
- data/gemfiles/activerecord-5.1.Gemfile +3 -0
- data/lib/active_record/mti/calculations.rb +10 -109
- data/lib/active_record/mti/connection_adapters/postgresql/schema_statements.rb +9 -39
- data/lib/active_record/mti/inheritance.rb +53 -41
- data/lib/active_record/mti/model_schema.rb +8 -0
- data/lib/active_record/mti/query_methods.rb +34 -12
- data/lib/active_record/mti/railtie.rb +1 -1
- data/lib/active_record/mti/version.rb +1 -1
- data/lib/active_record/mti.rb +7 -2
- data/spec/active_record/calculations_spec.rb +48 -0
- data/spec/active_record/mti/inheritence_spec.rb +145 -0
- data/spec/active_record/mti_spec.rb +15 -0
- data/spec/schema.rb +42 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/models.rb +34 -0
- metadata +36 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aed8fa7772f145269a995f4e2d139ebbb8670023
|
4
|
+
data.tar.gz: c40c16ae6a90a66aa9dc3aa6839fe9678761218b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 520d9654c833f837d3cb93e2320cfb44e86c18802f3d4df1e4b2edccbbec9b9621b257c372eb0ac0dfe288c92135d07593cb3af80c910bd3679a50319efd1562
|
7
|
+
data.tar.gz: 7e51e4286a33db38af13e12ee6a5bb05f43519b4d8cb7e2be1bbfeef71c46caa370a25d691e80eccf9f61871717c0d130f4861f370c7d4aadcbd8e1639edb1ee
|
data/.travis.yml
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
|
-
|
4
|
-
- 2.0
|
5
|
-
- 2.1
|
6
|
-
- 2.2
|
7
|
-
- 2.3.1
|
8
|
-
- 2.4.0
|
3
|
+
|
9
4
|
cache: bundler
|
10
5
|
script:
|
11
6
|
- bundle exec rspec
|
@@ -16,6 +11,33 @@ after_success:
|
|
16
11
|
addons:
|
17
12
|
postgresql: "9.3"
|
18
13
|
|
19
|
-
|
20
|
-
-
|
21
|
-
-
|
14
|
+
rvm:
|
15
|
+
- 2.0
|
16
|
+
- 2.1
|
17
|
+
- 2.2
|
18
|
+
- 2.3
|
19
|
+
- 2.4
|
20
|
+
|
21
|
+
gemfile:
|
22
|
+
- gemfiles/activerecord-4.0.Gemfile
|
23
|
+
- gemfiles/activerecord-4.1.Gemfile
|
24
|
+
- gemfiles/activerecord-4.2.Gemfile
|
25
|
+
- gemfiles/activerecord-5.0.Gemfile
|
26
|
+
- gemfiles/activerecord-5.1.Gemfile
|
27
|
+
|
28
|
+
matrix:
|
29
|
+
exclude:
|
30
|
+
- rvm: 2.0
|
31
|
+
gemfile: gemfiles/activerecord-5.0.Gemfile
|
32
|
+
- rvm: 2.0
|
33
|
+
gemfile: gemfiles/activerecord-5.1.Gemfile
|
34
|
+
|
35
|
+
- rvm: 2.1
|
36
|
+
gemfile: gemfiles/activerecord-5.0.Gemfile
|
37
|
+
- rvm: 2.1
|
38
|
+
gemfile: gemfiles/activerecord-5.1.Gemfile
|
39
|
+
|
40
|
+
- rvm: 2.4
|
41
|
+
gemfile: gemfiles/activerecord-4.0.Gemfile
|
42
|
+
- rvm: 2.4
|
43
|
+
gemfile: gemfiles/activerecord-4.1.Gemfile
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# ActiveRecord::MTI
|
2
|
+
|
3
|
+
## 0.3.0 _(November 7th 2017)_
|
4
|
+
- Greatly improved future-proofing injection strategy.
|
5
|
+
- No longer overwriting (and maintaining) ActiveRecord Calculation sub-routines.
|
6
|
+
- Instead of injecting at `build_select`, we're injecting at `build_arel` with one additional new sub-routine (`build_mti`)
|
7
|
+
- `build_mti` sub-routine detects if an MTI projection is needed based on grouping and selecting from query being built.
|
8
|
+
|
9
|
+
## 0.2.1 _(September 20th 2017)_
|
10
|
+
- More reliable class discrimination
|
11
|
+
- Improved view support
|
12
|
+
|
13
|
+
## 0.1.1 _(June 23rd 2017)_
|
14
|
+
- Fixes issue where inheritance check is called multiple times.
|
15
|
+
- Can handle a (simple) view that references a table that uses MTI
|
16
|
+
|
17
|
+
## 0.1.0 _(May 12th 2017)_
|
18
|
+
- PSQL Adapter now responds to version
|
19
|
+
- Improved column pulls from DB
|
20
|
+
|
21
|
+
## 0.0.7 _(May 11th 2017)_
|
22
|
+
- Specs!
|
23
|
+
- Breaking Change: must call `uses_mti` in models
|
24
|
+
- MTI class discrimination happens before STI
|
25
|
+
- More reliable projection/unprojection
|
26
|
+
- Improved table_name inference
|
27
|
+
|
28
|
+
## 0.0.6 _(March 28th 2017)_
|
29
|
+
- Improve how `ActiveRecord::MTI` is injected into Rails
|
30
|
+
|
31
|
+
## 0.0.5 _(September 27th 2016)_
|
32
|
+
- Allow SQL calculations (like `sum` and `count`) to execute by removing unneeded MTI projections
|
33
|
+
|
34
|
+
## 0.0.2 _(September 21st 2016)_
|
35
|
+
- Default value to return when finding MTI class
|
data/Gemfile
CHANGED
@@ -1,3 +1,20 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
gemspec
|
4
|
+
|
5
|
+
group :test do
|
6
|
+
|
7
|
+
# Generates coverage stats of specs
|
8
|
+
gem 'simplecov'
|
9
|
+
|
10
|
+
# Publishes coverage to codeclimate
|
11
|
+
gem 'codeclimate-test-reporter'
|
12
|
+
|
13
|
+
# Gives CircleCI more perspective on our tests
|
14
|
+
gem 'rspec_junit_formatter'
|
15
|
+
|
16
|
+
gem 'rspec'
|
17
|
+
|
18
|
+
gem 'database_cleaner'
|
19
|
+
|
20
|
+
end
|
data/README.md
CHANGED
@@ -5,9 +5,11 @@
|
|
5
5
|
|
6
6
|
# ActiveRecord::MTI
|
7
7
|
|
8
|
-
|
8
|
+
ActiveRecord support for PostgreSQL's native inherited tables (multi-table inheritance)
|
9
9
|
|
10
|
-
|
10
|
+
Compatible with ActiveRecord `4.0`, `4.1`, `4.2`, `5.0`, `5.1`
|
11
|
+
|
12
|
+
Confirmed production use in `4.2`
|
11
13
|
|
12
14
|
## Usage
|
13
15
|
|
@@ -25,6 +27,11 @@ Or install it yourself as:
|
|
25
27
|
|
26
28
|
### Application Code
|
27
29
|
|
30
|
+
ActiveRecord queries work as usual with the following differences:
|
31
|
+
|
32
|
+
* You need to specify which model represents the base of your multi table inheritance tree. To do so, add `uses_mti` to the model definition of the base class.
|
33
|
+
* The default query of "*" is changed to include the OID of each row for subclass discrimination. The default select will be `SELECT "accounts"."tableoid" AS tableoid, "accounts".*` (for example)
|
34
|
+
|
28
35
|
```ruby
|
29
36
|
class Account < ::ActiveRecord::Base
|
30
37
|
uses_mti
|
@@ -40,11 +47,6 @@ class Developer < Account
|
|
40
47
|
end
|
41
48
|
```
|
42
49
|
|
43
|
-
ActiveRecord queries work as usual with the following differences:
|
44
|
-
|
45
|
-
* You need to specify which model represents the base of your multi table inheritance tree. To do so, insert `uses_mti` in the model definition of the base class.
|
46
|
-
* The default query of "*" is changed to include the OID of each row for subclass discrimination. The default select will be `SELECT cast("accounts"."tableoid"::regclass AS text), "accounts".*` (for example)
|
47
|
-
|
48
50
|
### Migrations
|
49
51
|
|
50
52
|
In your migrations define a table to inherit from another table:
|
data/Rakefile
CHANGED
data/active_record-mti.gemspec
CHANGED
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["dale@twilightcoders.net"]
|
11
11
|
|
12
12
|
spec.summary = %q{Multi Table Inheritance for PostgreSQL in Rails}
|
13
|
-
spec.description = %q{
|
14
|
-
spec.homepage = ""
|
13
|
+
spec.description = %q{Gives ActiveRecord support for PostgreSQL's native inherited tables}
|
14
|
+
spec.homepage = "https://github.com/twilightcoders/active_record-mti"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
17
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
@@ -22,23 +22,20 @@ Gem::Specification.new do |spec|
|
|
22
22
|
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
23
23
|
end
|
24
24
|
|
25
|
-
spec.files = `git ls-files -z`.split("\x0")
|
26
|
-
spec.bindir = '
|
27
|
-
spec.executables = spec.files.grep(%r{^
|
28
|
-
spec.
|
25
|
+
spec.files = `git ls-files -z`.split("\x0")
|
26
|
+
spec.bindir = 'bin'
|
27
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
28
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
29
|
+
spec.require_paths = ['lib', 'spec']
|
29
30
|
|
30
|
-
rails_versions = ['>= 4
|
31
|
-
spec.required_ruby_version = '>= 2.0
|
31
|
+
rails_versions = ['>= 4', '< 6']
|
32
|
+
spec.required_ruby_version = '>= 2.0'
|
32
33
|
|
33
34
|
spec.add_runtime_dependency 'pg', '~> 0'
|
34
35
|
spec.add_runtime_dependency 'activerecord', rails_versions
|
35
36
|
|
36
|
-
spec.add_development_dependency 'pry-byebug'
|
37
|
+
spec.add_development_dependency 'pry-byebug', '~> 3'
|
37
38
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
38
39
|
spec.add_development_dependency 'rake', '~> 10.0'
|
39
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
40
|
-
spec.add_development_dependency 'simplecov', '~> 0.1'
|
41
|
-
spec.add_development_dependency 'codeclimate-test-reporter', '~> 1.0'
|
42
|
-
spec.add_development_dependency 'database_cleaner', '~> 1.6'
|
43
40
|
|
44
41
|
end
|
@@ -1,121 +1,22 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module MTI
|
3
3
|
module Calculations
|
4
|
+
extend ActiveSupport::Concern
|
4
5
|
|
5
6
|
private
|
6
7
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
if group_attrs.first.respond_to?(:to_sym)
|
11
|
-
association = @klass._reflect_on_association(group_attrs.first)
|
12
|
-
associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
|
13
|
-
group_fields = Array(associated ? association.foreign_key : group_attrs)
|
14
|
-
else
|
15
|
-
group_fields = group_attrs
|
8
|
+
def perform_calculation(*args)
|
9
|
+
result = swap_and_restore_tableoid_cast(true) do
|
10
|
+
super
|
16
11
|
end
|
17
|
-
group_fields = arel_columns(group_fields)
|
18
|
-
|
19
|
-
group_aliases = group_fields.map { |field|
|
20
|
-
column_alias_for(field)
|
21
|
-
}
|
22
|
-
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
|
23
|
-
[aliaz, field]
|
24
|
-
}
|
25
|
-
|
26
|
-
group = group_fields
|
27
|
-
|
28
|
-
if operation == 'count' && column_name == :all
|
29
|
-
aggregate_alias = 'count_all'
|
30
|
-
else
|
31
|
-
aggregate_alias = column_alias_for([operation, column_name].join(' '))
|
32
|
-
end
|
33
|
-
|
34
|
-
select_values = [
|
35
|
-
operation_over_aggregate_column(
|
36
|
-
aggregate_column(column_name),
|
37
|
-
operation,
|
38
|
-
distinct).as(aggregate_alias)
|
39
|
-
]
|
40
|
-
select_values += select_values unless having_values.empty?
|
41
|
-
|
42
|
-
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
43
|
-
if field.respond_to?(:as)
|
44
|
-
field.as(aliaz)
|
45
|
-
else
|
46
|
-
"#{field} AS #{aliaz}"
|
47
|
-
end
|
48
|
-
}
|
49
|
-
|
50
|
-
relation = except(:group)
|
51
|
-
relation.group_values = group
|
52
|
-
relation.select_values = select_values
|
53
|
-
|
54
|
-
# Remove our cast otherwise PSQL will insist that it be included in the GROUP
|
55
|
-
relation.arel.projections.select!{ |p| p.to_s != tableoid_cast(klass) } if @klass.using_multi_table_inheritance?
|
56
|
-
|
57
|
-
calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
|
58
|
-
|
59
|
-
if association
|
60
|
-
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
61
|
-
key_records = association.klass.base_class.find(key_ids)
|
62
|
-
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
63
|
-
end
|
64
|
-
|
65
|
-
Hash[calculated_data.map do |row|
|
66
|
-
key = group_columns.map { |aliaz, col_name|
|
67
|
-
column = calculated_data.column_types.fetch(aliaz) do
|
68
|
-
type_for(col_name)
|
69
|
-
end
|
70
|
-
type_cast_calculated_value(row[aliaz], column)
|
71
|
-
}
|
72
|
-
key = key.first if key.size == 1
|
73
|
-
key = key_records[key] if associated
|
74
|
-
|
75
|
-
column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
|
76
|
-
[key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
|
77
|
-
end]
|
78
12
|
end
|
79
13
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
bind_values = nil
|
87
|
-
|
88
|
-
if operation == "count" && (relation.limit_value || relation.offset_value)
|
89
|
-
# Shortcut when limit is zero.
|
90
|
-
return 0 if relation.limit_value == 0
|
91
|
-
|
92
|
-
query_builder = build_count_subquery(relation, column_name, distinct)
|
93
|
-
bind_values = query_builder.bind_values + relation.bind_values
|
94
|
-
else
|
95
|
-
column = aggregate_column(column_name)
|
96
|
-
|
97
|
-
select_value = operation_over_aggregate_column(column, operation, distinct)
|
98
|
-
|
99
|
-
column_alias = select_value.alias
|
100
|
-
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
101
|
-
relation.select_values = [select_value]
|
102
|
-
|
103
|
-
# Remove our cast otherwise PSQL will insist that it be included in the GROUP
|
104
|
-
# Somewhere between line 82 and 101 relation.arel.projections gets reset :/
|
105
|
-
relation.arel.projections.select!{ |p| p != tableoid_cast(klass) } if @klass.using_multi_table_inheritance?
|
106
|
-
|
107
|
-
query_builder = relation.arel
|
108
|
-
bind_values = query_builder.bind_values + relation.bind_values
|
109
|
-
end
|
110
|
-
|
111
|
-
result = @klass.connection.select_all(query_builder, nil, bind_values)
|
112
|
-
row = result.first
|
113
|
-
value = row && row.values.first
|
114
|
-
column = result.column_types.fetch(column_alias) do
|
115
|
-
type_for(column_name)
|
116
|
-
end
|
117
|
-
|
118
|
-
type_cast_calculated_value(value, column, operation)
|
14
|
+
def swap_and_restore_tableoid_cast(value, &block)
|
15
|
+
orignal_value = Thread.current['skip_tableoid_cast']
|
16
|
+
Thread.current['skip_tableoid_cast'] = value
|
17
|
+
return_value = yield
|
18
|
+
Thread.current['skip_tableoid_cast'] = orignal_value
|
19
|
+
return return_value
|
119
20
|
end
|
120
21
|
|
121
22
|
end
|
@@ -27,43 +27,18 @@ module ActiveRecord
|
|
27
27
|
table_name = %Q("#{schema}"."#{table_name}")
|
28
28
|
end
|
29
29
|
|
30
|
-
if
|
31
|
-
options[:options] = [
|
30
|
+
if inherited_table = options.delete(:inherits)
|
31
|
+
# options[:options] = options[:options].sub("INHERITS", "() INHERITS") if td.columns.empty?
|
32
|
+
options[:options] = [%Q(INHERITS ("#{inherited_table}")), options[:options]].compact.join
|
32
33
|
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
if options[:id] != false && !options[:as]
|
37
|
-
pk = options.fetch(:primary_key) do
|
38
|
-
Base.get_primary_key table_name.to_s.singularize
|
39
|
-
end
|
40
|
-
|
41
|
-
if pk.is_a?(Array)
|
42
|
-
td.primary_keys pk
|
43
|
-
else
|
44
|
-
td.primary_key pk, options.fetch(:id, :primary_key), options
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
yield td if block_given?
|
49
|
-
|
50
|
-
if options[:force] && data_source_exists?(table_name)
|
51
|
-
drop_table(table_name, options)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Rails 5 wont create an empty column list which we might have if we're
|
55
|
-
# working with inherited tables. So we need to do that manually
|
56
|
-
sql = schema_creation.accept(td)
|
57
|
-
# sql = sql.sub("INHERITS", "() INHERITS") if td.columns.empty?
|
58
|
-
|
59
|
-
result = execute sql
|
60
|
-
|
61
|
-
if parent_table
|
62
|
-
parent_table_primary_key = primary_key(parent_table)
|
63
|
-
execute %Q(ALTER TABLE "#{table_name}" ADD PRIMARY KEY ("#{parent_table_primary_key}"))
|
35
|
+
results = super(table_name, options)
|
64
36
|
|
37
|
+
if inherited_table
|
38
|
+
inherited_table_primary_key = primary_key(inherited_table)
|
39
|
+
execute %Q(ALTER TABLE "#{table_name}" ADD PRIMARY KEY ("#{inherited_table_primary_key}"))
|
65
40
|
|
66
|
-
indexes(
|
41
|
+
indexes(inherited_table).each do |index|
|
67
42
|
attributes = index.to_h.slice(:unique, :using, :where, :orders)
|
68
43
|
|
69
44
|
# Why rails insists on being inconsistant with itself is beyond me.
|
@@ -71,14 +46,9 @@ module ActiveRecord
|
|
71
46
|
|
72
47
|
add_index table_name, index.columns, attributes
|
73
48
|
end
|
74
|
-
# triggers_for_table(parent_table).each do |trigger|
|
75
|
-
# name = trigger.first
|
76
|
-
# definition = trigger.second.merge(on: table_name)
|
77
|
-
# create_trigger name, definition
|
78
|
-
# end
|
79
49
|
end
|
80
50
|
|
81
|
-
|
51
|
+
results
|
82
52
|
end
|
83
53
|
|
84
54
|
# Parent of inherited table
|
@@ -1,32 +1,13 @@
|
|
1
1
|
require 'active_support/concern'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
# == Multi
|
5
|
-
#
|
6
|
-
# PostgreSQL allows for table inheritance. To enable this in ActiveRecord, ensure that the
|
7
|
-
# inheritance_column is named "tableoid" (can be changed by setting <tt>Base.inheritance_column</tt>).
|
8
|
-
# This means that an inheritance looking like this:
|
9
|
-
#
|
10
|
-
# class Company < ActiveRecord::Base;
|
11
|
-
# self.inheritance_column = 'tableoid'
|
12
|
-
# end
|
13
|
-
# class Firm < Company; end
|
14
|
-
# class Client < Company; end
|
15
|
-
# class PriorityClient < Client; end
|
16
|
-
#
|
17
|
-
# When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
|
18
|
-
# the firms table which inherits from companies. You can then fetch this row again using
|
19
|
-
# <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
|
20
|
-
#
|
21
|
-
# Note, all the attributes for all the cases are kept in the same table. Read more:
|
22
|
-
# http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
|
23
|
-
#
|
4
|
+
# == Multi-Table Inheritance
|
24
5
|
module MTI
|
25
6
|
module Inheritance
|
26
7
|
extend ActiveSupport::Concern
|
27
|
-
@mti_tableoids = {}
|
28
8
|
|
29
9
|
included do
|
10
|
+
@@mti_tableoids = {}
|
30
11
|
scope :discern_inheritance, -> {
|
31
12
|
|
32
13
|
}
|
@@ -34,12 +15,14 @@ module ActiveRecord
|
|
34
15
|
|
35
16
|
module ClassMethods
|
36
17
|
|
18
|
+
@uses_mti = nil
|
19
|
+
@mti_setup = false
|
20
|
+
@mti_type_column = nil
|
21
|
+
|
37
22
|
def uses_mti(custom_table_name = nil, inheritance_column = nil)
|
38
23
|
self.inheritance_column = inheritance_column
|
39
24
|
|
40
25
|
@uses_mti = true
|
41
|
-
@mti_setup = false
|
42
|
-
@mti_tableoid_projection = nil
|
43
26
|
@tableoid_column = nil
|
44
27
|
end
|
45
28
|
|
@@ -48,8 +31,14 @@ module ActiveRecord
|
|
48
31
|
end
|
49
32
|
|
50
33
|
def uses_mti?
|
51
|
-
|
52
|
-
|
34
|
+
inheritance_check = check_inheritance_of(@table_name) unless @mti_setup
|
35
|
+
|
36
|
+
if @uses_mti.nil? && @uses_mti = inheritance_check
|
37
|
+
descendants.each do |d|
|
38
|
+
d.uses_mti?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
53
42
|
@uses_mti
|
54
43
|
end
|
55
44
|
|
@@ -57,17 +46,17 @@ module ActiveRecord
|
|
57
46
|
@tableoid_column != false
|
58
47
|
end
|
59
48
|
|
60
|
-
def
|
61
|
-
@
|
49
|
+
def mti_type_column
|
50
|
+
@mti_type_column
|
62
51
|
end
|
63
52
|
|
64
|
-
def
|
65
|
-
@
|
53
|
+
def mti_type_column=(value)
|
54
|
+
@mti_type_column = value
|
66
55
|
end
|
67
56
|
|
68
57
|
private
|
69
58
|
|
70
|
-
def
|
59
|
+
def check_inheritance_of(table_name)
|
71
60
|
ActiveRecord::MTI.logger.debug "Trying to check inheritance of table with no table name (#{self})" unless table_name
|
72
61
|
return nil unless table_name
|
73
62
|
|
@@ -82,21 +71,23 @@ module ActiveRecord
|
|
82
71
|
LEFT JOIN pg_catalog.pg_class AS cl_d ON cl_d.oid = d.refobjid
|
83
72
|
WHERE inhrelid = COALESCE(cl_d.relname, 'public.#{table_name}')::regclass::oid
|
84
73
|
OR inhparent = COALESCE(cl_d.relname, 'public.#{table_name}')::regclass::oid
|
85
|
-
);
|
74
|
+
) AS uses_inheritance;
|
86
75
|
SQL
|
87
76
|
|
88
|
-
|
77
|
+
uses_inheritance = ActiveRecord::MTI.testify(result.try(:first)['uses_inheritance'])
|
89
78
|
|
90
|
-
register_tableoid(table_name
|
79
|
+
register_tableoid(table_name) if uses_inheritance
|
91
80
|
|
92
81
|
@mti_setup = true
|
93
82
|
# Some versions of PSQL return {"?column?"=>"t"}
|
94
83
|
# instead of {"exists"=>"t"}, so we're saying screw it,
|
95
84
|
# just give me the first value of whatever is returned
|
96
|
-
|
85
|
+
|
86
|
+
# Ensure a boolean is returned
|
87
|
+
return uses_inheritance == true
|
97
88
|
end
|
98
89
|
|
99
|
-
def register_tableoid(table_name
|
90
|
+
def register_tableoid(table_name)
|
100
91
|
|
101
92
|
tableoid_query = connection.execute(<<-SQL
|
102
93
|
SELECT '#{table_name}'::regclass::oid AS tableoid, (SELECT EXISTS (
|
@@ -109,13 +100,14 @@ module ActiveRecord
|
|
109
100
|
SQL
|
110
101
|
).first
|
111
102
|
tableoid = tableoid_query['tableoid']
|
112
|
-
@tableoid_column = tableoid_query['has_tableoid_column']
|
103
|
+
@tableoid_column = ActiveRecord::MTI.testify(tableoid_query['has_tableoid_column'])
|
113
104
|
|
114
105
|
if (has_tableoid_column?)
|
115
|
-
ActiveRecord::MTI.logger.debug "#{table_name} has tableoid column!"
|
116
|
-
|
106
|
+
ActiveRecord::MTI.logger.debug "#{table_name} has tableoid column! (#{tableoid})"
|
107
|
+
add_tableoid_column
|
108
|
+
@mti_type_column = arel_table[:tableoid]
|
117
109
|
else
|
118
|
-
@
|
110
|
+
@mti_type_column = nil
|
119
111
|
end
|
120
112
|
|
121
113
|
Inheritance.add_mti(tableoid, self)
|
@@ -156,14 +148,34 @@ module ActiveRecord
|
|
156
148
|
sti_column.in(sti_names)
|
157
149
|
end
|
158
150
|
end
|
151
|
+
|
152
|
+
def add_tableoid_column
|
153
|
+
if self.respond_to? :attribute
|
154
|
+
self.attribute :tableoid, get_integer_oid_class.new
|
155
|
+
else
|
156
|
+
columns.unshift ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new('tableoid', nil, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Integer.new, "oid", false)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Rails decided to make a breaking change in it's 4.x series :P
|
161
|
+
def get_integer_oid_class
|
162
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Integer
|
163
|
+
rescue NameError
|
164
|
+
begin
|
165
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::Integer
|
166
|
+
rescue NameError
|
167
|
+
::ActiveModel::Type::Integer
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
159
171
|
end
|
160
172
|
|
161
173
|
def self.add_mti(tableoid, klass)
|
162
|
-
|
174
|
+
@@mti_tableoids[tableoid.to_s.to_sym] = klass
|
163
175
|
end
|
164
176
|
|
165
177
|
def self.find_mti(tableoid)
|
166
|
-
|
178
|
+
@@mti_tableoids[tableoid.to_s.to_sym]
|
167
179
|
end
|
168
180
|
|
169
181
|
end
|
@@ -44,6 +44,14 @@ module ActiveRecord
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def full_table_name_prefix #:nodoc:
|
48
|
+
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
|
49
|
+
end
|
50
|
+
|
51
|
+
def full_table_name_suffix #:nodoc:
|
52
|
+
(parents.detect {|p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
|
53
|
+
end
|
54
|
+
|
47
55
|
private
|
48
56
|
|
49
57
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
@@ -1,26 +1,48 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module MTI
|
3
3
|
module QueryMethods
|
4
|
+
extend ActiveSupport::Concern
|
4
5
|
|
5
|
-
|
6
|
+
def build_arel
|
7
|
+
select_by_tableoid = select_values.delete(:tableoid) == :tableoid
|
8
|
+
group_by_tableoid = group_values.delete(:tableoid) == :tableoid
|
6
9
|
|
7
|
-
|
8
|
-
def build_select(arel)
|
9
|
-
if @klass.using_multi_table_inheritance? && @klass.has_tableoid_column?
|
10
|
-
arel.project(tableoid_cast(@klass))
|
11
|
-
end
|
10
|
+
arel = super
|
12
11
|
|
13
|
-
if
|
14
|
-
arel.project(
|
15
|
-
|
16
|
-
arel.project(@klass.arel_table[Arel.star])
|
12
|
+
if tableoid?(@klass) || group_by_tableoid || select_by_tableoid
|
13
|
+
arel.project(tableoid_project(@klass))
|
14
|
+
arel.group(tableoid_group(@klass)) if group_values.any? || group_by_tableoid
|
17
15
|
end
|
16
|
+
|
17
|
+
arel
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def tableoid?(klass)
|
23
|
+
!Thread.current['skip_tableoid_cast'] &&
|
24
|
+
@klass.using_multi_table_inheritance? &&
|
25
|
+
@klass.has_tableoid_column?
|
18
26
|
end
|
19
27
|
|
20
|
-
def
|
28
|
+
def tableoid_project?(klass)
|
29
|
+
tableoid?(klass) &&
|
30
|
+
(group_values - [:tableoid]).any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def tableoid_group?(klass)
|
34
|
+
tableoid?(klass) &&
|
35
|
+
group_values.any?
|
36
|
+
end
|
37
|
+
|
38
|
+
def tableoid_project(klass)
|
21
39
|
# Arel::Nodes::NamedFunction.new('CAST', [klass.arel_table[:tableoid].as('regclass')])
|
22
40
|
# Arel::Nodes::NamedFunction.new('CAST', [@klass.arel_table['tableoid::regclass'].as('regclass')])
|
23
|
-
@klass.
|
41
|
+
@klass.mti_type_column.as('tableoid')
|
42
|
+
end
|
43
|
+
|
44
|
+
def tableoid_group(klass)
|
45
|
+
@klass.mti_type_column
|
24
46
|
end
|
25
47
|
|
26
48
|
end
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
module MTI
|
5
5
|
class Railtie < Rails::Railtie
|
6
6
|
initializer 'active_record-mti.load' do |_app|
|
7
|
-
ActiveRecord::MTI.logger.
|
7
|
+
ActiveRecord::MTI.logger.debug "active_record-mti.load"
|
8
8
|
ActiveSupport.on_load(:active_record) do
|
9
9
|
ActiveRecord::MTI.load
|
10
10
|
end
|
data/lib/active_record/mti.rb
CHANGED
@@ -32,11 +32,16 @@ module ActiveRecord
|
|
32
32
|
def self.load
|
33
33
|
::ActiveRecord::Base.send :include, Inheritance
|
34
34
|
::ActiveRecord::Base.send :include, ModelSchema
|
35
|
-
::ActiveRecord::Relation.send :
|
36
|
-
::ActiveRecord::Relation.send :
|
35
|
+
::ActiveRecord::Relation.send :prepend, QueryMethods
|
36
|
+
::ActiveRecord::Relation.send :prepend, Calculations
|
37
37
|
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :prepend, ConnectionAdapters::PostgreSQL::Adapter
|
38
38
|
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :include, ConnectionAdapters::PostgreSQL::SchemaStatements
|
39
39
|
::ActiveRecord::SchemaDumper.send :include, SchemaDumper
|
40
40
|
end
|
41
|
+
|
42
|
+
def self.testify(value)
|
43
|
+
value == true || value == 't' || value == 1 || value == '1'
|
44
|
+
end
|
45
|
+
|
41
46
|
end
|
42
47
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecord::MTI::Calculations do
|
4
|
+
|
5
|
+
context "don't project tableoid on" do
|
6
|
+
it "grouping" do
|
7
|
+
|
8
|
+
Admin.create(email: 'foo@bar.baz', god_powers: 3)
|
9
|
+
Admin.create(email: 'foo@bar.baz', god_powers: 3)
|
10
|
+
Admin.create(email: 'foo24@bar.baz', god_powers: 3)
|
11
|
+
|
12
|
+
grouped_count = Admin.group(:email).count
|
13
|
+
|
14
|
+
expect(grouped_count['foo24@bar.baz']).to eq(1)
|
15
|
+
expect(grouped_count['foo@bar.baz']).to eq(2)
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
it "count calculations" do
|
20
|
+
|
21
|
+
Admin.create(email: 'foo@bar.baz', god_powers: 3)
|
22
|
+
Admin.create(email: 'foo@bar.baz', god_powers: 3)
|
23
|
+
Admin.create(email: 'foo24@bar.baz', god_powers: 3)
|
24
|
+
|
25
|
+
expect(Admin.count(:email)).to eq(3)
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "projects tableoid" do
|
31
|
+
it "and groups tableoid when selecting :tableoid" do
|
32
|
+
sql = Admin.select(:email, :tableoid).group(:email).to_sql
|
33
|
+
|
34
|
+
expect(sql).to match(/SELECT .*, \"admins\".\"tableoid\" AS tableoid FROM \"admins\"/)
|
35
|
+
|
36
|
+
expect(sql).to match(/GROUP BY .*, \"admins\".\"tableoid\"/)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "when grouping :tableoid" do
|
40
|
+
sql = Admin.select(:email).group(:email, :tableoid).to_sql
|
41
|
+
|
42
|
+
expect(sql).to match(/SELECT .*, \"admins\".\"tableoid\" AS tableoid FROM \"admins\"/)
|
43
|
+
|
44
|
+
expect(sql).to match(/GROUP BY .*, \"admins\".\"tableoid\"/)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecord::MTI::Inheritance do
|
4
|
+
|
5
|
+
xit 'returns non-nil value when checking uses_mti?' do
|
6
|
+
# Mod = Class.new(User)
|
7
|
+
# expect(Mod.uses_mti?).to be(true)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'class definition' do
|
11
|
+
|
12
|
+
describe 'for classes that use MTI' do
|
13
|
+
it "doesn't check inheritance multiple times" do
|
14
|
+
Admin.instance_variable_set(:@mti_setup, false)
|
15
|
+
expect(Admin).to receive(:check_inheritance_of).and_call_original.exactly(1).times
|
16
|
+
|
17
|
+
Admin.create(email: 'foo@bar.baz', god_powers: 3)
|
18
|
+
Admin.create(email: 'foo2@bar.baz', god_powers: 3)
|
19
|
+
Admin.create(email: 'foo24@bar.baz', god_powers: 3)
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "for classes that don't use MTI" do
|
25
|
+
it "doesn't check inheritance multiple times" do
|
26
|
+
Post.instance_variable_set(:@uses_mti, nil)
|
27
|
+
expect(Post).to receive(:check_inheritance_of).and_call_original.exactly(1).times
|
28
|
+
|
29
|
+
Post.create(title: 'foo@bar.baz')
|
30
|
+
Post.create(title: 'foo2@bar.baz')
|
31
|
+
Post.create(title: 'foo24@bar.baz')
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'default inheritance_column model' do
|
39
|
+
let!(:user) { User.create(email: 'foo@bar.baz') }
|
40
|
+
let!(:admin) { Admin.create(email: 'foo@bar.baz', god_powers: 3) }
|
41
|
+
|
42
|
+
it 'casts properly' do
|
43
|
+
user = User.first
|
44
|
+
expect(user.class).to eq(User)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'base class querying' do
|
48
|
+
it 'casts children properly' do
|
49
|
+
users = User.all
|
50
|
+
expect(users.select{ |u| u.is_a?(Admin) }.count).to eql(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
xit 'deserializes children with child specific data' do
|
54
|
+
my_admin = User.find(admin.id)
|
55
|
+
expect(my_admin.god_powers).to eql(3)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'has the correct count for' do
|
60
|
+
it 'parents' do
|
61
|
+
users = User.all
|
62
|
+
expect(users.count).to eq(2)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'children' do
|
66
|
+
admins = Admin.all
|
67
|
+
expect(admins.count).to eq(1)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'dynamic class creation' do
|
72
|
+
it 'infers the table_name from superclass not base_class' do
|
73
|
+
god = Class.new(Admin)
|
74
|
+
expect(god.table_name).to eql(Admin.table_name)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'views' do
|
80
|
+
before(:each) do
|
81
|
+
class UserView < User
|
82
|
+
self.table_name = "users_all"
|
83
|
+
end
|
84
|
+
|
85
|
+
UserView.connection.execute <<-SQL
|
86
|
+
CREATE OR REPLACE VIEW "users_all"
|
87
|
+
AS #{ User.all.to_sql }
|
88
|
+
SQL
|
89
|
+
end
|
90
|
+
|
91
|
+
if ActiveRecord::Base.connection.version >= Gem::Version.new('9.4')
|
92
|
+
it 'allows creation pass-through' do
|
93
|
+
|
94
|
+
UserView.create(email: 'dale@twilightcoders.net')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'dynamic class creation' do
|
100
|
+
it 'infers the table_name from superclass not base_class' do
|
101
|
+
God = Class.new(Admin)
|
102
|
+
|
103
|
+
Hacker = Class.new(Admin) do
|
104
|
+
uses_mti
|
105
|
+
end
|
106
|
+
|
107
|
+
expect(God.table_name).to eql(Admin.table_name)
|
108
|
+
expect(Hacker.table_name).to eql('admin/hackers')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'custom inheritance_column model' do
|
113
|
+
let!(:vehicle) { Transportation::Vehicle.create(color: :red) }
|
114
|
+
let!(:truck) { Transportation::Truck.create(color: :blue, bed_size: 10) }
|
115
|
+
|
116
|
+
describe 'inheritance_column' do
|
117
|
+
xit 'should set the custom column correctly' do
|
118
|
+
expect(vehicle.type).to eql('vehicles')
|
119
|
+
expect(truck.type).to eql('trucks')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'base class querying' do
|
124
|
+
it 'casts children properly' do
|
125
|
+
expect(Transportation::Vehicle.all.select{ |v| v.is_a?(Transportation::Truck) }.count).to eql(1)
|
126
|
+
end
|
127
|
+
|
128
|
+
xit 'deserializes children with child specific data' do
|
129
|
+
my_truck = Transportation::Vehicle.find(truck.id)
|
130
|
+
expect(my_truck.bed_size).to eql(10)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'has the correct count for' do
|
135
|
+
it 'parents' do
|
136
|
+
expect(Transportation::Vehicle.count).to eql(2)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'children' do
|
140
|
+
expect(Transportation::Truck.count).to eql(1)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
self.verbose = false
|
3
|
+
|
4
|
+
create_table :users, force: true do |t|
|
5
|
+
t.string :email
|
6
|
+
t.timestamps null: false
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :admins, force: true, inherits: :users do |t|
|
10
|
+
t.integer :god_powers
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table 'admin/hackers', force: true, inherits: :admins do |t|
|
14
|
+
t.integer :god_powers
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :posts, force: true do |t|
|
18
|
+
t.integer :user_id
|
19
|
+
t.string :title
|
20
|
+
t.timestamps null: false
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :comments, force: true do |t|
|
24
|
+
t.integer :user_id
|
25
|
+
t.integer :post_id
|
26
|
+
t.timestamps null: false
|
27
|
+
end
|
28
|
+
|
29
|
+
#################################
|
30
|
+
### Custom Inheritance Column ###
|
31
|
+
#################################
|
32
|
+
|
33
|
+
create_table :vehicles, force: true do |t|
|
34
|
+
t.string :color
|
35
|
+
t.string :type # Inheritance column
|
36
|
+
t.timestamps null: false
|
37
|
+
end
|
38
|
+
|
39
|
+
create_table "vehicles/trucks", force: true, inherits: :vehicles do |t|
|
40
|
+
t.integer :bed_size
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
ENV['RAILS_ENV'] = 'test'
|
2
|
+
|
3
|
+
require 'database_cleaner'
|
4
|
+
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start do
|
7
|
+
# add_group 'Lib', 'lib'
|
8
|
+
add_filter 'spec'
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'active_record/mti'
|
12
|
+
|
13
|
+
ActiveRecord::MTI.load
|
14
|
+
|
15
|
+
db_config = {
|
16
|
+
adapter: 'postgresql', database: 'active_record-mti-test'
|
17
|
+
}
|
18
|
+
|
19
|
+
db_config_admin = db_config.merge({ database: 'postgres', schema_search_path: 'public' })
|
20
|
+
|
21
|
+
ActiveRecord::Base.establish_connection db_config_admin
|
22
|
+
ActiveRecord::Base.connection.drop_database(db_config[:database])
|
23
|
+
ActiveRecord::Base.connection.create_database(db_config[:database])
|
24
|
+
ActiveRecord::Base.establish_connection db_config
|
25
|
+
|
26
|
+
load File.dirname(__FILE__) + '/schema.rb'
|
27
|
+
|
28
|
+
Dir[File.join(File.dirname(__FILE__), '..', 'spec', 'support', '**', '**.rb')].each do |f|
|
29
|
+
require f
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec.configure do |config|
|
33
|
+
config.order = 'random'
|
34
|
+
|
35
|
+
# Configure the DatabaseCleaner
|
36
|
+
config.before(:suite) do
|
37
|
+
DatabaseCleaner.strategy = :transaction
|
38
|
+
DatabaseCleaner.clean_with(:truncation)
|
39
|
+
end
|
40
|
+
|
41
|
+
config.around(:each) do |example|
|
42
|
+
DatabaseCleaner.cleaning do
|
43
|
+
example.run
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
class Comment < ::ActiveRecord::Base
|
4
|
+
belongs_to :user
|
5
|
+
belongs_to :post
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
class Post < ::ActiveRecord::Base
|
10
|
+
belongs_to :user
|
11
|
+
has_many :comments
|
12
|
+
end
|
13
|
+
|
14
|
+
class User < ::ActiveRecord::Base
|
15
|
+
uses_mti
|
16
|
+
|
17
|
+
has_many :posts
|
18
|
+
has_many :comments
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Admin < User
|
23
|
+
self.table_name = 'admins'
|
24
|
+
end
|
25
|
+
|
26
|
+
module Transportation
|
27
|
+
class Vehicle < ::ActiveRecord::Base
|
28
|
+
uses_mti
|
29
|
+
end
|
30
|
+
|
31
|
+
class Truck < Vehicle
|
32
|
+
self.table_name = 'vehicles/trucks'
|
33
|
+
end
|
34
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record-mti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0.pre.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dale Stevens
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|
@@ -30,34 +30,34 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '4
|
33
|
+
version: '4'
|
34
34
|
- - "<"
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: '
|
36
|
+
version: '6'
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: '4
|
43
|
+
version: '4'
|
44
44
|
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
46
|
+
version: '6'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: pry-byebug
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - "
|
51
|
+
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
53
|
+
version: '3'
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - "
|
58
|
+
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: '
|
60
|
+
version: '3'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: bundler
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,63 +86,7 @@ dependencies:
|
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '10.0'
|
89
|
-
|
90
|
-
name: rspec
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
|
-
requirements:
|
93
|
-
- - "~>"
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '3.0'
|
96
|
-
type: :development
|
97
|
-
prerelease: false
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '3.0'
|
103
|
-
- !ruby/object:Gem::Dependency
|
104
|
-
name: simplecov
|
105
|
-
requirement: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - "~>"
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '0.1'
|
110
|
-
type: :development
|
111
|
-
prerelease: false
|
112
|
-
version_requirements: !ruby/object:Gem::Requirement
|
113
|
-
requirements:
|
114
|
-
- - "~>"
|
115
|
-
- !ruby/object:Gem::Version
|
116
|
-
version: '0.1'
|
117
|
-
- !ruby/object:Gem::Dependency
|
118
|
-
name: codeclimate-test-reporter
|
119
|
-
requirement: !ruby/object:Gem::Requirement
|
120
|
-
requirements:
|
121
|
-
- - "~>"
|
122
|
-
- !ruby/object:Gem::Version
|
123
|
-
version: '1.0'
|
124
|
-
type: :development
|
125
|
-
prerelease: false
|
126
|
-
version_requirements: !ruby/object:Gem::Requirement
|
127
|
-
requirements:
|
128
|
-
- - "~>"
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
version: '1.0'
|
131
|
-
- !ruby/object:Gem::Dependency
|
132
|
-
name: database_cleaner
|
133
|
-
requirement: !ruby/object:Gem::Requirement
|
134
|
-
requirements:
|
135
|
-
- - "~>"
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
version: '1.6'
|
138
|
-
type: :development
|
139
|
-
prerelease: false
|
140
|
-
version_requirements: !ruby/object:Gem::Requirement
|
141
|
-
requirements:
|
142
|
-
- - "~>"
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: '1.6'
|
145
|
-
description: Allows use of native inherited tables in PostgreSQL
|
89
|
+
description: Gives ActiveRecord support for PostgreSQL's native inherited tables
|
146
90
|
email:
|
147
91
|
- dale@twilightcoders.net
|
148
92
|
executables: []
|
@@ -152,11 +96,17 @@ files:
|
|
152
96
|
- ".gitignore"
|
153
97
|
- ".rspec"
|
154
98
|
- ".travis.yml"
|
99
|
+
- CHANGELOG.md
|
155
100
|
- Gemfile
|
156
101
|
- LICENSE.txt
|
157
102
|
- README.md
|
158
103
|
- Rakefile
|
159
104
|
- active_record-mti.gemspec
|
105
|
+
- gemfiles/activerecord-4.0.Gemfile
|
106
|
+
- gemfiles/activerecord-4.1.Gemfile
|
107
|
+
- gemfiles/activerecord-4.2.Gemfile
|
108
|
+
- gemfiles/activerecord-5.0.Gemfile
|
109
|
+
- gemfiles/activerecord-5.1.Gemfile
|
160
110
|
- lib/active_record/mti.rb
|
161
111
|
- lib/active_record/mti/calculations.rb
|
162
112
|
- lib/active_record/mti/connection_adapters/postgresql/adapter.rb
|
@@ -168,7 +118,13 @@ files:
|
|
168
118
|
- lib/active_record/mti/railtie.rb
|
169
119
|
- lib/active_record/mti/schema_dumper.rb
|
170
120
|
- lib/active_record/mti/version.rb
|
171
|
-
|
121
|
+
- spec/active_record/calculations_spec.rb
|
122
|
+
- spec/active_record/mti/inheritence_spec.rb
|
123
|
+
- spec/active_record/mti_spec.rb
|
124
|
+
- spec/schema.rb
|
125
|
+
- spec/spec_helper.rb
|
126
|
+
- spec/support/models.rb
|
127
|
+
homepage: https://github.com/twilightcoders/active_record-mti
|
172
128
|
licenses:
|
173
129
|
- MIT
|
174
130
|
metadata:
|
@@ -177,20 +133,27 @@ post_install_message:
|
|
177
133
|
rdoc_options: []
|
178
134
|
require_paths:
|
179
135
|
- lib
|
136
|
+
- spec
|
180
137
|
required_ruby_version: !ruby/object:Gem::Requirement
|
181
138
|
requirements:
|
182
139
|
- - ">="
|
183
140
|
- !ruby/object:Gem::Version
|
184
|
-
version: 2.0
|
141
|
+
version: '2.0'
|
185
142
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
143
|
requirements:
|
187
|
-
- - "
|
144
|
+
- - ">"
|
188
145
|
- !ruby/object:Gem::Version
|
189
|
-
version:
|
146
|
+
version: 1.3.1
|
190
147
|
requirements: []
|
191
148
|
rubyforge_project:
|
192
149
|
rubygems_version: 2.6.11
|
193
150
|
signing_key:
|
194
151
|
specification_version: 4
|
195
152
|
summary: Multi Table Inheritance for PostgreSQL in Rails
|
196
|
-
test_files:
|
153
|
+
test_files:
|
154
|
+
- spec/active_record/calculations_spec.rb
|
155
|
+
- spec/active_record/mti/inheritence_spec.rb
|
156
|
+
- spec/active_record/mti_spec.rb
|
157
|
+
- spec/schema.rb
|
158
|
+
- spec/spec_helper.rb
|
159
|
+
- spec/support/models.rb
|