gotime-cassandra_object 0.8.3 → 0.8.4

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'gotime-cassandra_object'
5
- s.version = '0.8.3'
5
+ s.version = '0.8.4'
6
6
  s.description = 'Cassandra ActiveModel'
7
7
  s.summary = 'Cassandra ActiveModel'
8
8
  s.required_rubygems_version = '>= 1.3.5'
@@ -26,8 +26,7 @@ module CassandraObject
26
26
  autoload :FindWithIds
27
27
  autoload :Timestamps
28
28
 
29
- autoload :Migrator
30
- autoload :Migration
29
+ autoload :Schema
31
30
 
32
31
  module Tasks
33
32
  extend ActiveSupport::Autoload
@@ -1,4 +1,4 @@
1
- class <%= name %> < CassandraObject::Migration
1
+ class <%= name %> < CassandraObject::Schema::Migration
2
2
 
3
3
  def self.up
4
4
 
@@ -1,23 +1,15 @@
1
1
  module CassandraObject
2
2
  module Migrations
3
3
  extend ActiveSupport::Concern
4
+ extend ActiveSupport::Autoload
5
+
4
6
  included do
5
7
  class_inheritable_array :migrations
6
8
  class_inheritable_accessor :current_schema_version
7
9
  self.current_schema_version = 0
8
10
  end
9
-
10
- class Migration
11
- attr_reader :version
12
- def initialize(version, block)
13
- @version = version
14
- @block = block
15
- end
16
-
17
- def run(attrs)
18
- @block.call(attrs)
19
- end
20
- end
11
+
12
+ autoload :Migration
21
13
 
22
14
  class MigrationNotFoundError < StandardError
23
15
  def initialize(record_version, migrations)
@@ -0,0 +1,15 @@
1
+ module CassandraObject
2
+ module Migrations
3
+ class Migration
4
+ attr_reader :version
5
+ def initialize(version, block)
6
+ @version = version
7
+ @block = block
8
+ end
9
+
10
+ def run(attrs)
11
+ @block.call(attrs)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ module CassandraObject
2
+ module Schema
3
+ extend ActiveSupport::Autoload
4
+
5
+ class IrreversibleMigration < StandardError
6
+ end
7
+
8
+ class DuplicateMigrationVersionError < StandardError#:nodoc:
9
+ def initialize(version)
10
+ super("Multiple migrations have the version number #{version}")
11
+ end
12
+ end
13
+
14
+ class DuplicateMigrationNameError < StandardError#:nodoc:
15
+ def initialize(name)
16
+ super("Multiple migrations have the name #{name}")
17
+ end
18
+ end
19
+
20
+ class UnknownMigrationVersionError < StandardError #:nodoc:
21
+ def initialize(version)
22
+ super("No migration with version number #{version}")
23
+ end
24
+ end
25
+
26
+ class IllegalMigrationNameError < StandardError#:nodoc:
27
+ def initialize(name)
28
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
29
+ end
30
+ end
31
+
32
+ autoload :Migrator
33
+ autoload :Migration
34
+ autoload :MigrationProxy
35
+
36
+ end
37
+ end
@@ -0,0 +1,106 @@
1
+ module CassandraObject
2
+ module Schema
3
+
4
+ class Migration
5
+
6
+ @@verbose = true
7
+ cattr_accessor :verbose
8
+
9
+ # Returns the raw connection to Cassandra
10
+ def self.connection
11
+ CassandraObject::Base.connection
12
+ end
13
+
14
+ def self.migrate(direction)
15
+ return unless respond_to?(direction)
16
+
17
+ case direction
18
+ when :up then announce "migrating"
19
+ when :down then announce "reverting"
20
+ end
21
+
22
+ result = nil
23
+ time = Benchmark.measure { result = send("#{direction}") }
24
+
25
+ case direction
26
+ when :up then announce "migrated (%.4fs)" % time.real; write
27
+ when :down then announce "reverted (%.4fs)" % time.real; write
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ # Creates a new column family with the given name. Column family configurations can be set within
34
+ # a block like this:
35
+ #
36
+ # create_column_family(:users) do |cf|
37
+ # cf.comment = 'Users column family'
38
+ # cf.comparator_type = 'TimeUUIDType'
39
+ # end
40
+ #
41
+ # A complete list of available configuration settings is here:
42
+ #
43
+ # http://github.com/fauna/cassandra/blob/master/vendor/0.7/gen-rb/cassandra_types.rb
44
+ #
45
+ # Scroll down to the CfDef definition.
46
+ def self.create_column_family(name, &block)
47
+ say_with_time("create_column_family #{name}") do
48
+ column_family_tasks.create(name, &block)
49
+ end
50
+ end
51
+
52
+ # Drops the given column family
53
+ def self.drop_column_family(name)
54
+ say_with_time("drop_column_family #{name}") do
55
+ column_family_tasks.drop(name)
56
+ end
57
+ end
58
+
59
+ # Renames the column family from the old name to the new name
60
+ def self.rename_column_family(old_name, new_name)
61
+ say_with_time("rename_column_family #{name}") do
62
+ column_family_tasks.rename(old_name, new_name)
63
+ end
64
+ end
65
+
66
+ def self.write(text="")
67
+ puts(text) if verbose
68
+ end
69
+
70
+ def self.announce(message)
71
+ version = defined?(@version) ? @version : nil
72
+
73
+ text = "#{version} #{name}: #{message}"
74
+ length = [0, 75 - text.length].max
75
+ write "== %s %s" % [text, "=" * length]
76
+ end
77
+
78
+ def self.say(message, subitem=false)
79
+ write "#{subitem ? " ->" : "--"} #{message}"
80
+ end
81
+
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
89
+ end
90
+
91
+ def self.suppress_messages
92
+ save, self.verbose = verbose, false
93
+ yield
94
+ ensure
95
+ self.verbose = save
96
+ end
97
+
98
+ private
99
+
100
+ def self.column_family_tasks
101
+ Tasks::ColumnFamily.new(CassandraObject::Base.connection.keyspace)
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,25 @@
1
+ module CassandraObject
2
+ module Schema
3
+
4
+ # MigrationProxy is used to defer loading of the actual migration classes
5
+ # until they are needed
6
+ class MigrationProxy
7
+
8
+ attr_accessor :name, :version, :filename
9
+
10
+ delegate :migrate, :announce, :write, :to=>:migration
11
+
12
+ private
13
+
14
+ def migration
15
+ @migration ||= load_migration
16
+ end
17
+
18
+ def load_migration
19
+ require(File.expand_path(filename))
20
+ name.constantize
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,213 @@
1
+ module CassandraObject
2
+ module Schema
3
+
4
+ class Migrator
5
+
6
+ def self.migrate(migrations_path, target_version = nil)
7
+ case
8
+ when target_version.nil?
9
+ up(migrations_path, target_version)
10
+ when current_version == 0 && target_version == 0
11
+ when current_version > target_version
12
+ down(migrations_path, target_version)
13
+ else
14
+ up(migrations_path, target_version)
15
+ end
16
+ end
17
+
18
+ def self.rollback(migrations_path, steps = 1)
19
+ move(:down, migrations_path, steps)
20
+ end
21
+
22
+ def self.forward(migrations_path, steps = 1)
23
+ move(:up, migrations_path, steps)
24
+ end
25
+
26
+ def self.up(migrations_path, target_version = nil)
27
+ new(:up, migrations_path, target_version).migrate
28
+ end
29
+
30
+ def self.down(migrations_path, target_version = nil)
31
+ new(:down, migrations_path, target_version).migrate
32
+ end
33
+
34
+ def self.run(direction, migrations_path, target_version)
35
+ new(direction, migrations_path, target_version).run
36
+ end
37
+
38
+ def self.migrations_path
39
+ 'ks/migrate'
40
+ end
41
+
42
+ def self.schema_migrations_column_family
43
+ :schema_migrations
44
+ end
45
+
46
+ def self.column_family_tasks
47
+ cas = CassandraObject::Base.connection
48
+ Tasks::ColumnFamily.new(cas.keyspace)
49
+ end
50
+
51
+ def self.get_all_versions
52
+ cas = CassandraObject::Base.connection
53
+ cas.get(schema_migrations_column_family, 'all').map {|(name, _value)| name.to_i}.sort
54
+ end
55
+
56
+ def self.current_version
57
+ sm_cf = schema_migrations_column_family
58
+ if column_family_tasks.exists?(sm_cf)
59
+ get_all_versions.max || 0
60
+ else
61
+ 0
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def self.move(direction, migrations_path, steps)
68
+ migrator = self.new(direction, migrations_path)
69
+ start_index = migrator.migrations.index(migrator.current_migration)
70
+
71
+ if start_index
72
+ finish = migrator.migrations[start_index + steps]
73
+ version = finish ? finish.version : 0
74
+ send(direction, migrations_path, version)
75
+ end
76
+ end
77
+
78
+ public
79
+
80
+ def initialize(direction, migrations_path, target_version = nil)
81
+ sm_cf = self.class.schema_migrations_column_family
82
+
83
+ unless column_family_tasks.exists?(sm_cf)
84
+ column_family_tasks.create(sm_cf) do |cf|
85
+ cf.comparator_type = 'LongType'
86
+ end
87
+ end
88
+
89
+ @direction, @migrations_path, @target_version = direction, migrations_path, target_version
90
+ end
91
+
92
+ def current_version
93
+ migrated.last || 0
94
+ end
95
+
96
+ def current_migration
97
+ migrations.detect { |m| m.version == current_version }
98
+ end
99
+
100
+ def run
101
+ target = migrations.detect { |m| m.version == @target_version }
102
+ raise UnknownMigrationVersionError.new(@target_version) if target.nil?
103
+ unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
104
+ target.migrate(@direction)
105
+ record_version_state_after_migrating(target)
106
+ end
107
+ end
108
+
109
+ def migrate
110
+ current = migrations.detect { |m| m.version == current_version }
111
+ target = migrations.detect { |m| m.version == @target_version }
112
+
113
+ if target.nil? && !@target_version.nil? && @target_version > 0
114
+ raise UnknownMigrationVersionError.new(@target_version)
115
+ end
116
+
117
+ start = up? ? 0 : (migrations.index(current) || 0)
118
+ finish = migrations.index(target) || migrations.size - 1
119
+ runnable = migrations[start..finish]
120
+
121
+ # skip the last migration if we're headed down, but not ALL the way down
122
+ runnable.pop if down? && !target.nil?
123
+
124
+ runnable.each do |migration|
125
+ #puts "Migrating to #{migration.name} (#{migration.version})"
126
+
127
+ # On our way up, we skip migrating the ones we've already migrated
128
+ next if up? && migrated.include?(migration.version.to_i)
129
+
130
+ # On our way down, we skip reverting the ones we've never migrated
131
+ if down? && !migrated.include?(migration.version.to_i)
132
+ migration.announce 'never migrated, skipping'; migration.write
133
+ next
134
+ end
135
+
136
+ migration.migrate(@direction)
137
+ record_version_state_after_migrating(migration)
138
+ end
139
+ end
140
+
141
+ def migrations
142
+ @migrations ||= begin
143
+ files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
144
+
145
+ migrations = files.inject([]) do |klasses, file|
146
+ version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
147
+
148
+ raise IllegalMigrationNameError.new(file) unless version
149
+ version = version.to_i
150
+
151
+ if klasses.detect { |m| m.version == version }
152
+ raise DuplicateMigrationVersionError.new(version)
153
+ end
154
+
155
+ if klasses.detect { |m| m.name == name.camelize }
156
+ raise DuplicateMigrationNameError.new(name.camelize)
157
+ end
158
+
159
+ migration = MigrationProxy.new
160
+ migration.name = name.camelize
161
+ migration.version = version
162
+ migration.filename = file
163
+ klasses << migration
164
+ end
165
+
166
+ migrations = migrations.sort_by { |m| m.version }
167
+ down? ? migrations.reverse : migrations
168
+ end
169
+ end
170
+
171
+ def pending_migrations
172
+ already_migrated = migrated
173
+ migrations.reject { |m| already_migrated.include?(m.version.to_i) }
174
+ end
175
+
176
+ def migrated
177
+ @migrated_versions ||= self.class.get_all_versions
178
+ end
179
+
180
+ private
181
+
182
+ def column_family_tasks
183
+ Tasks::ColumnFamily.new(connection.keyspace)
184
+ end
185
+
186
+ def connection
187
+ CassandraObject::Base.connection
188
+ end
189
+
190
+ def record_version_state_after_migrating(migration)
191
+ sm_cf = self.class.schema_migrations_column_family
192
+
193
+ @migrated_versions ||= []
194
+ if down?
195
+ @migrated_versions.delete(migration.version)
196
+ connection.remove sm_cf, 'all', migration.version
197
+ else
198
+ @migrated_versions.push(migration.version).sort!
199
+ connection.insert sm_cf, 'all', { migration.version => migration.name }
200
+ end
201
+ end
202
+
203
+ def up?
204
+ @direction == :up
205
+ end
206
+
207
+ def down?
208
+ @direction == :down
209
+ end
210
+
211
+ end
212
+ end
213
+ end
@@ -50,21 +50,21 @@ namespace :ks do
50
50
  desc 'Migrate the keyspace (options: VERSION=x)'
51
51
  task :migrate => :configure do
52
52
  version = ( ENV['VERSION'] ? ENV['VERSION'].to_i : nil )
53
- CassandraObject::Migrator.migrate CassandraObject::Migrator.migrations_path, version
53
+ CassandraObject::Schema::Migrator.migrate CassandraObject::Schema::Migrator.migrations_path, version
54
54
  schema_dump
55
55
  end
56
56
 
57
57
  desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n)'
58
58
  task :rollback => :set_keyspace do
59
59
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
60
- CassandraObject::Migrator.rollback CassandraObject::Migrator.migrations_path, step
60
+ CassandraObject::Schema::Migrator.rollback CassandraObject::Schema::Migrator.migrations_path, step
61
61
  schema_dump
62
62
  end
63
63
 
64
64
  desc 'Pushes the schema to the next version (specify steps w/ STEP=n)'
65
65
  task :forward => :set_keyspace do
66
66
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
67
- CassandraObject::Migrator.forward CassandraObject::Migrator.migrations_path, step
67
+ CassandraObject::Schema::Migrator.forward CassandraObject::Schema::Migrator.migrations_path, step
68
68
  schema_dump
69
69
  end
70
70
 
@@ -90,7 +90,7 @@ namespace :ks do
90
90
 
91
91
  desc 'Retrieves the current schema version number'
92
92
  task :version => :set_keyspace do
93
- version = CassandraObject::Migrator.current_version
93
+ version = CassandraObject::Schema::Migrator.current_version
94
94
  puts "Current version: #{version}"
95
95
  end
96
96
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gotime-cassandra_object
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.8.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,12 +10,12 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-03-09 00:00:00.000000000 -08:00
13
+ date: 2011-03-10 00:00:00.000000000 -08:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
18
- requirement: &11677480 !ruby/object:Gem::Requirement
18
+ requirement: &10878420 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ~>
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: '3'
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *11677480
26
+ version_requirements: *10878420
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
- requirement: &11676780 !ruby/object:Gem::Requirement
29
+ requirement: &10877740 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ~>
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: '3'
35
35
  type: :runtime
36
36
  prerelease: false
37
- version_requirements: *11676780
37
+ version_requirements: *10877740
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: cassandra
40
- requirement: &11676220 !ruby/object:Gem::Requirement
40
+ requirement: &10877160 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :runtime
47
47
  prerelease: false
48
- version_requirements: *11676220
48
+ version_requirements: *10877160
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: shoulda
51
- requirement: &11675520 !ruby/object:Gem::Requirement
51
+ requirement: &10876480 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ! '>='
@@ -56,10 +56,10 @@ dependencies:
56
56
  version: '0'
57
57
  type: :development
58
58
  prerelease: false
59
- version_requirements: *11675520
59
+ version_requirements: *10876480
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: bundler
62
- requirement: &11674780 !ruby/object:Gem::Requirement
62
+ requirement: &10875720 !ruby/object:Gem::Requirement
63
63
  none: false
64
64
  requirements:
65
65
  - - ~>
@@ -67,10 +67,10 @@ dependencies:
67
67
  version: 1.0.0
68
68
  type: :development
69
69
  prerelease: false
70
- version_requirements: *11674780
70
+ version_requirements: *10875720
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: jeweler
73
- requirement: &11674020 !ruby/object:Gem::Requirement
73
+ requirement: &10874980 !ruby/object:Gem::Requirement
74
74
  none: false
75
75
  requirements:
76
76
  - - ~>
@@ -78,10 +78,10 @@ dependencies:
78
78
  version: 1.5.1
79
79
  type: :development
80
80
  prerelease: false
81
- version_requirements: *11674020
81
+ version_requirements: *10874980
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: rcov
84
- requirement: &11673460 !ruby/object:Gem::Requirement
84
+ requirement: &10874400 !ruby/object:Gem::Requirement
85
85
  none: false
86
86
  requirements:
87
87
  - - ! '>='
@@ -89,7 +89,7 @@ dependencies:
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
- version_requirements: *11673460
92
+ version_requirements: *10874400
93
93
  description: Cassandra ActiveModel
94
94
  email: grantr@gmail.com
95
95
  executables: []
@@ -130,11 +130,14 @@ files:
130
130
  - lib/cassandra_object/identity/uuid_key_factory.rb
131
131
  - lib/cassandra_object/indexes.rb
132
132
  - lib/cassandra_object/log_subscriber.rb
133
- - lib/cassandra_object/migration.rb
134
133
  - lib/cassandra_object/migrations.rb
135
- - lib/cassandra_object/migrator.rb
134
+ - lib/cassandra_object/migrations/migration.rb
136
135
  - lib/cassandra_object/mocking.rb
137
136
  - lib/cassandra_object/persistence.rb
137
+ - lib/cassandra_object/schema.rb
138
+ - lib/cassandra_object/schema/migration.rb
139
+ - lib/cassandra_object/schema/migration_proxy.rb
140
+ - lib/cassandra_object/schema/migrator.rb
138
141
  - lib/cassandra_object/serialization.rb
139
142
  - lib/cassandra_object/tasks/column_family.rb
140
143
  - lib/cassandra_object/tasks/keyspace.rb
@@ -1,126 +0,0 @@
1
- module CassandraObject
2
-
3
- class Migration
4
-
5
- @@verbose = true
6
- cattr_accessor :verbose
7
-
8
- # Returns the raw connection to Cassandra
9
- def self.connection
10
- CassandraObject::Base.connection
11
- end
12
-
13
- def self.migrate(direction)
14
- return unless respond_to?(direction)
15
-
16
- case direction
17
- when :up then announce "migrating"
18
- when :down then announce "reverting"
19
- end
20
-
21
- result = nil
22
- time = Benchmark.measure { result = send("#{direction}") }
23
-
24
- case direction
25
- when :up then announce "migrated (%.4fs)" % time.real; write
26
- when :down then announce "reverted (%.4fs)" % time.real; write
27
- end
28
-
29
- result
30
- end
31
-
32
- # Creates a new column family with the given name. Column family configurations can be set within
33
- # a block like this:
34
- #
35
- # create_column_family(:users) do |cf|
36
- # cf.comment = 'Users column family'
37
- # cf.comparator_type = 'TimeUUIDType'
38
- # end
39
- #
40
- # A complete list of available configuration settings is here:
41
- #
42
- # http://github.com/fauna/cassandra/blob/master/vendor/0.7/gen-rb/cassandra_types.rb
43
- #
44
- # Scroll down to the CfDef definition.
45
- def self.create_column_family(name, &block)
46
- say_with_time("create_column_family #{name}") do
47
- column_family_tasks.create(name, &block)
48
- end
49
- end
50
-
51
- # Drops the given column family
52
- def self.drop_column_family(name)
53
- say_with_time("drop_column_family #{name}") do
54
- column_family_tasks.drop(name)
55
- end
56
- end
57
-
58
- # Renames the column family from the old name to the new name
59
- def self.rename_column_family(old_name, new_name)
60
- say_with_time("rename_column_family #{name}") do
61
- column_family_tasks.rename(old_name, new_name)
62
- end
63
- end
64
-
65
- def self.write(text="")
66
- puts(text) if verbose
67
- end
68
-
69
- def self.announce(message)
70
- version = defined?(@version) ? @version : nil
71
-
72
- text = "#{version} #{name}: #{message}"
73
- length = [0, 75 - text.length].max
74
- write "== %s %s" % [text, "=" * length]
75
- end
76
-
77
- def self.say(message, subitem=false)
78
- write "#{subitem ? " ->" : "--"} #{message}"
79
- end
80
-
81
- def self.say_with_time(message)
82
- say(message)
83
- result = nil
84
- time = Benchmark.measure { result = yield }
85
- say "%.4fs" % time.real, :subitem
86
- say("#{result} rows", :subitem) if result.is_a?(Integer)
87
- result
88
- end
89
-
90
- def self.suppress_messages
91
- save, self.verbose = verbose, false
92
- yield
93
- ensure
94
- self.verbose = save
95
- end
96
-
97
- private
98
-
99
- def self.column_family_tasks
100
- Tasks::ColumnFamily.new(CassandraObject::Base.connection.keyspace)
101
- end
102
-
103
- end
104
-
105
- # MigrationProxy is used to defer loading of the actual migration classes
106
- # until they are needed
107
- class MigrationProxy
108
-
109
- attr_accessor :name, :version, :filename
110
-
111
- delegate :migrate, :announce, :write, :to=>:migration
112
-
113
- private
114
-
115
- def migration
116
- @migration ||= load_migration
117
- end
118
-
119
- def load_migration
120
- require(File.expand_path(filename))
121
- name.constantize
122
- end
123
-
124
- end
125
-
126
- end
@@ -1,238 +0,0 @@
1
- module CassandraObject
2
- class IrreversibleMigration < StandardError
3
- end
4
-
5
- class DuplicateMigrationVersionError < StandardError#:nodoc:
6
- def initialize(version)
7
- super("Multiple migrations have the version number #{version}")
8
- end
9
- end
10
-
11
- class DuplicateMigrationNameError < StandardError#:nodoc:
12
- def initialize(name)
13
- super("Multiple migrations have the name #{name}")
14
- end
15
- end
16
-
17
- class UnknownMigrationVersionError < StandardError #:nodoc:
18
- def initialize(version)
19
- super("No migration with version number #{version}")
20
- end
21
- end
22
-
23
- class IllegalMigrationNameError < StandardError#: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
- new(:up, migrations_path, target_version).migrate
53
- end
54
-
55
- def self.down(migrations_path, target_version = nil)
56
- new(:down, migrations_path, target_version).migrate
57
- end
58
-
59
- def self.run(direction, migrations_path, target_version)
60
- 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.column_family_tasks
72
- cas = CassandraObject::Base.connection
73
- Tasks::ColumnFamily.new(cas.keyspace)
74
- end
75
-
76
- def self.get_all_versions
77
- cas = CassandraObject::Base.connection
78
- cas.get(schema_migrations_column_family, 'all').map {|(name, _value)| name.to_i}.sort
79
- end
80
-
81
- def self.current_version
82
- sm_cf = schema_migrations_column_family
83
- if column_family_tasks.exists?(sm_cf)
84
- get_all_versions.max || 0
85
- else
86
- 0
87
- end
88
- end
89
-
90
- private
91
-
92
- def self.move(direction, migrations_path, steps)
93
- migrator = self.new(direction, migrations_path)
94
- start_index = migrator.migrations.index(migrator.current_migration)
95
-
96
- if start_index
97
- finish = migrator.migrations[start_index + steps]
98
- version = finish ? finish.version : 0
99
- send(direction, migrations_path, version)
100
- end
101
- end
102
-
103
- public
104
-
105
- def initialize(direction, migrations_path, target_version = nil)
106
- sm_cf = self.class.schema_migrations_column_family
107
-
108
- unless column_family_tasks.exists?(sm_cf)
109
- column_family_tasks.create(sm_cf) do |cf|
110
- cf.comparator_type = 'LongType'
111
- end
112
- end
113
-
114
- @direction, @migrations_path, @target_version = direction, migrations_path, target_version
115
- end
116
-
117
- def current_version
118
- migrated.last || 0
119
- end
120
-
121
- def current_migration
122
- migrations.detect { |m| m.version == current_version }
123
- end
124
-
125
- def run
126
- target = migrations.detect { |m| m.version == @target_version }
127
- raise UnknownMigrationVersionError.new(@target_version) if target.nil?
128
- unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
129
- target.migrate(@direction)
130
- record_version_state_after_migrating(target)
131
- end
132
- end
133
-
134
- def migrate
135
- current = migrations.detect { |m| m.version == current_version }
136
- target = migrations.detect { |m| m.version == @target_version }
137
-
138
- if target.nil? && !@target_version.nil? && @target_version > 0
139
- raise UnknownMigrationVersionError.new(@target_version)
140
- end
141
-
142
- start = up? ? 0 : (migrations.index(current) || 0)
143
- finish = migrations.index(target) || migrations.size - 1
144
- runnable = migrations[start..finish]
145
-
146
- # skip the last migration if we're headed down, but not ALL the way down
147
- runnable.pop if down? && !target.nil?
148
-
149
- runnable.each do |migration|
150
- #puts "Migrating to #{migration.name} (#{migration.version})"
151
-
152
- # On our way up, we skip migrating the ones we've already migrated
153
- next if up? && migrated.include?(migration.version.to_i)
154
-
155
- # On our way down, we skip reverting the ones we've never migrated
156
- if down? && !migrated.include?(migration.version.to_i)
157
- migration.announce 'never migrated, skipping'; migration.write
158
- next
159
- end
160
-
161
- migration.migrate(@direction)
162
- record_version_state_after_migrating(migration)
163
- end
164
- end
165
-
166
- def migrations
167
- @migrations ||= begin
168
- files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
169
-
170
- migrations = files.inject([]) do |klasses, file|
171
- version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
172
-
173
- raise IllegalMigrationNameError.new(file) unless version
174
- version = version.to_i
175
-
176
- if klasses.detect { |m| m.version == version }
177
- raise DuplicateMigrationVersionError.new(version)
178
- end
179
-
180
- if klasses.detect { |m| m.name == name.camelize }
181
- raise DuplicateMigrationNameError.new(name.camelize)
182
- end
183
-
184
- migration = MigrationProxy.new
185
- migration.name = name.camelize
186
- migration.version = version
187
- migration.filename = file
188
- klasses << migration
189
- end
190
-
191
- migrations = migrations.sort_by { |m| m.version }
192
- down? ? migrations.reverse : migrations
193
- end
194
- end
195
-
196
- def pending_migrations
197
- already_migrated = migrated
198
- migrations.reject { |m| already_migrated.include?(m.version.to_i) }
199
- end
200
-
201
- def migrated
202
- @migrated_versions ||= self.class.get_all_versions
203
- end
204
-
205
- private
206
-
207
- def column_family_tasks
208
- Tasks::ColumnFamily.new(connection.keyspace)
209
- end
210
-
211
- def connection
212
- CassandraObject::Base.connection
213
- end
214
-
215
- def record_version_state_after_migrating(migration)
216
- sm_cf = self.class.schema_migrations_column_family
217
-
218
- @migrated_versions ||= []
219
- if down?
220
- @migrated_versions.delete(migration.version)
221
- connection.remove sm_cf, 'all', migration.version
222
- else
223
- @migrated_versions.push(migration.version).sort!
224
- connection.insert sm_cf, 'all', { migration.version => migration.name }
225
- end
226
- end
227
-
228
- def up?
229
- @direction == :up
230
- end
231
-
232
- def down?
233
- @direction == :down
234
- end
235
-
236
- end
237
-
238
- end