cassandra_migrations 0.0.1.pre0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []