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, options = {}, &block) ⇒ Migration

Creates a new migration.

Parameters:

  • position (Symbol, String, Integer)

    The position or version the migration belongs to.

  • name (Symbol)

    The name of the migration.

  • options (Hash) (defaults to: {})

    Additional options for the migration.

Options Hash (options):

  • :verbose (Boolean) — default: true

    Enables or disables verbose output.

  • :repository (Symbol) — default: :default

    The DataMapper repository the migration will operate on.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/dm-migrations/migration.rb', line 37

def initialize(position, name, options = {}, &block)
  @position    = position
  @name        = name
  @options     = options
  @verbose     = options.fetch(:verbose, true)
  @up_action   = nil
  @down_action = nil

  @repository = if options.key?(:database)
                  warn 'Using the :database option with migrations is deprecated, use :repository instead'
                  options[:database]
                else
                  options.fetch(:repository, :default)
                end

  instance_eval(&block)
end

Instance Attribute Details

#nameObject (readonly)

The name of the migration



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

def name
  @name
end

#positionObject (readonly)

The position or version the migration belongs to



11
12
13
# File 'lib/dm-migrations/migration.rb', line 11

def position
  @position
end

#repositoryObject (readonly)

The repository the migration operates on



17
18
19
# File 'lib/dm-migrations/migration.rb', line 17

def repository
  @repository
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.



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

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

#adapterDataMapper::Adapter

The adapter the migration will use.

Returns:

  • (DataMapper::Adapter)

    The adapter the migration will operate on.

Since:

  • 1.0.1



78
79
80
81
82
# File 'lib/dm-migrations/migration.rb', line 78

def adapter
  setup! unless setup?

  @adapter
end

#create_index(table_name, *columns_and_options) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/dm-migrations/migration.rb', line 175

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

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

  execute DataMapper::Ext::String.compress_lines(<<-SQL)
    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



235
236
237
238
239
240
# File 'lib/dm-migrations/migration.rb', line 235

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

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



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

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

#databaseSymbol?

Deprecated.

Use #repository instead.

The repository the migration will operate on.

Returns:

  • (Symbol, nil)

    The name of the DataMapper repository the migration will run against.

Since:

  • 1.0.1.



65
66
67
68
# File 'lib/dm-migrations/migration.rb', line 65

def database
  warn 'Using the DataMapper::Migration#database method is deprecated, use #repository instead'
  @repository
end

#down(&block) ⇒ Object

define the actions that should be performed on a down migration



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

def down(&block)
  @down_action = block
end

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



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

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



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

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



273
274
275
# File 'lib/dm-migrations/migration.rb', line 273

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

#migration_info_table_exists?Boolean

Returns:

  • (Boolean)


247
248
249
# File 'lib/dm-migrations/migration.rb', line 247

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

#migration_name_columnObject

Quoted ‘migration_name` column, for the adapter



278
279
280
# File 'lib/dm-migrations/migration.rb', line 278

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



252
253
254
255
256
# File 'lib/dm-migrations/migration.rb', line 252

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



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

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)


266
267
268
269
270
# File 'lib/dm-migrations/migration.rb', line 266

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)


259
260
261
262
263
# File 'lib/dm-migrations/migration.rb', line 259

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



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/dm-migrations/migration.rb', line 115

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
    if @down_action
      say_with_time "== Performing Down Migration ##{position}: #{name}", 0 do
        result = @down_action&.call
      end
    end

    update_migration_info(:down)
    # end
  end

  result
end

#perform_upObject

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



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/dm-migrations/migration.rb', line 95

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
    if @up_action
      say_with_time "== Performing Up Migration ##{position}: #{name}", 0 do
        result = @up_action&.call
      end
    end

    update_migration_info(:up)
    # end
  end

  result
end

#quote_column_name(column_name) ⇒ Object



287
288
289
290
# File 'lib/dm-migrations/migration.rb', line 287

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



282
283
284
285
# File 'lib/dm-migrations/migration.rb', line 282

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



243
244
245
# File 'lib/dm-migrations/migration.rb', line 243

def quoted_name
  "'#{name}'"
end

#say(message, indent = 4) ⇒ Object

Output some text. Optional indent level



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

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.



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

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

#select(sql, *bind_values) ⇒ Array<Struct>

Execute raw SQL and return a result set.

Parameters:

  • sql (String)

    The raw SQL statement.

  • bind_values (Array)

    Additional values to bind to the statement.

Returns:

  • (Array<Struct>)

    The result set.

Since:

  • 1.3.0



155
156
157
158
159
# File 'lib/dm-migrations/migration.rb', line 155

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

#up(&block) ⇒ Object

define the actions that should be performed on an up migration



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

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



221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/dm-migrations/migration.rb', line 221

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



216
217
218
# File 'lib/dm-migrations/migration.rb', line 216

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