active_column 0.1.1 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,267 +1,129 @@
1
1
  module ActiveColumn
2
2
 
3
- class IrreversibleMigration < ActiveColumnError
4
- end
3
+ class Migration
5
4
 
6
- class DuplicateMigrationVersionError < ActiveColumnError#:nodoc:
7
- def initialize(version)
8
- super("Multiple migrations have the version number #{version}")
9
- end
10
- end
5
+ @@verbose = true
11
6
 
12
- class DuplicateMigrationNameError < ActiveColumnError#:nodoc:
13
- def initialize(name)
14
- super("Multiple migrations have the name #{name}")
7
+ def self.verbose=(verbose)
8
+ @@verbose = verbose
15
9
  end
16
- end
17
10
 
18
- class UnknownMigrationVersionError < ActiveColumnError #:nodoc:
19
- def initialize(version)
20
- super("No migration with version number #{version}")
11
+ def self.verbose
12
+ @@verbose
21
13
  end
22
- end
23
-
24
- class IllegalMigrationNameError < ActiveColumnError#:nodoc:
25
- def initialize(name)
26
- super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
27
- end
28
- end
29
-
30
- class Migration
31
14
 
15
+ # Returns the raw connection to Cassandra
32
16
  def self.connection
33
- $cassandra
17
+ ActiveColumn.connection
34
18
  end
35
19
 
36
20
  def self.migrate(direction)
37
21
  return unless respond_to?(direction)
38
- send direction
39
- end
40
-
41
- def self.create_column_family(name, options = {})
42
- ActiveColumn::Tasks::ColumnFamily.new.create(name, options)
43
- end
44
-
45
- def self.drop_column_family(name)
46
- ActiveColumn::Tasks::ColumnFamily.new.drop(name)
47
- end
48
-
49
- end
50
-
51
- # MigrationProxy is used to defer loading of the actual migration classes
52
- # until they are needed
53
- class MigrationProxy
54
-
55
- attr_accessor :name, :version, :filename
56
-
57
- delegate :migrate, :announce, :write, :to=>:migration
58
-
59
- private
60
-
61
- def migration
62
- @migration ||= load_migration
63
- end
64
-
65
- def load_migration
66
- require(File.expand_path(filename))
67
- name.constantize
68
- end
69
-
70
- end
71
-
72
- class Migrator
73
22
 
74
- def self.migrate(migrations_path, target_version = nil)
75
- case
76
- when target_version.nil?
77
- up(migrations_path, target_version)
78
- when current_version == 0 && target_version == 0
79
- when current_version > target_version
80
- down(migrations_path, target_version)
81
- else
82
- up(migrations_path, target_version)
23
+ case direction
24
+ when :up then announce "migrating"
25
+ when :down then announce "reverting"
83
26
  end
84
- end
85
27
 
86
- def self.rollback(migrations_path, steps = 1)
87
- move(:down, migrations_path, steps)
88
- end
28
+ result = nil
29
+ time = Benchmark.measure { result = send("#{direction}") }
89
30
 
90
- def self.forward(migrations_path, steps = 1)
91
- move(:up, migrations_path, steps)
92
- end
93
-
94
- def self.up(migrations_path, target_version = nil)
95
- self.new(:up, migrations_path, target_version).migrate
96
- end
97
-
98
- def self.down(migrations_path, target_version = nil)
99
- self.new(:down, migrations_path, target_version).migrate
100
- end
31
+ case direction
32
+ when :up then announce "migrated (%.4fs)" % time.real; write
33
+ when :down then announce "reverted (%.4fs)" % time.real; write
34
+ end
101
35
 
102
- def self.run(direction, migrations_path, target_version)
103
- self.new(direction, migrations_path, target_version).run
36
+ result
104
37
  end
105
38
 
106
- def self.migrations_path
107
- 'ks/migrate'
39
+ # Creates a new column family with the given name. Column family configurations can be set within
40
+ # a block like this:
41
+ #
42
+ # create_column_family(:users) do |cf|
43
+ # cf.comment = 'Users column family'
44
+ # cf.comparator_type = 'TimeUUIDType'
45
+ # end
46
+ #
47
+ # A complete list of available configuration settings is here:
48
+ #
49
+ # http://github.com/fauna/cassandra/blob/master/vendor/0.7/gen-rb/cassandra_types.rb
50
+ #
51
+ # Scroll down to the CfDef definition.
52
+ def self.create_column_family(name, &block)
53
+ ActiveColumn.column_family_tasks.create(name, &block)
108
54
  end
109
55
 
110
- def self.schema_migrations_column_family
111
- :schema_migrations
56
+ # Drops the given column family
57
+ def self.drop_column_family(name)
58
+ ActiveColumn.column_family_tasks.drop(name)
112
59
  end
113
60
 
114
- def self.get_all_versions
115
- cas = ActiveColumn.connection
116
- cas.get(schema_migrations_column_family, 'all').map {|(name, _value)| name.to_i}.sort
61
+ # Renames the column family from the old name to the new name
62
+ def self.rename_column_family(old_name, new_name)
63
+ ActiveColumn.column_family_tasks.rename(old_name, new_name)
117
64
  end
118
65
 
119
- def self.current_version
120
- sm_cf = schema_migrations_column_family
121
- cf = ActiveColumn::Tasks::ColumnFamily.new
122
- if cf.exists?(sm_cf)
123
- get_all_versions.max || 0
124
- else
125
- 0
126
- end
127
- end
128
-
129
- private
130
-
131
- def self.move(direction, migrations_path, steps)
132
- migrator = self.new(direction, migrations_path)
133
- start_index = migrator.migrations.index(migrator.current_migration)
134
-
135
- if start_index
136
- finish = migrator.migrations[start_index + steps]
137
- version = finish ? finish.version : 0
138
- send(direction, migrations_path, version)
139
- end
66
+ def self.write(text="")
67
+ puts(text) if verbose
140
68
  end
141
69
 
142
- public
70
+ def self.announce(message)
71
+ version = defined?(@version) ? @version : nil
143
72
 
144
- def initialize(direction, migrations_path, target_version = nil)
145
- cf = ActiveColumn::Tasks::ColumnFamily.new
146
- sm_cf = self.class.schema_migrations_column_family
147
-
148
- unless cf.exists?(sm_cf)
149
- cf.create(sm_cf, :comparator_type => 'LongType')
150
- end
151
-
152
- @direction, @migrations_path, @target_version = direction, migrations_path, target_version
73
+ text = "#{version} #{name}: #{message}"
74
+ length = [0, 75 - text.length].max
75
+ write "== %s %s" % [text, "=" * length]
153
76
  end
154
77
 
155
- def current_version
156
- migrated.last || 0
78
+ def self.say(message, subitem=false)
79
+ write "#{subitem ? " ->" : "--"} #{message}"
157
80
  end
158
81
 
159
- def current_migration
160
- migrations.detect { |m| m.version == current_version }
82
+ def self.say_with_time(message)
83
+ say(message)
84
+ result = nil
85
+ time = Benchmark.measure { result = yield }
86
+ say "%.4fs" % time.real, :subitem
87
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
88
+ result
161
89
  end
162
90
 
163
- def run
164
- target = migrations.detect { |m| m.version == @target_version }
165
- raise UnknownMigrationVersionError.new(@target_version) if target.nil?
166
- unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
167
- target.migrate(@direction)
168
- record_version_state_after_migrating(target)
169
- end
91
+ def self.suppress_messages
92
+ save, self.verbose = verbose, false
93
+ yield
94
+ ensure
95
+ self.verbose = save
170
96
  end
171
97
 
172
- def migrate
173
- current = migrations.detect { |m| m.version == current_version }
174
- target = migrations.detect { |m| m.version == @target_version }
175
-
176
- if target.nil? && !@target_version.nil? && @target_version > 0
177
- raise UnknownMigrationVersionError.new(@target_version)
178
- end
179
-
180
- start = up? ? 0 : (migrations.index(current) || 0)
181
- finish = migrations.index(target) || migrations.size - 1
182
- runnable = migrations[start..finish]
183
-
184
- # skip the last migration if we're headed down, but not ALL the way down
185
- runnable.pop if down? && !target.nil?
186
-
187
- runnable.each do |migration|
188
- #puts "Migrating to #{migration.name} (#{migration.version})"
189
-
190
- # On our way up, we skip migrating the ones we've already migrated
191
- next if up? && migrated.include?(migration.version.to_i)
192
-
193
- # On our way down, we skip reverting the ones we've never migrated
194
- if down? && !migrated.include?(migration.version.to_i)
195
- migration.announce 'never migrated, skipping'; migration.write
196
- next
197
- end
198
-
199
- migration.migrate(@direction)
200
- record_version_state_after_migrating(migration)
201
- end
202
- end
203
-
204
- def migrations
205
- @migrations ||= begin
206
- files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
207
-
208
- migrations = files.inject([]) do |klasses, file|
209
- version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
210
-
211
- raise IllegalMigrationNameError.new(file) unless version
212
- version = version.to_i
213
-
214
- if klasses.detect { |m| m.version == version }
215
- raise DuplicateMigrationVersionError.new(version)
216
- end
98
+ end
217
99
 
218
- if klasses.detect { |m| m.name == name.camelize }
219
- raise DuplicateMigrationNameError.new(name.camelize)
220
- end
100
+ # MigrationProxy is used to defer loading of the actual migration classes
101
+ # until they are needed
102
+ class MigrationProxy
221
103
 
222
- migration = MigrationProxy.new
223
- migration.name = name.camelize
224
- migration.version = version
225
- migration.filename = file
226
- klasses << migration
227
- end
104
+ attr_accessor :name, :version, :filename
228
105
 
229
- migrations = migrations.sort_by { |m| m.version }
230
- down? ? migrations.reverse : migrations
231
- end
106
+ def migrate(*args)
107
+ migration.migrate *args
232
108
  end
233
109
 
234
- def pending_migrations
235
- already_migrated = migrated
236
- migrations.reject { |m| already_migrated.include?(m.version.to_i) }
110
+ def announce(*args)
111
+ migration.announce *args
237
112
  end
238
113
 
239
- def migrated
240
- @migrated_versions ||= self.class.get_all_versions
114
+ def write(*args)
115
+ migration.write *args
241
116
  end
242
117
 
243
118
  private
244
119
 
245
- def record_version_state_after_migrating(migration)
246
- cas = ActiveColumn.connection
247
- sm_cf = self.class.schema_migrations_column_family
248
-
249
- @migrated_versions ||= []
250
- if down?
251
- @migrated_versions.delete(migration.version)
252
- cas.remove sm_cf, 'all', migration.version
253
- else
254
- @migrated_versions.push(migration.version).sort!
255
- cas.insert sm_cf, 'all', { migration.version => migration.name }
256
- end
257
- end
258
-
259
- def up?
260
- @direction == :up
120
+ def migration
121
+ @migration ||= load_migration
261
122
  end
262
123
 
263
- def down?
264
- @direction == :down
124
+ def load_migration
125
+ require(File.expand_path(filename))
126
+ eval(name)
265
127
  end
266
128
 
267
129
  end
@@ -0,0 +1,231 @@
1
+ module ActiveColumn
2
+ class IrreversibleMigration < ActiveColumnError
3
+ end
4
+
5
+ class DuplicateMigrationVersionError < ActiveColumnError#:nodoc:
6
+ def initialize(version)
7
+ super("Multiple migrations have the version number #{version}")
8
+ end
9
+ end
10
+
11
+ class DuplicateMigrationNameError < ActiveColumnError#:nodoc:
12
+ def initialize(name)
13
+ super("Multiple migrations have the name #{name}")
14
+ end
15
+ end
16
+
17
+ class UnknownMigrationVersionError < ActiveColumnError #:nodoc:
18
+ def initialize(version)
19
+ super("No migration with version number #{version}")
20
+ end
21
+ end
22
+
23
+ class IllegalMigrationNameError < ActiveColumnError#:nodoc:
24
+ def initialize(name)
25
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
26
+ end
27
+ end
28
+
29
+ class Migrator
30
+
31
+ def self.migrate(migrations_path, target_version = nil)
32
+ case
33
+ when target_version.nil?
34
+ up(migrations_path, target_version)
35
+ when current_version == 0 && target_version == 0
36
+ when current_version > target_version
37
+ down(migrations_path, target_version)
38
+ else
39
+ up(migrations_path, target_version)
40
+ end
41
+ end
42
+
43
+ def self.rollback(migrations_path, steps = 1)
44
+ move(:down, migrations_path, steps)
45
+ end
46
+
47
+ def self.forward(migrations_path, steps = 1)
48
+ move(:up, migrations_path, steps)
49
+ end
50
+
51
+ def self.up(migrations_path, target_version = nil)
52
+ self.new(:up, migrations_path, target_version).migrate
53
+ end
54
+
55
+ def self.down(migrations_path, target_version = nil)
56
+ self.new(:down, migrations_path, target_version).migrate
57
+ end
58
+
59
+ def self.run(direction, migrations_path, target_version)
60
+ self.new(direction, migrations_path, target_version).run
61
+ end
62
+
63
+ def self.migrations_path
64
+ 'ks/migrate'
65
+ end
66
+
67
+ def self.schema_migrations_column_family
68
+ :schema_migrations
69
+ end
70
+
71
+ def self.get_all_versions
72
+ cas = ActiveColumn.connection
73
+ cas.get(schema_migrations_column_family, 'all').map {|(name, _value)| name.to_i}.sort
74
+ end
75
+
76
+ def self.current_version
77
+ sm_cf = schema_migrations_column_family
78
+ if ActiveColumn.column_family_tasks.exists?(sm_cf)
79
+ get_all_versions.max || 0
80
+ else
81
+ 0
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def self.move(direction, migrations_path, steps)
88
+ migrator = self.new(direction, migrations_path)
89
+ start_index = migrator.migrations.index(migrator.current_migration)
90
+
91
+ if start_index
92
+ finish = migrator.migrations[start_index + steps]
93
+ version = finish ? finish.version : 0
94
+ send(direction, migrations_path, version)
95
+ end
96
+ end
97
+
98
+ public
99
+
100
+ def initialize(direction, migrations_path, target_version = nil)
101
+ cf_tasks = ActiveColumn.column_family_tasks
102
+ sm_cf = self.class.schema_migrations_column_family
103
+
104
+ unless cf_tasks.exists?(sm_cf)
105
+ cf_tasks.create(sm_cf) do |cf|
106
+ cf.comparator_type = 'LongType'
107
+ end
108
+ end
109
+
110
+ @direction, @migrations_path, @target_version = direction, migrations_path, target_version
111
+ end
112
+
113
+ def current_version
114
+ migrated.last || 0
115
+ end
116
+
117
+ def current_migration
118
+ migrations.detect { |m| m.version == current_version }
119
+ end
120
+
121
+ def run
122
+ target = migrations.detect { |m| m.version == @target_version }
123
+ raise UnknownMigrationVersionError.new(@target_version) if target.nil?
124
+ unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
125
+ target.migrate(@direction)
126
+ record_version_state_after_migrating(target)
127
+ end
128
+ end
129
+
130
+ def migrate
131
+ current = migrations.detect { |m| m.version == current_version }
132
+ target = migrations.detect { |m| m.version == @target_version }
133
+
134
+ if target.nil? && !@target_version.nil? && @target_version > 0
135
+ raise UnknownMigrationVersionError.new(@target_version)
136
+ end
137
+
138
+ start = up? ? 0 : (migrations.index(current) || 0)
139
+ finish = migrations.index(target) || migrations.size - 1
140
+ runnable = migrations[start..finish]
141
+
142
+ # skip the last migration if we're headed down, but not ALL the way down
143
+ runnable.pop if down? && !target.nil?
144
+
145
+ runnable.each do |migration|
146
+ #puts "Migrating to #{migration.name} (#{migration.version})"
147
+
148
+ # On our way up, we skip migrating the ones we've already migrated
149
+ next if up? && migrated.include?(migration.version.to_i)
150
+
151
+ # On our way down, we skip reverting the ones we've never migrated
152
+ if down? && !migrated.include?(migration.version.to_i)
153
+ migration.announce 'never migrated, skipping'; migration.write
154
+ next
155
+ end
156
+
157
+ migration.migrate(@direction)
158
+ record_version_state_after_migrating(migration)
159
+ end
160
+ end
161
+
162
+ def migrations
163
+ @migrations ||= begin
164
+ files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
165
+
166
+ migrations = files.inject([]) do |klasses, file|
167
+ version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
168
+
169
+ raise IllegalMigrationNameError.new(file) unless version
170
+ version = version.to_i
171
+
172
+ if klasses.detect { |m| m.version == version }
173
+ raise DuplicateMigrationVersionError.new(version)
174
+ end
175
+
176
+ if klasses.detect { |m| m.name == name.camelize }
177
+ raise DuplicateMigrationNameError.new(name.camelize)
178
+ end
179
+
180
+ migration = MigrationProxy.new
181
+ migration.name = to_camel name
182
+ migration.version = version
183
+ migration.filename = file
184
+ klasses << migration
185
+ end
186
+
187
+ migrations = migrations.sort_by { |m| m.version }
188
+ down? ? migrations.reverse : migrations
189
+ end
190
+ end
191
+
192
+ def pending_migrations
193
+ already_migrated = migrated
194
+ migrations.reject { |m| already_migrated.include?(m.version.to_i) }
195
+ end
196
+
197
+ def migrated
198
+ @migrated_versions ||= self.class.get_all_versions
199
+ end
200
+
201
+ private
202
+
203
+ def record_version_state_after_migrating(migration)
204
+ cas = ActiveColumn.connection
205
+ sm_cf = self.class.schema_migrations_column_family
206
+
207
+ @migrated_versions ||= []
208
+ if down?
209
+ @migrated_versions.delete(migration.version)
210
+ cas.remove sm_cf, 'all', migration.version
211
+ else
212
+ @migrated_versions.push(migration.version).sort!
213
+ cas.insert sm_cf, 'all', { migration.version => migration.name }
214
+ end
215
+ end
216
+
217
+ def up?
218
+ @direction == :up
219
+ end
220
+
221
+ def down?
222
+ @direction == :down
223
+ end
224
+
225
+ def to_camel(lower_case_and_underscored_word)
226
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
227
+ end
228
+
229
+ end
230
+
231
+ end