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