Class: ActiveRecordInlineSchema::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record_inline_schema/config.rb

Defined Under Namespace

Classes: Column, Index

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model) ⇒ Config

Returns a new instance of Config.



9
10
11
12
13
# File 'lib/active_record_inline_schema/config.rb', line 9

def initialize(model)
  @model = model
  @ideal_columns = ::Set.new
  @ideal_indexes = ::Set.new
end

Instance Attribute Details

#ideal_columnsObject (readonly)

Returns the value of attribute ideal_columns.



6
7
8
# File 'lib/active_record_inline_schema/config.rb', line 6

def ideal_columns
  @ideal_columns
end

#ideal_indexesObject (readonly)

Returns the value of attribute ideal_indexes.



7
8
9
# File 'lib/active_record_inline_schema/config.rb', line 7

def ideal_indexes
  @ideal_indexes
end

#modelObject (readonly)

Returns the value of attribute model.



5
6
7
# File 'lib/active_record_inline_schema/config.rb', line 5

def model
  @model
end

Instance Method Details

#add_ideal_column(column_name, options) ⇒ Object



15
16
17
# File 'lib/active_record_inline_schema/config.rb', line 15

def add_ideal_column(column_name, options)
  ideal_columns.add Column.new(self, column_name, options)
end

#add_ideal_index(column_name, options) ⇒ Object



19
20
21
# File 'lib/active_record_inline_schema/config.rb', line 19

def add_ideal_index(column_name, options)
  ideal_indexes.add Index.new(self, column_name, options)
end

#apply(options) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/active_record_inline_schema/config.rb', line 27

def apply(options)
  dry_run = options.fetch(:dry_run, false)
  has_primary_key = true
  non_standard_primary_key = false

  if !model.primary_key
    has_primary_key = false
  elsif (primary_key_column = find_ideal_column(model.primary_key))
    non_standard_primary_key = (primary_key_column.type != :primary_key)
  elsif model.primary_key != 'id'
    non_standard_primary_key = true
  end
  
  if non_standard_primary_key
    if primary_key_column and (postgresql? or sqlite?)
      primary_key_column.options[:null] = false
    end
  elsif has_primary_key
    add_ideal_column :id, :type => :primary_key
  end

  table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new connection
  ideal_columns.each do |ideal_column|
    ideal_column.inject table_definition
  end

  # Table doesn't exist, create it
  unless connection.table_exists? model.table_name
    statements = []
    statements << "CREATE TABLE #{model.quoted_table_name} (#{table_definition.to_sql}) #{options[:create_table]}"

    if non_standard_primary_key
      if postgresql?
        statements << %{ALTER TABLE #{model.quoted_table_name} ADD PRIMARY KEY (#{model.quoted_primary_key})}
      elsif mysql?
        k = model.quoted_primary_key
        statements.first.sub! /#{k}([^\)]+)\)([^\),]*)/, "#{k}\\1) PRIMARY KEY"
      end
    end

    statements.each do |sql|
      if dry_run
        log_dry sql
      else
        connection.execute sql
      end
    end
    safe_reset_column_information
  end

  if non_standard_primary_key and sqlite?
    # make sure this doesn't get deleted later
    add_ideal_index model.primary_key, :unique => true
  end

  # Remove fields from db no longer in schema
  unless options[:gentle]
    existing_column_names.reject do |existing_column_name|
      find_ideal_column existing_column_name
    end.each do |existing_column_name|
      if dry_run
        log_dry "remove column #{model.table_name}.#{existing_column_name}"
      else
        connection.remove_column model.table_name, existing_column_name
      end
    end
  end

  # Add fields to db new to schema
  ideal_columns.reject do |ideal_column|
    find_existing_column ideal_column.name
  end.each do |ideal_column|
    if dry_run
      log_dry "add column #{model.table_name}.#{ideal_column.name} #{ideal_column.type} #{ideal_column.options.inspect}"
    else
      connection.add_column model.table_name, ideal_column.name, ideal_column.type, ideal_column.options
    end
  end

  # Change attributes of existent columns
  existing_columns_hash.reject do |existing_column_name, existing_column|
    existing_column_name.to_s == model.primary_key.to_s
  end.each do |existing_column_name, existing_column|
    next unless (ideal_column = find_ideal_column(existing_column_name))

    option_changes = {}

    # First, check if the field type changed
    type_changed = !([existing_column.type.to_s, existing_column.sql_type.to_s].include?(ideal_column.type.to_s))

    # Next, iterate through our extended attributes, looking for any differences
    # This catches stuff like :null, :precision, etc
    ideal_column.options.except(:base).each do |k, v|
      if !v.nil? and v != existing_column.send(k)
        option_changes[k] = v
      end
    end

    # Change the column if applicable
    if type_changed or option_changes.any?
      if dry_run
        log_dry "change column #{model.table_name}.#{existing_column_name} #{ideal_column.type} #{option_changes.inspect}"
      else
        connection.change_column model.table_name, existing_column_name, ideal_column.type, option_changes
      end
    end
  end

  # Remove old index
  unless options[:gentle]
    existing_index_names.reject do |existing_index_name|
      find_ideal_index existing_index_name
    end.each do |existing_index_name|
      if dry_run
        log_dry "remove index #{model.table_name} #{existing_index_name}"
      else
        connection.remove_index model.table_name, :name => existing_index_name
      end
    end
  end

  # Add indexes
  ideal_indexes.reject do |ideal_index|
    find_existing_index ideal_index.name
  end.each do |ideal_index|
    if dry_run
      log_dry "add index #{model.table_name}.#{ideal_index.column_name} #{ideal_index.options.inspect}"
    else
      connection.add_index model.table_name, ideal_index.column_name, ideal_index.options
    end
  end

  safe_reset_column_information
end

#log_dry(msg) ⇒ Object



23
24
25
# File 'lib/active_record_inline_schema/config.rb', line 23

def log_dry(msg)
  (ActiveRecord::Base.logger || (@logger ||= Logger.new($stderr))).info "[ActiveRecordInlineSchema DRY RUN] #{msg}"
end