active_record-mti 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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