cassandra_migrations 0.0.1.pre0

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.
@@ -0,0 +1,5 @@
1
+
2
+ module CassandraMigrations
3
+ end
4
+
5
+ require 'cassandra_migrations/railtie' if defined?(Rails)
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+ require 'cql'
5
+ require 'cassandra_migrations/cassandra/query'
6
+ require 'cassandra_migrations/cassandra/errors'
7
+ require 'cassandra_migrations/cassandra/migrator'
8
+
9
+ module CassandraMigrations::Cassandra
10
+ extend Query
11
+
12
+ mattr_accessor :client
13
+ mattr_accessor :config
14
+
15
+ def self.start!
16
+ connect_to_server unless client
17
+
18
+ # setup keyspace use
19
+ begin
20
+ use(config['keyspace'])
21
+ rescue Cql::QueryError # keyspace does not exist
22
+ raise Errors::UnexistingKeyspaceError, config['keyspace']
23
+ end
24
+ end
25
+
26
+ def self.shutdown!
27
+ if client
28
+ client.shutdown!
29
+ self.client = nil
30
+ end
31
+
32
+ self.config = nil if config
33
+ end
34
+
35
+ def self.create_keyspace!
36
+ connect_to_server unless client
37
+
38
+ begin
39
+ execute(
40
+ "CREATE KEYSPACE #{config['keyspace']} \
41
+ WITH replication = { \
42
+ 'class':'#{config['replication']['class']}', \
43
+ 'replication_factor': #{config['replication']['replication_factor']} \
44
+ }"
45
+ )
46
+ use(config['keyspace'])
47
+ execute("CREATE TABLE metadata (data_name varchar PRIMARY KEY, data_value varchar)")
48
+ write("metadata", {:data_name => 'version', :data_value => '0'})
49
+ rescue Exception => exception
50
+ drop!
51
+ raise exception
52
+ end
53
+ end
54
+
55
+ def self.drop!
56
+ connect_to_server unless client
57
+
58
+ begin
59
+ execute("DROP KEYSPACE #{config['keyspace']}")
60
+ rescue Cql::QueryError
61
+ raise Errors::UnexistingKeyspaceError, config['keyspace']
62
+ end
63
+ end
64
+
65
+ def self.use(keyspace)
66
+ raise Errors::ClientNotStartedError unless client
67
+ client.use(keyspace)
68
+ end
69
+
70
+ def self.execute(cql)
71
+ raise Errors::ClientNotStartedError unless client
72
+ client.execute(cql)
73
+ end
74
+
75
+ private
76
+
77
+ def self.connect_to_server
78
+ load_config
79
+
80
+ begin
81
+ self.client = Cql::Client.new(:host => config['host'], :port => config['port'])
82
+ client.start!
83
+ rescue Cql::Io::ConnectionError => e
84
+ raise Errors::ConnectionError, e.message
85
+ end
86
+ end
87
+
88
+ def self.load_config
89
+ begin
90
+ self.config = YAML.load_file(Rails.root.join("config", "cassandra.yml"))[Rails.env]
91
+ rescue Errno::ENOENT
92
+ raise Errors::MissingConfigurationError
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ module CassandraMigrations::Cassandra
4
+ module Errors
5
+
6
+ class CassandraError < StandardError
7
+ end
8
+
9
+ class ClientNotStartedError < CassandraError
10
+ def initialize
11
+ super("Cassandra.start has not been called yet! Can't execute queries before connecting to server...")
12
+ end
13
+ end
14
+
15
+ class MissingConfigurationError < CassandraError
16
+ def initialize
17
+ super("config/cassandra.yml is missing use this as example: \n\
18
+ development: \n\
19
+ host: '127.0.0.1' \n\
20
+ port: 9042 \n\
21
+ keyspace: 'lme_smart_grid_server_development' \n\
22
+ replication: \n\
23
+ class: 'SimpleStrategy' \n\
24
+ replication_factor: 1
25
+ ")
26
+ end
27
+ end
28
+
29
+ class UnexistingKeyspaceError < CassandraError
30
+ def initialize(keyspace)
31
+ super("Keyspace #{keyspace} does not exist. Run rake cassandra:create.")
32
+ end
33
+ end
34
+
35
+ class ConnectionError < CassandraError
36
+ def initialize(msg)
37
+ super(msg)
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+
3
+ module CassandraMigrations::Cassandra
4
+ module Migrator
5
+
6
+ def self.up_to_latest
7
+ current_version = read_current_version
8
+
9
+ new_migrations = get_all_migration_names.sort.select do |migration_name|
10
+ get_version_from_migration_name(migration_name) > current_version
11
+ end
12
+
13
+ if !new_migrations.empty?
14
+ new_migrations.each { |migration| up(migration) }
15
+ end
16
+
17
+ new_migrations.size
18
+ end
19
+
20
+ def self.rollback(count=1)
21
+ current_version = read_current_version
22
+
23
+ executed_migrations = get_all_migration_names.sort.reverse.select do |migration_name|
24
+ get_version_from_migration_name(migration_name) <= current_version
25
+ end
26
+
27
+ down_count = 0
28
+
29
+ if !executed_migrations.empty?
30
+ count.times do |i|
31
+ if executed_migrations[i]
32
+ down(executed_migrations[i], executed_migrations[i])
33
+ down_count += 1
34
+ end
35
+ end
36
+ end
37
+
38
+ down_count
39
+ end
40
+
41
+ def self.read_current_version
42
+ CassandraMigrations::Cassandra.select("metadata", :selection => "data_name='version'", :projection => 'data_value').first['data_value'].to_i
43
+ end
44
+
45
+ private
46
+
47
+ def self.up(migration_name)
48
+ # load migration
49
+ require migration_name
50
+ # run migration
51
+ get_class_from_migration_name(migration_name).up
52
+
53
+ # update version
54
+ CassandraMigrations::Cassandra.write("metadata", {:data_name => 'version', :data_value => get_version_from_migration_name(migration_name).to_s})
55
+ end
56
+
57
+ def self.down(migration_name, previous_migration_name=nil)
58
+ # load migration
59
+ require migration_name
60
+ # run migration
61
+ get_class_from_migration_name(migration_name).down
62
+
63
+ # downgrade version
64
+ if previous_migration_name
65
+ CassandraMigrations::Cassandra.write("metadata", {:data_name => 'version', :data_value => get_version_from_migration_name(previous_migration_name).to_s})
66
+ else
67
+ CassandraMigrations::Cassandra.write("metadata", {:data_name => 'version', :data_value => '0'})
68
+ end
69
+ end
70
+
71
+ def self.get_all_migration_names
72
+ Dir[Rails.root.join("db", "cassandra_migrate/[0-9]*_*.rb")]
73
+ end
74
+
75
+ def self.get_class_from_migration_name(filename)
76
+ filename.match(/[0-9]{14}_(.+)\.rb$/).captures.first.camelize.constantize
77
+ end
78
+
79
+ def self.get_version_from_migration_name(migration_name)
80
+ migration_name.match(/([0-9]{14})_.+\.rb$/).captures.first.to_i
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ module CassandraMigrations::Cassandra
4
+ module Query
5
+
6
+ def write(table, hash)
7
+ columns = []
8
+ values = []
9
+
10
+ hash.each do |k,v|
11
+ columns << k.to_s
12
+ values << "'#{v.to_s}'"
13
+ end
14
+
15
+ execute("INSERT INTO #{table} (#{columns.join(', ')}) VALUES (#{values.join(', ')})")
16
+ end
17
+
18
+ def select(table, options={})
19
+ query_string = "SELECT #{options[:projection] || '*'} FROM #{table}"
20
+
21
+ if options[:selection]
22
+ query_string << " WHERE #{options[:selection]}"
23
+ end
24
+
25
+ if options[:order_by]
26
+ query_string << " ORDER BY #{options[:order_by]}"
27
+ end
28
+
29
+ if options[:limit]
30
+ query_string << " LIMIT #{options[:limit]}"
31
+ end
32
+
33
+ execute(query_string)
34
+ end
35
+
36
+ def delete(table, selection, options={})
37
+ execute("DELETE #{options[:projection]} FROM #{table} WHERE #{selection}")
38
+ end
39
+
40
+ def truncate(table)
41
+ execute("TRUNCATE #{table}")
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ # encoding : utf-8
2
+
3
+ require 'cassandra_migrations/cassandra'
4
+
5
+ class CassandraMigrations::Railtie < ::Rails::Railtie
6
+
7
+ initializer "cassandra_migrations.start" do
8
+ CassandraMigrations::Cassandra.start!
9
+ end
10
+
11
+ rake_tasks do
12
+ Dir[File.expand_path("tasks/**/*.rake", File.dirname(__FILE__))].each do |file|
13
+ load file
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,74 @@
1
+ # encoding : utf-8
2
+
3
+ namespace :cassandra do
4
+
5
+ task :start do
6
+ CassandraMigrations::Cassandra.start!
7
+ end
8
+
9
+ desc 'Create the keyspace in config/cassandra.yml for the current environment'
10
+ task :create do
11
+ begin
12
+ CassandraMigrations::Cassandra.start!
13
+ puts "Keyspace #{CassandraMigrations::Cassandra.config['keyspace']} already exists!"
14
+ rescue CassandraMigrations::Cassandra::Errors::UnexistingKeyspaceError
15
+ CassandraMigrations::Cassandra.create_keyspace!
16
+ puts "Created keyspace #{CassandraMigrations::Cassandra.config['keyspace']}"
17
+ end
18
+ end
19
+
20
+ desc 'Drop keyspace in config/cassandra.yml for the current environment'
21
+ task :drop do
22
+ begin
23
+ CassandraMigrations::Cassandra.drop!
24
+ puts "Dropped keyspace #{CassandraMigrations::Cassandra.config['keyspace']}"
25
+ rescue CassandraMigrations::Cassandra::Errors::UnexistingKeyspaceError
26
+ puts "Keyspace #{CassandraMigrations::Cassandra.config['keyspace']} does not exist already... cannot be dropped"
27
+ end
28
+ end
29
+
30
+ desc 'Migrate the keyspace to the latest version'
31
+ task :migrate => :start do
32
+ migrations_up_count = CassandraMigrations::Cassandra::Migrator.up_to_latest
33
+
34
+ if migrations_up_count == 0
35
+ puts "Already up-to-date"
36
+ else
37
+ puts "Migrated #{migrations_up_count} version(s) up."
38
+ end
39
+ end
40
+
41
+ desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n)'
42
+ task :rollback => :start do
43
+ steps = ENV['STEP'] ? ENV['STEP'].to_i : 1
44
+
45
+ migrations_down_count = CassandraMigrations::Cassandra::Migrator.rollback(steps)
46
+
47
+ if steps == migrations_down_count
48
+ puts "Rolled back #{steps} version(s)."
49
+ else
50
+ puts "Asked to rollback #{steps} version(s). Only achieved #{migrations_down_count}."
51
+ end
52
+ end
53
+
54
+ desc 'Resets and prepares cassandra database (all data will be lost)'
55
+ task :setup do
56
+ Rake::Task['cassandra:drop'].execute
57
+ Rake::Task['cassandra:create'].execute
58
+ Rake::Task['cassandra:migrate'].execute
59
+ end
60
+
61
+ namespace :test do
62
+ desc 'Load the development schema in to the test keyspace'
63
+ task :prepare do
64
+ Rails.env = 'test'
65
+ Rake::Task['cassandra:setup'].execute
66
+ end
67
+ end
68
+
69
+ desc 'Retrieves the current schema version number'
70
+ task :version => :start do
71
+ puts "Current version: #{Cassandra::Migrator.read_current_version}"
72
+ end
73
+
74
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cassandra_migrations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre0
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Henrique Gubert
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cql-rb
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0.pre3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0.pre3
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '10'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '10'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: debugger
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: A gem to manage Cassandra database schema for Rails. This gem offers
95
+ migrations and environment specific databases out-of-the-box for Rails users.
96
+ email: guberthenrique@hotmail.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - lib/cassandra_migrations/cassandra.rb
102
+ - lib/cassandra_migrations/cassandra/errors.rb
103
+ - lib/cassandra_migrations/cassandra/migrator.rb
104
+ - lib/cassandra_migrations/cassandra/query.rb
105
+ - lib/cassandra_migrations/tasks/cassandra.rake
106
+ - lib/cassandra_migrations/railtie.rb
107
+ - lib/cassandra_migrations.rb
108
+ homepage: https://github.com/hsgubert/cassandra_migrations
109
+ licenses: []
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: 1.8.0
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 1.8.24
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Cassandra schema management for a multi-environment developer.
132
+ test_files: []