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
39
40
# 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

  if @adapter.respond_to?(:dialect)
    begin
      @adapter.extend(SQL.const_get("#{@adapter.dialect}"))
    rescue NameError
      raise "Unsupported Migration Adapter #{@adapter.class} with SQL dialect #{@adapter.dialect}"
    end
  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 position, then by name, so at least the order is predictable.



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

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



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

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



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

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



89
90
91
# File 'lib/dm-migrations/migration.rb', line 89

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



48
49
50
# File 'lib/dm-migrations/migration.rb', line 48

def down(&block)
  @down_action = block
end

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



93
94
95
# File 'lib/dm-migrations/migration.rb', line 93

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



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

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



198
199
200
# File 'lib/dm-migrations/migration.rb', line 198

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

#migration_info_table_exists?Boolean

Returns:

  • (Boolean)


175
176
177
# File 'lib/dm-migrations/migration.rb', line 175

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

#migration_name_columnObject

Quoted ‘migration_name` column, for the adapter



203
204
205
# File 'lib/dm-migrations/migration.rb', line 203

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



180
181
182
183
# File 'lib/dm-migrations/migration.rb', line 180

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



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

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)


192
193
194
195
# File 'lib/dm-migrations/migration.rb', line 192

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)


186
187
188
189
# File 'lib/dm-migrations/migration.rb', line 186

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



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

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



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

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



212
213
214
215
# File 'lib/dm-migrations/migration.rb', line 212

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



207
208
209
210
# File 'lib/dm-migrations/migration.rb', line 207

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



171
172
173
# File 'lib/dm-migrations/migration.rb', line 171

def quoted_name
  "'#{name}'"
end

#say(message, indent = 4) ⇒ Object

Output some text. Optional indent level



130
131
132
# File 'lib/dm-migrations/migration.rb', line 130

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.



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

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



43
44
45
# File 'lib/dm-migrations/migration.rb', line 43

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



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

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



144
145
146
# File 'lib/dm-migrations/migration.rb', line 144

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