Class: DataMapper::Migration

Inherits:
Object
  • Object
show all
Includes:
SQL
Defined in:
lib/dm-migrations/migration.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(position, name, opts = {}, &block) ⇒ Migration

Returns a new instance of Migration.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/dm-migrations/migration.rb', line 17

def initialize( position, name, opts = {}, &block )
  @position, @name = position, name
  @options = opts

  @database = DataMapper.repository(@options[:database] || :default)
  @adapter = @database.adapter

  case @adapter.class.to_s
  when /Sqlite3/  then @adapter.extend(SQL::Sqlite3)
  when /Mysql/    then @adapter.extend(SQL::Mysql)
  when /Postgres/ then @adapter.extend(SQL::Postgresql)
  else
    raise "Unsupported Migration Adapter #{@adapter.class}"
  end

  @verbose = @options.has_key?(:verbose) ? @options[:verbose] : true

  @up_action   = lambda {}
  @down_action = lambda {}

  instance_eval &block
end

Instance Attribute Details

#adapterObject

Returns the value of attribute adapter.



15
16
17
# File 'lib/dm-migrations/migration.rb', line 15

def adapter
  @adapter
end

#databaseObject

Returns the value of attribute database.



15
16
17
# File 'lib/dm-migrations/migration.rb', line 15

def database
  @database
end

#nameObject

Returns the value of attribute name.



15
16
17
# File 'lib/dm-migrations/migration.rb', line 15

def name
  @name
end

#positionObject

Returns the value of attribute position.



15
16
17
# File 'lib/dm-migrations/migration.rb', line 15

def position
  @position
end

Instance Method Details

#<=>(other) ⇒ Object

Orders migrations by position, so we know what order to run them in. First order by postition, then by name, so at least the order is predictable.



119
120
121
122
123
124
125
# File 'lib/dm-migrations/migration.rb', line 119

def <=> other
  if self.position == other.position
    self.name.to_s <=> other.name.to_s
  else
    self.position <=> other.position
  end
end

#create_index(table_name, *columns_and_options) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/dm-migrations/migration.rb', line 101

def create_index(table_name, *columns_and_options)
  if columns_and_options.last.is_a?(Hash)
    opts = columns_and_options.pop
  else
    opts = {}
  end
  columns = columns_and_options.flatten

  opts[:name] ||= "#{opts[:unique] ? 'unique_' : ''}index_#{table_name}_#{columns.join('_')}"

  execute <<-SQL.compress_lines
    CREATE #{opts[:unique] ? 'UNIQUE ' : '' }INDEX #{quote_column_name(opts[:name])} ON
    #{quote_table_name(table_name)} (#{columns.map { |c| quote_column_name(c) }.join(', ') })
  SQL
end

#create_migration_info_table_if_neededObject



160
161
162
163
164
165
166
# File 'lib/dm-migrations/migration.rb', line 160

def create_migration_info_table_if_needed
  save, @verbose = @verbose, false
  unless migration_info_table_exists?
    execute("CREATE TABLE #{migration_info_table} (#{migration_name_column} VARCHAR(255) UNIQUE)")
  end
  @verbose = save
end

#create_table(table_name, opts = {}, &block) ⇒ Object



87
88
89
# File 'lib/dm-migrations/migration.rb', line 87

def create_table(table_name, opts = {}, &block)
  execute TableCreator.new(@adapter, table_name, opts, &block).to_sql
end

#down(&block) ⇒ Object

define the actions that should be performed on a down migration



46
47
48
# File 'lib/dm-migrations/migration.rb', line 46

def down(&block)
  @down_action = block
end

#drop_table(table_name, opts = {}) ⇒ Object



91
92
93
# File 'lib/dm-migrations/migration.rb', line 91

def drop_table(table_name, opts = {})
  execute "DROP TABLE #{@adapter.send(:quote_name, table_name.to_s)}"
end

#execute(sql, *bind_values) ⇒ Object

execute raw SQL



81
82
83
84
85
# File 'lib/dm-migrations/migration.rb', line 81

def execute(sql, *bind_values)
  say_with_time(sql) do
    @adapter.execute(sql, *bind_values)
  end
end

#migration_info_tableObject

Quoted table name, for the adapter



196
197
198
# File 'lib/dm-migrations/migration.rb', line 196

def migration_info_table
  @migration_info_table ||= quote_table_name('migration_info')
end

#migration_info_table_exists?Boolean

Returns:

  • (Boolean)


173
174
175
# File 'lib/dm-migrations/migration.rb', line 173

def migration_info_table_exists?
  adapter.storage_exists?('migration_info')
end

#migration_name_columnObject

Quoted ‘migration_name` column, for the adapter



201
202
203
# File 'lib/dm-migrations/migration.rb', line 201

def migration_name_column
  @migration_name_column ||= quote_column_name('migration_name')
end

#migration_recordObject

Fetch the record for this migration out of the migration_info table



178
179
180
181
# File 'lib/dm-migrations/migration.rb', line 178

def migration_record
  return [] unless migration_info_table_exists?
  @adapter.select("SELECT #{migration_name_column} FROM #{migration_info_table} WHERE #{migration_name_column} = #{quoted_name}")
end

#modify_table(table_name, opts = {}, &block) ⇒ Object



95
96
97
98
99
# File 'lib/dm-migrations/migration.rb', line 95

def modify_table(table_name, opts = {}, &block)
  TableModifier.new(@adapter, table_name, opts, &block).statements.each do |sql|
    execute(sql)
  end
end

#needs_down?Boolean

True if the migration has already been run

Returns:

  • (Boolean)


190
191
192
193
# File 'lib/dm-migrations/migration.rb', line 190

def needs_down?
  return false unless migration_info_table_exists?
  ! migration_record.empty?
end

#needs_up?Boolean

True if the migration needs to be run

Returns:

  • (Boolean)


184
185
186
187
# File 'lib/dm-migrations/migration.rb', line 184

def needs_up?
  return true unless migration_info_table_exists?
  migration_record.empty?
end

#perform_downObject

un-do the migration by running the code in the #down block



66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/dm-migrations/migration.rb', line 66

def perform_down
  result = nil
  if needs_down?
    # TODO: fix this so it only does transactions for databases that support create/drop
    # database.transaction.commit do
      say_with_time "== Performing Down Migration ##{position}: #{name}", 0 do
        result = @down_action.call
      end
      update_migration_info(:down)
    # end
  end
  result
end

#perform_upObject

perform the migration by running the code in the #up block



51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/dm-migrations/migration.rb', line 51

def perform_up
  result = nil
  if needs_up?
    # TODO: fix this so it only does transactions for databases that support create/drop
    # database.transaction.commit do
      say_with_time "== Performing Up Migration ##{position}: #{name}", 0 do
        result = @up_action.call
      end
      update_migration_info(:up)
    # end
  end
  result
end

#quote_column_name(column_name) ⇒ Object



210
211
212
213
# File 'lib/dm-migrations/migration.rb', line 210

def quote_column_name(column_name)
  # TODO: Fix this for 1.9 - can't use this hack to access a private method
  @adapter.send(:quote_name, column_name.to_s)
end

#quote_table_name(table_name) ⇒ Object



205
206
207
208
# File 'lib/dm-migrations/migration.rb', line 205

def quote_table_name(table_name)
  # TODO: Fix this for 1.9 - can't use this hack to access a private method
  @adapter.send(:quote_name, table_name.to_s)
end

#quoted_nameObject

Quote the name of the migration for use in SQL



169
170
171
# File 'lib/dm-migrations/migration.rb', line 169

def quoted_name
  "'#{name}'"
end

#say(message, indent = 4) ⇒ Object

Output some text. Optional indent level



128
129
130
# File 'lib/dm-migrations/migration.rb', line 128

def say(message, indent = 4)
  write "#{" " * indent} #{message}"
end

#say_with_time(message, indent = 2) ⇒ Object

Time how long the block takes to run, and output it with the message.



133
134
135
136
137
138
139
# File 'lib/dm-migrations/migration.rb', line 133

def say_with_time(message, indent = 2)
  say(message, indent)
  result = nil
  time = Benchmark.measure { result = yield }
  say("-> %.4fs" % time.real, indent)
  result
end

#up(&block) ⇒ Object

define the actions that should be performed on an up migration



41
42
43
# File 'lib/dm-migrations/migration.rb', line 41

def up(&block)
  @up_action = block
end

#update_migration_info(direction) ⇒ Object

Inserts or removes a row into the ‘migration_info` table, so we can mark this migration as run, or un-done



147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/dm-migrations/migration.rb', line 147

def update_migration_info(direction)
  save, @verbose = @verbose, false

  create_migration_info_table_if_needed

  if direction.to_sym == :up
    execute("INSERT INTO #{migration_info_table} (#{migration_name_column}) VALUES (#{quoted_name})")
  elsif direction.to_sym == :down
    execute("DELETE FROM #{migration_info_table} WHERE #{migration_name_column} = #{quoted_name}")
  end
  @verbose = save
end

#write(text = "") ⇒ Object

output the given text, but only if verbose mode is on



142
143
144
# File 'lib/dm-migrations/migration.rb', line 142

def write(text="")
  puts text if @verbose
end