Class: DataMiner::Schema

Inherits:
Object
  • Object
show all
Includes:
Blockenspiel::DSL
Defined in:
lib/data_miner/schema.rb

Constant Summary collapse

MAX_INDEX_NAME_LENGTH =
50
INDEX_PROPERTIES =
%w{ name columns }
EXTRA_COLUMNS =
{
  :updated_at => :datetime,
  :created_at => :datetime
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base, position_in_run, create_table_options) ⇒ Schema

Returns a new instance of Schema.



10
11
12
13
14
15
16
17
18
# File 'lib/data_miner/schema.rb', line 10

def initialize(base, position_in_run, create_table_options)
  @base = base
  @position_in_run = position_in_run
  @create_table_options = create_table_options
  @create_table_options.symbolize_keys!
  DataMiner.log_or_raise ":id => true is not allowed in create_table_options." if @create_table_options[:id] === true
  DataMiner.log_or_raise ":primary_key is not allowed in create_table_options. Use set_primary_key instead." if @create_table_options.has_key?(:primary_key)
  @create_table_options[:id] = false # always
end

Instance Attribute Details

#baseObject (readonly)

Returns the value of attribute base.



5
6
7
# File 'lib/data_miner/schema.rb', line 5

def base
  @base
end

#create_table_optionsObject (readonly)

Returns the value of attribute create_table_options.



7
8
9
# File 'lib/data_miner/schema.rb', line 7

def create_table_options
  @create_table_options
end

#position_in_runObject (readonly)

Returns the value of attribute position_in_run.



6
7
8
# File 'lib/data_miner/schema.rb', line 6

def position_in_run
  @position_in_run
end

Instance Method Details

#_add_columnsObject



225
226
227
228
229
# File 'lib/data_miner/schema.rb', line 225

def _add_columns
  ideal_table.columns.each do |ideal|
    place_column ideal.name if column_needs_to_be_placed? ideal.name
  end
end

#_add_extra_columnsObject



174
175
176
177
178
# File 'lib/data_miner/schema.rb', line 174

def _add_extra_columns
  EXTRA_COLUMNS.each do |extra_name, extra_type|
    send extra_type, extra_name unless ideal_column extra_name
  end
end

#_add_indexesObject



237
238
239
240
241
242
# File 'lib/data_miner/schema.rb', line 237

def _add_indexes
  ideal_indexes.each do |ideal|
    next if ideal.name == ideal_primary_key_name # this should already have been taken care of
    place_index ideal.name if index_needs_to_be_placed? ideal.name
  end
end

#_create_tableObject



180
181
182
183
184
185
186
187
188
# File 'lib/data_miner/schema.rb', line 180

def _create_table
  if not resource.table_exists?
    DataMiner.log_debug "CREATING TABLE #{table_name} with #{create_table_options.inspect}"
    connection.create_table table_name, create_table_options do |t|
      t.integer :data_miner_placeholder
    end
    resource.reset_column_information
  end
end

#_remove_columnsObject



219
220
221
222
223
# File 'lib/data_miner/schema.rb', line 219

def _remove_columns
  resource.columns_hash.values.each do |actual|
    remove_column actual.name if column_needs_to_be_removed? actual.name
  end
end

#_remove_indexesObject



231
232
233
234
235
# File 'lib/data_miner/schema.rb', line 231

def _remove_indexes
  actual_indexes.each do |actual|
    remove_index actual.name if index_needs_to_be_removed? actual.name
  end
end

#_set_primary_keyObject

FIXME mysql only



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/data_miner/schema.rb', line 191

def _set_primary_key
  if ideal_primary_key_name == 'id' and not ideal_column('id')
    DataMiner.log_debug "no special primary key set on #{table_name}, so using 'id'"
    column 'id', :primary_key
  end
  actual = actual_column actual_primary_key_name
  ideal = ideal_column ideal_primary_key_name
  if not column_equivalent? actual, ideal
    DataMiner.log_debug "looks like #{table_name} has a bad (or missing) primary key"
    if actual
      DataMiner.log_debug "looks like primary key needs to change from #{actual_primary_key_name} to #{ideal_primary_key_name}, re-creating #{table_name} from scratch"
      connection.drop_table table_name
      resource.reset_column_information
      _create_table
    end
    place_column ideal_primary_key_name
    unless ideal.type.to_s == 'primary_key'
      DataMiner.log_debug "SETTING #{ideal_primary_key_name} AS PRIMARY KEY"
      if ActiveRecord::Base.connection.adapter_name.downcase == 'sqlite'
        connection.execute "CREATE UNIQUE INDEX IDX_#{table_name}_#{ideal_primary_key_name} ON #{table_name} (#{ideal_primary_key_name} ASC)"
      else
        connection.execute "ALTER TABLE `#{table_name}` ADD PRIMARY KEY (`#{ideal_primary_key_name}`)"
      end
    end
  end
  resource.reset_column_information
end

#actual_column(name) ⇒ Object



119
120
121
# File 'lib/data_miner/schema.rb', line 119

def actual_column(name)
  resource.columns_hash[name.to_s]
end

#actual_index(name) ⇒ Object



127
128
129
# File 'lib/data_miner/schema.rb', line 127

def actual_index(name)
  actual_indexes.detect { |actual| actual.name == name.to_s }
end

#actual_indexesObject



36
37
38
# File 'lib/data_miner/schema.rb', line 36

def actual_indexes
  connection.indexes table_name
end

#actual_primary_key_nameObject



79
80
81
# File 'lib/data_miner/schema.rb', line 79

def actual_primary_key_name
  connection.primary_key(table_name).to_s
end

#column(*args) ⇒ Object



59
60
61
# File 'lib/data_miner/schema.rb', line 59

def column(*args)
  ideal_table.column(*args)
end

#column_equivalent?(a, b) ⇒ Boolean

FIXME mysql only (assume integer primary keys)

Returns:

  • (Boolean)


93
94
95
96
97
98
# File 'lib/data_miner/schema.rb', line 93

def column_equivalent?(a, b)
  return false unless a and b
  a_type = a.type.to_s == 'primary_key' ? 'integer' : a.type.to_s
  b_type = b.type.to_s == 'primary_key' ? 'integer' : b.type.to_s
  a_type == b_type and a.name.to_s == b.name.to_s
end

#connectionObject



20
21
22
# File 'lib/data_miner/schema.rb', line 20

def connection
  ActiveRecord::Base.connection
end

#descriptionObject



40
41
42
# File 'lib/data_miner/schema.rb', line 40

def description
  "Define a table called #{table_name} with primary key #{ideal_primary_key_name}"
end

#ideal_column(name) ⇒ Object



115
116
117
# File 'lib/data_miner/schema.rb', line 115

def ideal_column(name)
  ideal_table[name.to_s]
end

#ideal_index(name) ⇒ Object



123
124
125
# File 'lib/data_miner/schema.rb', line 123

def ideal_index(name)
  ideal_indexes.detect { |ideal| ideal.name == name.to_s }
end

#ideal_indexesObject



32
33
34
# File 'lib/data_miner/schema.rb', line 32

def ideal_indexes
  @ideal_indexes ||= Array.new
end

#ideal_primary_key_nameObject



75
76
77
# File 'lib/data_miner/schema.rb', line 75

def ideal_primary_key_name
  resource.primary_key.to_s
end

#ideal_tableObject



28
29
30
# File 'lib/data_miner/schema.rb', line 28

def ideal_table
  @ideal_table ||= ActiveRecord::ConnectionAdapters::TableDefinition.new(connection)
end

#index(columns, options = {}) ⇒ Object



64
65
66
67
68
69
70
71
72
73
# File 'lib/data_miner/schema.rb', line 64

def index(columns, options = {})
  options.symbolize_keys!
  columns = Array.wrap columns
  unless name = options[:name]
    default_name = connection.index_name(table_name, options.merge(:column => columns))
    name = default_name.length < MAX_INDEX_NAME_LENGTH ? default_name : default_name[0..MAX_INDEX_NAME_LENGTH-11] + Zlib.crc32(default_name).to_s
  end
  index_unique = options.has_key?(:unique) ? options[:unique] : true
  ideal_indexes.push ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, name, index_unique, columns)
end

#index_equivalent?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
87
88
89
90
# File 'lib/data_miner/schema.rb', line 84

def index_equivalent?(a, b)
  return false unless a and b
  INDEX_PROPERTIES.all? do |property|
    DataMiner.log_debug "...comparing #{a.send(property).inspect}.to_s <-> #{b.send(property).inspect}.to_s"
    a.send(property).to_s == b.send(property).to_s
  end
end

#inspectObject



44
45
46
# File 'lib/data_miner/schema.rb', line 44

def inspect
  "Schema(#{resource}): #{description}"
end

#place_column(name) ⇒ Object



131
132
133
134
135
136
137
# File 'lib/data_miner/schema.rb', line 131

def place_column(name)
  remove_column name if actual_column name
  ideal = ideal_column name
  DataMiner.log_debug "ADDING COLUMN #{name}"
  connection.add_column table_name, name, ideal.type.to_sym # symbol type!
  resource.reset_column_information
end

#place_index(name) ⇒ Object



145
146
147
148
149
150
151
# File 'lib/data_miner/schema.rb', line 145

def place_index(name)
  remove_index name if actual_index name
  ideal = ideal_index name
  DataMiner.log_debug "ADDING INDEX #{name}"
  connection.add_index table_name, ideal.columns, :name => ideal.name
  resource.reset_column_information
end

#remove_column(name) ⇒ Object



139
140
141
142
143
# File 'lib/data_miner/schema.rb', line 139

def remove_column(name)
  DataMiner.log_debug "REMOVING COLUMN #{name}"
  connection.remove_column table_name, name
  resource.reset_column_information
end

#remove_index(name) ⇒ Object



153
154
155
156
157
# File 'lib/data_miner/schema.rb', line 153

def remove_index(name)
  DataMiner.log_debug "REMOVING INDEX #{name}"
  connection.remove_index table_name, :name => name
  resource.reset_column_information
end

#run(run) ⇒ Object



159
160
161
162
163
164
165
166
167
168
# File 'lib/data_miner/schema.rb', line 159

def run(run)
  _add_extra_columns
  _create_table
  _set_primary_key
  _remove_columns
  _add_columns
  _remove_indexes
  _add_indexes
  DataMiner.log_debug "ran #{inspect}"
end

#table_nameObject



24
25
26
# File 'lib/data_miner/schema.rb', line 24

def table_name
  resource.table_name
end