active_record-mti 0.2.1 → 0.3.0.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|