active_record-mti 0.3.2 → 0.4.0.pre.1
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/CHANGELOG.md +8 -6
- data/{LICENSE.txt → LICENSE} +0 -0
- data/README.md +47 -10
- data/lib/active_record/mti.rb +56 -56
- data/lib/active_record/mti/config.rb +26 -0
- data/lib/active_record/mti/connection_adapters/postgresql/adapter.rb +0 -8
- data/lib/active_record/mti/connection_adapters/postgresql/schema_statements.rb +30 -11
- data/lib/active_record/mti/core_extension.rb +170 -0
- data/lib/active_record/mti/railtie.rb +9 -4
- data/lib/active_record/mti/relation.rb +95 -0
- data/lib/active_record/mti/schema_dumper.rb +4 -4
- data/lib/active_record/mti/table.rb +19 -0
- data/lib/active_record/mti/table_oid.rb +6 -0
- data/lib/active_record/mti/version.rb +1 -1
- data/lib/core_ext/array.rb +14 -0
- data/lib/core_ext/hash.rb +1 -3
- data/lib/core_ext/thread.rb +14 -0
- metadata +94 -95
- data/.gitignore +0 -78
- data/.rspec +0 -2
- data/.travis.yml +0 -38
- data/Gemfile +0 -21
- data/Rakefile +0 -6
- data/active_record-mti.gemspec +0 -42
- data/gemfiles/activerecord-4.0.Gemfile +0 -3
- data/gemfiles/activerecord-4.1.Gemfile +0 -3
- data/gemfiles/activerecord-4.2.Gemfile +0 -3
- data/gemfiles/activerecord-5.0.Gemfile +0 -3
- data/gemfiles/activerecord-5.1.Gemfile +0 -3
- data/lib/active_record/mti/calculations.rb +0 -23
- data/lib/active_record/mti/inheritance.rb +0 -127
- data/lib/active_record/mti/model_schema.rb +0 -55
- data/lib/active_record/mti/query_methods.rb +0 -40
- data/lib/active_record/mti/querying.rb +0 -7
- data/lib/active_record/mti/registry.rb +0 -23
- data/spec/active_record/mti/calculations_spec.rb +0 -56
- data/spec/active_record/mti/inheritance_spec.rb +0 -184
- data/spec/active_record/mti/model_schema_spec.rb +0 -11
- data/spec/active_record/mti/query_methods_spec.rb +0 -12
- data/spec/active_record/mti/schema_dumper_spec.rb +0 -22
- data/spec/active_record/mti_spec.rb +0 -24
- data/spec/active_record/sti/inheritance_spec.rb +0 -24
- data/spec/spec_helper.rb +0 -28
- data/spec/support/rails/app/models/admin.rb +0 -3
- data/spec/support/rails/app/models/comment.rb +0 -4
- data/spec/support/rails/app/models/post.rb +0 -4
- data/spec/support/rails/app/models/transportation/military/vehicle.rb +0 -7
- data/spec/support/rails/app/models/transportation/truck.rb +0 -5
- data/spec/support/rails/app/models/transportation/vehicle.rb +0 -4
- data/spec/support/rails/app/models/user.rb +0 -6
- data/spec/support/rails/config/database.yml +0 -4
- data/spec/support/rails/config/routes.rb +0 -3
- data/spec/support/rails/db/schema.rb +0 -51
- data/spec/support/rails/log/.gitignore +0 -1
- data/spec/support/rails/public/favicon.ico +0 -0
data/.gitignore
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
2
|
-
#
|
3
|
-
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
-
# or operating system, you probably want to add a global ignore instead:
|
5
|
-
# git config --global core.excludesfile ~/.gitignore_global
|
6
|
-
|
7
|
-
# Database config and secrets
|
8
|
-
/config/database.yml
|
9
|
-
/config/secrets.yml
|
10
|
-
|
11
|
-
# Ignore bundler config
|
12
|
-
/.bundle
|
13
|
-
|
14
|
-
# Ignore client credentials
|
15
|
-
/config/client_api_credentials.yml
|
16
|
-
|
17
|
-
# Ignore the default SQLite database.
|
18
|
-
/db/*.sqlite3
|
19
|
-
|
20
|
-
# Ignore all logfiles and tempfiles.
|
21
|
-
/log/*.log
|
22
|
-
/tmp
|
23
|
-
/db/structure.sql
|
24
|
-
/doc/app/*
|
25
|
-
/vendor/cldr/*
|
26
|
-
/public/uploads/*
|
27
|
-
/public/photo/*
|
28
|
-
/public/test/*
|
29
|
-
/test/assets/*
|
30
|
-
/spec/assets/*
|
31
|
-
/public/assets/**
|
32
|
-
.powenv
|
33
|
-
.rvmrc
|
34
|
-
.env
|
35
|
-
.ruby-version
|
36
|
-
|
37
|
-
# Compiled source #
|
38
|
-
###################
|
39
|
-
/pkg/
|
40
|
-
*.com
|
41
|
-
*.class
|
42
|
-
*.dll
|
43
|
-
*.exe
|
44
|
-
*.o
|
45
|
-
*.so
|
46
|
-
|
47
|
-
# Packages #
|
48
|
-
############
|
49
|
-
# it's better to unpack these files and commit the raw source
|
50
|
-
# git has its own built in compression methods
|
51
|
-
*.7z
|
52
|
-
*.dmg
|
53
|
-
*.gz
|
54
|
-
*.iso
|
55
|
-
*.jar
|
56
|
-
*.rar
|
57
|
-
*.tar
|
58
|
-
*.zip
|
59
|
-
|
60
|
-
# Logs and databases #
|
61
|
-
######################
|
62
|
-
*.log
|
63
|
-
*.sql
|
64
|
-
*.sqlite
|
65
|
-
|
66
|
-
# OS generated files #
|
67
|
-
######################
|
68
|
-
.DS_Store
|
69
|
-
.DS_Store?
|
70
|
-
._*
|
71
|
-
.Spotlight-V100
|
72
|
-
.Trashes
|
73
|
-
Icon?
|
74
|
-
ehthumbs.db
|
75
|
-
Thumbs.db
|
76
|
-
/Gemfile.lock
|
77
|
-
/coverage
|
78
|
-
/.editorconfig
|
data/.rspec
DELETED
data/.travis.yml
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
|
4
|
-
cache: bundler
|
5
|
-
script:
|
6
|
-
- bundle exec rspec
|
7
|
-
|
8
|
-
after_success:
|
9
|
-
- bundle exec codeclimate-test-reporter
|
10
|
-
|
11
|
-
addons:
|
12
|
-
postgresql: "9.3"
|
13
|
-
|
14
|
-
rvm:
|
15
|
-
- 2.4
|
16
|
-
- 2.3
|
17
|
-
- 2.2
|
18
|
-
- 2.1
|
19
|
-
|
20
|
-
gemfile:
|
21
|
-
- gemfiles/activerecord-5.1.Gemfile
|
22
|
-
- gemfiles/activerecord-5.0.Gemfile
|
23
|
-
- gemfiles/activerecord-4.2.Gemfile
|
24
|
-
- gemfiles/activerecord-4.1.Gemfile
|
25
|
-
- gemfiles/activerecord-4.0.Gemfile
|
26
|
-
|
27
|
-
matrix:
|
28
|
-
exclude:
|
29
|
-
|
30
|
-
- rvm: 2.1
|
31
|
-
gemfile: gemfiles/activerecord-5.0.Gemfile
|
32
|
-
- rvm: 2.1
|
33
|
-
gemfile: gemfiles/activerecord-5.1.Gemfile
|
34
|
-
|
35
|
-
- rvm: 2.4
|
36
|
-
gemfile: gemfiles/activerecord-4.0.Gemfile
|
37
|
-
- rvm: 2.4
|
38
|
-
gemfile: gemfiles/activerecord-4.1.Gemfile
|
data/Gemfile
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
gemspec
|
4
|
-
|
5
|
-
group :test do
|
6
|
-
|
7
|
-
gem 'pry'
|
8
|
-
|
9
|
-
# Generates coverage stats of specs
|
10
|
-
gem 'simplecov'
|
11
|
-
|
12
|
-
# Publishes coverage to codeclimate
|
13
|
-
gem 'codeclimate-test-reporter'
|
14
|
-
|
15
|
-
gem 'rspec'
|
16
|
-
|
17
|
-
gem 'database_cleaner'
|
18
|
-
|
19
|
-
gem 'combustion'
|
20
|
-
|
21
|
-
end
|
data/Rakefile
DELETED
data/active_record-mti.gemspec
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'active_record/mti/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "active_record-mti"
|
8
|
-
spec.version = ActiveRecord::MTI::VERSION
|
9
|
-
spec.authors = ["Dale Stevens"]
|
10
|
-
spec.email = ["dale@twilightcoders.net"]
|
11
|
-
|
12
|
-
spec.summary = %q{Multi Table Inheritance for PostgreSQL in Rails}
|
13
|
-
spec.description = %q{Gives ActiveRecord support for PostgreSQL's native inherited tables}
|
14
|
-
spec.homepage = "https://github.com/twilightcoders/active_record-mti"
|
15
|
-
spec.license = "MIT"
|
16
|
-
|
17
|
-
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
-
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
-
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
21
|
-
else
|
22
|
-
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
23
|
-
end
|
24
|
-
|
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']
|
30
|
-
|
31
|
-
rails_versions = ['>= 4', '< 6']
|
32
|
-
spec.required_ruby_version = '>= 2.1'
|
33
|
-
|
34
|
-
spec.add_runtime_dependency 'pg', '~> 0'
|
35
|
-
spec.add_runtime_dependency 'activerecord', rails_versions
|
36
|
-
|
37
|
-
spec.add_development_dependency 'pry-byebug', '~> 3'
|
38
|
-
spec.add_development_dependency 'bundler', '~> 1.3'
|
39
|
-
spec.add_development_dependency 'rake', '~> 12.0'
|
40
|
-
spec.add_development_dependency 'combustion', '~> 0.7'
|
41
|
-
|
42
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module MTI
|
3
|
-
module Calculations
|
4
|
-
|
5
|
-
private
|
6
|
-
|
7
|
-
def perform_calculation(*args)
|
8
|
-
swap_and_restore_tableoid_cast(true) do
|
9
|
-
super
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def swap_and_restore_tableoid_cast(value)
|
14
|
-
orignal_value = Thread.current['skip_tableoid_cast']
|
15
|
-
Thread.current['skip_tableoid_cast'] = value
|
16
|
-
return_value = yield if block_given?
|
17
|
-
ensure
|
18
|
-
Thread.current['skip_tableoid_cast'] = orignal_value
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
# == Multi-Table Inheritance
|
3
|
-
module MTI
|
4
|
-
module Inheritance
|
5
|
-
|
6
|
-
def self.prepended(subclass)
|
7
|
-
subclass.extend(ClassMethods)
|
8
|
-
class << subclass
|
9
|
-
attr_reader :mti_type_column
|
10
|
-
attr_reader :tableoid_column
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def has_tableoid_column?
|
16
|
-
tableoid_column != false
|
17
|
-
end
|
18
|
-
|
19
|
-
def inherited(subclass)
|
20
|
-
super
|
21
|
-
subclass.using_multi_table_inheritance?
|
22
|
-
end
|
23
|
-
|
24
|
-
def uses_mti(*args)
|
25
|
-
warn "DEPRECATED - `uses_mti` is no longer needed (nor has any effect)"
|
26
|
-
end
|
27
|
-
|
28
|
-
def using_multi_table_inheritance?
|
29
|
-
mti = ActiveRecord::MTI::Registry.tableoid?(self)
|
30
|
-
return (mti != false) unless mti == nil
|
31
|
-
|
32
|
-
if (mti = check_inheritance_of(@table_name))
|
33
|
-
if (self != base_class && self.table_name == base_class.table_name)
|
34
|
-
mti = false
|
35
|
-
else
|
36
|
-
mti = detect_tableoid(table_name)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
ActiveRecord::MTI::Registry[self] = mti
|
41
|
-
|
42
|
-
descendants.each do |d|
|
43
|
-
d.using_multi_table_inheritance?
|
44
|
-
end
|
45
|
-
|
46
|
-
return mti && mti != false
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def check_inheritance_of(table_name, table_schema = 'public')
|
52
|
-
ActiveRecord::MTI.logger.debug("Trying to check inheritance of table with no table name (#{self})") and return nil unless table_name
|
53
|
-
ActiveRecord::MTI.logger.debug "Checking inheritance for #{table_schema}.#{table_name}"
|
54
|
-
|
55
|
-
result = connection.execute <<-SQL
|
56
|
-
SELECT EXISTS (
|
57
|
-
SELECT 1
|
58
|
-
FROM pg_catalog.pg_inherits AS i
|
59
|
-
JOIN information_schema.tables AS t ON t.table_schema = '#{table_schema}' AND t.table_name = '#{table_name}'
|
60
|
-
LEFT JOIN pg_catalog.pg_rewrite AS r ON r.ev_class = t.table_name::regclass::oid
|
61
|
-
LEFT JOIN pg_catalog.pg_depend AS d ON d.objid = r.oid
|
62
|
-
LEFT JOIN pg_catalog.pg_class AS c ON c.oid = d.refobjid
|
63
|
-
WHERE i.inhrelid = COALESCE(c.relname, t.table_name)::regclass::oid
|
64
|
-
OR i.inhparent = COALESCE(c.relname, t.table_name)::regclass::oid
|
65
|
-
) AS uses_inheritance;
|
66
|
-
SQL
|
67
|
-
|
68
|
-
return ActiveRecord::MTI.testify(result.try(:first)['uses_inheritance']) == true
|
69
|
-
end
|
70
|
-
|
71
|
-
def detect_tableoid(table_name, table_schema = 'public')
|
72
|
-
|
73
|
-
tableoid_query = connection.execute(<<-SQL
|
74
|
-
SELECT 1 AS has_tableoid_column, t.table_name::regclass::oid as tableoid
|
75
|
-
FROM pg_catalog.pg_attribute
|
76
|
-
JOIN information_schema.tables t ON t.table_schema = '#{table_schema}' AND t.table_name = '#{table_name}'
|
77
|
-
WHERE attrelid = t.table_name::regclass
|
78
|
-
AND attname = 'tableoid'
|
79
|
-
AND NOT attisdropped;
|
80
|
-
SQL
|
81
|
-
).first
|
82
|
-
|
83
|
-
tableoid = tableoid_query.try(:[], 'tableoid') || false
|
84
|
-
@tableoid_column = ActiveRecord::MTI.testify(tableoid_query.try(:[], 'has_tableoid_column'))
|
85
|
-
|
86
|
-
if (has_tableoid_column?)
|
87
|
-
ActiveRecord::MTI.logger.debug "#{table_schema}.#{table_name} has tableoid column! (#{tableoid})"
|
88
|
-
add_tableoid_column
|
89
|
-
@mti_type_column = arel_table[:tableoid]
|
90
|
-
else
|
91
|
-
@mti_type_column = nil
|
92
|
-
end
|
93
|
-
|
94
|
-
tableoid
|
95
|
-
end
|
96
|
-
|
97
|
-
# Called by +instantiate+ to decide which class to use for a new
|
98
|
-
# record instance. For single-table inheritance, we check the record
|
99
|
-
# for a +type+ column and return the corresponding class.
|
100
|
-
def discriminate_class_for_record(record)
|
101
|
-
if using_multi_table_inheritance?
|
102
|
-
ActiveRecord::MTI::Registry.find_mti_class(record['tableoid']) || self
|
103
|
-
else
|
104
|
-
super
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# Type condition only applies if it's STI, otherwise it's
|
109
|
-
# done for free by querying the inherited table in MTI
|
110
|
-
def type_condition(table = arel_table)
|
111
|
-
return nil if using_multi_table_inheritance?
|
112
|
-
super
|
113
|
-
end
|
114
|
-
|
115
|
-
def add_tableoid_column
|
116
|
-
if self.respond_to? :attribute
|
117
|
-
self.attribute :tableoid, ActiveRecord::MTI.oid_class.new
|
118
|
-
else
|
119
|
-
new_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new('tableoid', nil, ActiveRecord::MTI.oid_class.new, "oid", false)
|
120
|
-
columns.unshift new_column
|
121
|
-
columns_hash['tableoid'] = new_column
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module MTI
|
3
|
-
module ModelSchema
|
4
|
-
|
5
|
-
def self.prepended(base)
|
6
|
-
base.extend(ClassMethods)
|
7
|
-
end
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
# Computes and returns a table name according to default conventions.
|
11
|
-
def compute_table_name
|
12
|
-
if self != base_class
|
13
|
-
# Nested classes are prefixed with singular parent table name.
|
14
|
-
if superclass < Base && !superclass.abstract_class?
|
15
|
-
contained = superclass.table_name
|
16
|
-
contained = contained.singularize if superclass.pluralize_table_names
|
17
|
-
contained += '/'
|
18
|
-
end
|
19
|
-
|
20
|
-
potential_table_name = "#{full_table_name_prefix}#{contained}#{decorated_table_name(name)}#{full_table_name_suffix}"
|
21
|
-
|
22
|
-
if check_inheritance_of(potential_table_name)
|
23
|
-
potential_table_name
|
24
|
-
else
|
25
|
-
superclass.table_name
|
26
|
-
end
|
27
|
-
else
|
28
|
-
super
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def full_table_name_suffix #:nodoc:
|
33
|
-
super
|
34
|
-
rescue NoMethodError
|
35
|
-
full_table_name_rescue(:table_name_suffix)
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def full_table_name_rescue(which)
|
41
|
-
(parents.detect{ |p| p.respond_to?(which) } || self).send(which)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
45
|
-
def decorated_table_name(class_name = base_class.name)
|
46
|
-
super
|
47
|
-
rescue NoMethodError
|
48
|
-
table_name = class_name.to_s.underscore
|
49
|
-
pluralize_table_names ? table_name.pluralize : table_name
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module MTI
|
3
|
-
module QueryMethods
|
4
|
-
|
5
|
-
def build_arel
|
6
|
-
@select_by_tableoid = [select_values.delete(:tableoid), tableoid?(klass)].compact.first
|
7
|
-
@group_by_tableoid = group_values.delete(:tableoid)
|
8
|
-
|
9
|
-
super.tap do |arel|
|
10
|
-
if @group_by_tableoid || (@select_by_tableoid && group_values.any?)
|
11
|
-
arel.group(tableoid_group(@klass))
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def build_select(*args)
|
17
|
-
super.tap do |arel|
|
18
|
-
arel.project(tableoid_project(@klass)) if (@group_by_tableoid || @select_by_tableoid)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def tableoid?(klass)
|
25
|
-
!Thread.current['skip_tableoid_cast'] &&
|
26
|
-
klass.using_multi_table_inheritance? &&
|
27
|
-
klass.mti_type_column
|
28
|
-
end
|
29
|
-
|
30
|
-
def tableoid_project(klass)
|
31
|
-
klass.mti_type_column.as('tableoid')
|
32
|
-
end
|
33
|
-
|
34
|
-
def tableoid_group(klass)
|
35
|
-
klass.mti_type_column
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|