active_record-mti 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 556fa783caa9bcba984b0ce5773a69f4703317b6
4
- data.tar.gz: 6d6c0f07e90648aae6cdcce02acde5c787c40a93
3
+ metadata.gz: 70e92bb458152badc92fe1d0f6aa3ac255419211
4
+ data.tar.gz: ec759e61c4042d9fa8119f3c5602fa674b226260
5
5
  SHA512:
6
- metadata.gz: 129a447a2b60dadef04349abb786136e6d5be55090aac4f950ca86ebabd5393a4d0ab981faceca3802b73c15b071d0b7ac36a801af403bc978cee18ae70cc050
7
- data.tar.gz: 99abf9b045de3a36aff4cfb450ce54cba4378de16ad8dde7406e5044890bf87ace33caa4cadcd6e382b3a8699a42d87d66a03a34a0e9df954d46b6082a18dd2f
6
+ metadata.gz: 8156561ef762ff46a270d9907f1df24e74824fd4314a96ca365d30d03700cf92bc8e6a96c8f68f598680b98185fbbc2642900b2b32302303e81ceda50b596ba0
7
+ data.tar.gz: 4d453baef7016ff52e81c366bb54bc8a095f35fce78186847ee605a2c2252810b258fb7e6f4e0e8bfc3be93d2623069802421cf4d0757e6f1267bf6a52002761
@@ -0,0 +1,74 @@
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
+ *.com
40
+ *.class
41
+ *.dll
42
+ *.exe
43
+ *.o
44
+ *.so
45
+
46
+ # Packages #
47
+ ############
48
+ # it's better to unpack these files and commit the raw source
49
+ # git has its own built in compression methods
50
+ *.7z
51
+ *.dmg
52
+ *.gz
53
+ *.iso
54
+ *.jar
55
+ *.rar
56
+ *.tar
57
+ *.zip
58
+
59
+ # Logs and databases #
60
+ ######################
61
+ *.log
62
+ *.sql
63
+ *.sqlite
64
+
65
+ # OS generated files #
66
+ ######################
67
+ .DS_Store
68
+ .DS_Store?
69
+ ._*
70
+ .Spotlight-V100
71
+ .Trashes
72
+ Icon?
73
+ ehthumbs.db
74
+ Thumbs.db
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Dale Stevens
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,88 @@
1
+ # ActiveRecord::MTI
2
+
3
+ Allows for true native inheritance of tables in PostgreSQL
4
+
5
+ Currently requires Rails 4.2
6
+
7
+ ## Usage
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'active_record-mti'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install active_record-mti
20
+
21
+ ### Migrations
22
+
23
+ In your migrations define a table to inherit from another table:
24
+
25
+ ```ruby
26
+ class CreateAccounts < ActiveRecord::Migration
27
+ def change
28
+ # Things is the head of or inheritance tree representing all things
29
+ # both tangible and intangible. Can be considered the vertices in
30
+ # the graph.
31
+ create_table :accounts do |t|
32
+ t.jsonb :settings
33
+ t.timestamps
34
+ end
35
+
36
+ create_table :users, inherits: :accounts do |t|
37
+ t.string :firstname
38
+ t.string :lastname
39
+ end
40
+
41
+ create_table :developers, inherits: :users do |t|
42
+ t.string :url
43
+ t.string :api_key
44
+ end
45
+ end
46
+ end
47
+
48
+ ```
49
+
50
+ ### Schema.rb
51
+
52
+ A schema will be created that reflects the inheritance chain so that rake:db:schema:load will work
53
+
54
+ ```ruby
55
+ ctiveRecord::Schema.define(version: 20160910024954) do
56
+
57
+ create_table "accounts", force: :cascade do |t|
58
+ t.jsonb "settings"
59
+ t.datetime "created_at"
60
+ t.datetime "updated_at"
61
+ end
62
+
63
+ create_table "users", inherits: "accounts" do |t|
64
+ t.string "firstname"
65
+ t.string "lastname"
66
+ end
67
+
68
+ create_table "developers", inherits: "users" do |t|
69
+ t.string "url"
70
+ t.string "api_key"
71
+ end
72
+
73
+ end
74
+ ```
75
+
76
+ ### In your application code
77
+
78
+ ActiveRecord queries work as usual with the following differences:
79
+
80
+ * 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".*`
81
+
82
+ ## Contributing
83
+
84
+ 1. Fork it ( https://github.com/[my-github-username]/active_record-mti/fork )
85
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
86
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
87
+ 4. Push to the branch (`git push origin my-new-feature`)
88
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,26 @@
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
+ spec.summary = %q{Multi Table Inheritance for PostgreSQL in Rails}
12
+ spec.description = %q{Allows use of native inherited tables in PostgreSQL}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency 'rake', '~> 0'
23
+ spec.add_runtime_dependency 'rails', '~> 4.1', '> 4.1'
24
+ spec.add_runtime_dependency 'pg', '~> 0'
25
+
26
+ end
@@ -0,0 +1,8 @@
1
+ require "active_record/mti/version"
2
+ require 'active_record/mti/railtie' if defined?(Rails)
3
+
4
+ module ActiveRecord
5
+ module MTI
6
+ # Your code goes here...
7
+ end
8
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module MTI
3
+ module Calculations
4
+
5
+ private
6
+
7
+ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
8
+ # Postgresql doesn't like ORDER BY when there are no GROUP BY
9
+ relation = unscope(:order)
10
+
11
+ column_alias = column_name
12
+
13
+ bind_values = nil
14
+
15
+ if operation == "count" && (relation.limit_value || relation.offset_value)
16
+ # Shortcut when limit is zero.
17
+ return 0 if relation.limit_value == 0
18
+
19
+ query_builder = build_count_subquery(relation, column_name, distinct)
20
+ bind_values = query_builder.bind_values + relation.bind_values
21
+ else
22
+ column = aggregate_column(column_name)
23
+
24
+ select_value = operation_over_aggregate_column(column, operation, distinct)
25
+
26
+ column_alias = select_value.alias
27
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
28
+ relation.select_values = [select_value]
29
+
30
+ # Only use the last projection (probably the COUNT(*)) all others don't matter
31
+ # relation.arel.projections = [relation.arel.projections.last].compact if @klass.using_multi_table_inheritance?
32
+ relation.arel.projections.shift if @klass.using_multi_table_inheritance?
33
+
34
+ query_builder = relation.arel
35
+ bind_values = query_builder.bind_values + relation.bind_values
36
+ end
37
+
38
+ result = @klass.connection.select_all(query_builder, nil, bind_values)
39
+ row = result.first
40
+ value = row && row.values.first
41
+ column = result.column_types.fetch(column_alias) do
42
+ type_for(column_name)
43
+ end
44
+
45
+ type_cast_calculated_value(value, column, operation)
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,96 @@
1
+ module ActiveRecord
2
+ module MTI
3
+ module ConnectionAdapters
4
+ module PostgreSQL
5
+ module SchemaStatements
6
+ # Creates a new table with the name +table_name+. +table_name+ may either
7
+ # be a String or a Symbol.
8
+ #
9
+ # Add :inherits options for Postgres table inheritance. If a table is inherited then
10
+ # the primary key column is also inherited. Therefore the :primary_key options is set to false
11
+ # so we don't duplicate that colume.
12
+ #
13
+ # However the primary key column from the parent is not inherited as primary key so
14
+ # we manually add it. Lastly we also create indexes on the child table to match those
15
+ # on the parent table since indexes are also not inherited.
16
+ def create_table(table_name, options = {})
17
+ if options[:inherits]
18
+ options[:id] = false
19
+ options.delete(:primary_key)
20
+ end
21
+
22
+ if schema = options.delete(:schema)
23
+ # If we specify a schema then we only create it if it doesn't exist
24
+ # and we only force create it if only the specific schema is in the search path
25
+ table_name = "#{schema}.#{table_name}"
26
+ end
27
+
28
+ if parent_table = options.delete(:inherits)
29
+ options[:options] = ["INHERITS (#{parent_table})", options[:options]].compact.join
30
+ end
31
+
32
+ td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
33
+
34
+ if options[:id] != false && !options[:as]
35
+ pk = options.fetch(:primary_key) do
36
+ Base.get_primary_key table_name.to_s.singularize
37
+ end
38
+
39
+ if pk.is_a?(Array)
40
+ td.primary_keys pk
41
+ else
42
+ td.primary_key pk, options.fetch(:id, :primary_key), options
43
+ end
44
+ end
45
+
46
+ yield td if block_given?
47
+
48
+ if options[:force] && data_source_exists?(table_name)
49
+ drop_table(table_name, options)
50
+ end
51
+
52
+ # Rails 5 wont create an empty column list which we might have if we're
53
+ # working with inherited tables. So we need to do that manually
54
+ sql = schema_creation.accept(td)
55
+ # sql = sql.sub("INHERITS", "() INHERITS") if td.columns.empty?
56
+
57
+ result = execute sql
58
+
59
+ if parent_table
60
+ parent_table_primary_key = primary_key(parent_table)
61
+ execute "ALTER TABLE #{table_name} ADD PRIMARY KEY (#{parent_table_primary_key})"
62
+ indexes(parent_table).each do |index|
63
+ add_index table_name, index.columns, :unique => index.unique
64
+ end
65
+ # triggers_for_table(parent_table).each do |trigger|
66
+ # name = trigger.first
67
+ # definition = trigger.second.merge(on: table_name)
68
+ # create_trigger name, definition
69
+ # end
70
+ end
71
+
72
+ td.indexes.each_pair { |c,o| add_index table_name, c, o }
73
+ end
74
+
75
+ # Parent of inherited table
76
+ def parent_tables(table_name)
77
+ sql = <<-SQL
78
+ SELECT pg_namespace.nspname, pg_class.relname
79
+ FROM pg_catalog.pg_inherits
80
+ INNER JOIN pg_catalog.pg_class ON (pg_inherits.inhparent = pg_class.oid)
81
+ INNER JOIN pg_catalog.pg_namespace ON (pg_class.relnamespace = pg_namespace.oid)
82
+ WHERE inhrelid = '#{table_name}'::regclass
83
+ SQL
84
+ result = exec_query(sql, "SCHEMA")
85
+ result.map{|a| a['relname']}
86
+ end
87
+
88
+ def parent_table(table_name)
89
+ parents = parent_tables(table_name)
90
+ parents.first
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,79 @@
1
+ module ActiveRecord
2
+ # == Multi table inheritance
3
+ #
4
+ # PostgreSQL allows for table inheritance. To enable this in ActiveRecord, ensure that the
5
+ # inheritance_column is named "tableoid" (can be changed by setting <tt>Base.inheritance_column</tt>).
6
+ # This means that an inheritance looking like this:
7
+ #
8
+ # class Company < ActiveRecord::Base;
9
+ # self.inheritance_column = 'tableoid'
10
+ # end
11
+ # class Firm < Company; end
12
+ # class Client < Company; end
13
+ # class PriorityClient < Client; end
14
+ #
15
+ # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
16
+ # the firms table which inherits from companies. You can then fetch this row again using
17
+ # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
18
+ #
19
+ # Note, all the attributes for all the cases are kept in the same table. Read more:
20
+ # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
21
+ #
22
+ module MTI
23
+ module Inheritance
24
+ extend ActiveSupport::Concern
25
+
26
+ module ClassMethods
27
+
28
+ # We know we're using multi-table inheritance if the inheritance_column is not actually
29
+ # present in the DB structure. Thereby implying the inheritance_column is inferred.
30
+ # To further isolate usage of multi-table inheritance, the inheritance column must be set
31
+ # to 'tableoid'
32
+ def using_multi_table_inheritance?(klass = self)
33
+ @using_multi_table_inheritance ||= if klass.columns_hash.include?(klass.inheritance_column)
34
+ false
35
+ elsif klass.inheritance_column == 'tableoid' && (klass.descendants.select{ |d| d.table_name != klass.table_name }.any?)
36
+ true
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # Called by +instantiate+ to decide which class to use for a new
45
+ # record instance. For single-table inheritance, we check the record
46
+ # for a +type+ column and return the corresponding class.
47
+ def discriminate_class_for_record(record)
48
+ if using_single_table_inheritance?(record)
49
+ find_sti_class(record[inheritance_column])
50
+ elsif using_multi_table_inheritance?(base_class)
51
+ find_mti_class(record)
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ # Search descendants for one who's table_name is equal to the returned tableoid.
58
+ # This indicates the class of the record
59
+ def find_mti_class(record)
60
+ descendants.find(Proc.new{ self }) { |d| d.table_name == record['tableoid'] }
61
+ end
62
+
63
+ # Type condition only applies if it's STI, otherwise it's
64
+ # done for free by querying the inherited table in MTI
65
+ def type_condition(table = arel_table)
66
+ if using_multi_table_inheritance?
67
+ nil
68
+ else
69
+ sti_column = table[inheritance_column]
70
+ sti_names = ([self] + descendants).map { |model| model.sti_name }
71
+
72
+ sti_column.in(sti_names)
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ module MTI
3
+ module QueryMethods
4
+
5
+ private
6
+
7
+ # Retrieve the OID as well on a default select
8
+ def build_select(arel)
9
+ arel.project("cast(\"#{klass.table_name}\".\"tableoid\"::regclass as text)") if @klass.using_multi_table_inheritance?
10
+ # arel.project("\"#{klass.table_name}\".\"tableoid\"::regclass as \"#{klass.inheritance_column}\"") if @klass.using_multi_table_inheritance?
11
+ if select_values.any?
12
+ arel.project(*arel_columns(select_values.uniq))
13
+ else
14
+ arel.project(@klass.arel_table[Arel.star])
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_record/mti/schema_dumper'
2
+ require 'active_record/mti/inheritance'
3
+ require 'active_record/mti/query_methods'
4
+ require 'active_record/mti/calculations'
5
+ require 'active_record/mti/connection_adapters/postgresql/schema_statements'
6
+
7
+ module ActiveRecord
8
+ module MTI
9
+ class Railtie < Rails::Railtie
10
+ initializer 'active_record-mti.inheritance.initialization' do |_app|
11
+ ::ActiveRecord::Base.send :include, Inheritance
12
+ ::ActiveRecord::Relation.send :include, QueryMethods
13
+ ::ActiveRecord::Relation.send :include, ActiveRecord::MTI::Calculations
14
+
15
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :include, ConnectionAdapters::PostgreSQL::SchemaStatements
16
+ ::ActiveRecord::SchemaDumper.send :include, ActiveRecord::MTI::SchemaDumper
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,177 @@
1
+ # Modified SchemaDumper that knows how to dump
2
+ # inherited tables. Key is that we have to dump parent
3
+ # tables before we dump child tables (of course).
4
+ # In addition we have to make sure we don't dump columns
5
+ # that are inherited.
6
+ module ActiveRecord
7
+ # = Active Record Schema Dumper
8
+ #
9
+ # This class is used to dump the database schema for some connection to some
10
+ # output format (i.e., ActiveRecord::Schema).
11
+ module MTI
12
+ module SchemaDumper #:nodoc:
13
+ extend ActiveSupport::Concern
14
+
15
+
16
+ included do
17
+
18
+ private
19
+
20
+ def dumped_tables
21
+ @dumped_tables ||= []
22
+ end
23
+
24
+ # Output table and columns - but don't output columns that are inherited from
25
+ # a parent table.
26
+ #
27
+ # TODO: Qualify with the schema name IF the table is in a schema other than the first
28
+ # schema in the search path (not including the $user schema)
29
+ def table(table, stream)
30
+ return if already_dumped?(table)
31
+ if parent_table = @connection.parent_table(table)
32
+ table(parent_table, stream)
33
+ parent_column_names = @connection.columns(parent_table).map(&:name)
34
+ end
35
+
36
+ columns = @connection.columns(table)
37
+ begin
38
+ tbl = StringIO.new
39
+
40
+ # first dump primary key column
41
+ pk = @connection.primary_key(table)
42
+
43
+ tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
44
+ if parent_table
45
+ tbl.print %Q(, inherits: "#{parent_table}")
46
+ else
47
+ pkcol = columns.detect { |c| c.name == pk }
48
+ if pkcol
49
+ if pk != 'id'
50
+ tbl.print %Q(, primary_key: "#{pk}")
51
+ elsif pkcol.sql_type == 'bigint'
52
+ tbl.print ", id: :bigserial"
53
+ elsif pkcol.sql_type == 'uuid'
54
+ tbl.print ", id: :uuid"
55
+ tbl.print %Q(, default: #{pkcol.default_function.inspect})
56
+ end
57
+ else
58
+ tbl.print ", id: false"
59
+ end
60
+ tbl.print ", force: :cascade"
61
+ end
62
+ tbl.puts " do |t|"
63
+
64
+ # then dump all non-primary key columns
65
+ column_specs = columns.map do |column|
66
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
67
+ next if column.name == pk
68
+
69
+ # Except columns in parent table
70
+ next if parent_column_names && parent_column_names.include?(column.name)
71
+
72
+ @connection.column_spec(column, @types)
73
+ end.compact
74
+
75
+ # find all migration keys used in this table
76
+ keys = @connection.migration_keys
77
+
78
+ # figure out the lengths for each column based on above keys
79
+ lengths = keys.map { |key|
80
+ column_specs.map { |spec|
81
+ spec[key] ? spec[key].length + 2 : 0
82
+ }.max
83
+ }
84
+
85
+ # the string we're going to sprintf our values against, with standardized column widths
86
+ format_string = lengths.map{ |len| "%-#{len}s" }
87
+
88
+ # find the max length for the 'type' column, which is special
89
+ type_length = column_specs.map{ |column| column[:type].length }.max
90
+
91
+ # add column type definition to our format string
92
+ format_string.unshift " t.%-#{type_length}s "
93
+
94
+ format_string *= ''
95
+
96
+ column_specs.each do |colspec|
97
+ values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
98
+ values.unshift colspec[:type]
99
+ tbl.print((format_string % values).gsub(/,\s*$/, ''))
100
+ tbl.puts
101
+ end
102
+
103
+ tbl.puts " end"
104
+ tbl.puts
105
+
106
+ indexes(table, tbl)
107
+
108
+ tbl.rewind
109
+ stream.print tbl.read
110
+ rescue => e
111
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
112
+ stream.puts "# #{e.message}"
113
+ stream.puts
114
+ end
115
+
116
+ dumped_tables << table
117
+ stream
118
+ end
119
+
120
+ # Output indexes but don't output indexes that are inherited from parent tables
121
+ # since those will be created by create_table.
122
+ def indexes(table, stream)
123
+ if (indexes = @connection.indexes(table)).any?
124
+ if parent_table = @connection.parent_table(table)
125
+ parent_indexes = @connection.indexes(parent_table)
126
+ end
127
+
128
+ indexes.delete_if {|i| is_parent_index?(i, parent_indexes) } if parent_indexes
129
+ return if indexes.empty?
130
+
131
+ add_index_statements = indexes.map do |index|
132
+ statement_parts = [
133
+ ('add_index ' + remove_prefix_and_suffix(index.table).inspect),
134
+ index.columns.inspect,
135
+ ('name: ' + index.name.inspect),
136
+ ]
137
+ statement_parts << 'unique: true' if index.unique
138
+
139
+ index_lengths = (index.lengths || []).compact
140
+ statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
141
+
142
+ index_orders = (index.orders || {})
143
+ statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
144
+
145
+ statement_parts << ('where: ' + index.where.inspect) if index.where
146
+
147
+ statement_parts << ('using: ' + index.using.inspect) if index.using
148
+
149
+ statement_parts << ('type: ' + index.type.inspect) if index.type
150
+
151
+ ' ' + statement_parts.join(', ')
152
+ end
153
+
154
+ stream.puts add_index_statements.sort.join("\n")
155
+ stream.puts
156
+ end
157
+ end
158
+
159
+
160
+ def remove_prefix_and_suffix(table)
161
+ table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
162
+ end
163
+
164
+ def already_dumped?(table)
165
+ dumped_tables.include? table
166
+ end
167
+
168
+ def is_parent_index?(index, parent_indexes)
169
+ parent_indexes.each do |pindex|
170
+ return true if pindex.columns == index.columns
171
+ end
172
+ return false
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module MTI
3
+ VERSION = "0.0.2"
4
+ end
5
+ 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.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dale Stevens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-10 00:00:00.000000000 Z
11
+ date: 2016-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -78,7 +78,20 @@ email:
78
78
  executables: []
79
79
  extensions: []
80
80
  extra_rdoc_files: []
81
- files: []
81
+ files:
82
+ - ".gitignore"
83
+ - LICENSE.txt
84
+ - README.md
85
+ - Rakefile
86
+ - active_record-mti.gemspec
87
+ - lib/active_record/mti.rb
88
+ - lib/active_record/mti/calculations.rb
89
+ - lib/active_record/mti/connection_adapters/postgresql/schema_statements.rb
90
+ - lib/active_record/mti/inheritance.rb
91
+ - lib/active_record/mti/query_methods.rb
92
+ - lib/active_record/mti/railtie.rb
93
+ - lib/active_record/mti/schema_dumper.rb
94
+ - lib/active_record/mti/version.rb
82
95
  homepage: ''
83
96
  licenses:
84
97
  - MIT