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.
- data/gotime-cassandra_object.gemspec +1 -1
- data/lib/cassandra_object.rb +1 -2
- data/lib/cassandra_object/generators/templates/migration.rb.erb +1 -1
- data/lib/cassandra_object/migrations.rb +4 -12
- data/lib/cassandra_object/migrations/migration.rb +15 -0
- data/lib/cassandra_object/schema.rb +37 -0
- data/lib/cassandra_object/schema/migration.rb +106 -0
- data/lib/cassandra_object/schema/migration_proxy.rb +25 -0
- data/lib/cassandra_object/schema/migrator.rb +213 -0
- data/lib/cassandra_object/tasks/ks.rb +4 -4
- metadata +21 -18
- data/lib/cassandra_object/migration.rb +0 -126
- data/lib/cassandra_object/migrator.rb +0 -238
data/lib/cassandra_object.rb
CHANGED
@@ -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
|
-
|
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,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.
|
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-
|
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: &
|
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: *
|
26
|
+
version_requirements: *10878420
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
|
-
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: *
|
37
|
+
version_requirements: *10877740
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: cassandra
|
40
|
-
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: *
|
48
|
+
version_requirements: *10877160
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: shoulda
|
51
|
-
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: *
|
59
|
+
version_requirements: *10876480
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: bundler
|
62
|
-
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: *
|
70
|
+
version_requirements: *10875720
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: jeweler
|
73
|
-
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: *
|
81
|
+
version_requirements: *10874980
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
83
|
name: rcov
|
84
|
-
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: *
|
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/
|
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
|