cassandra_migrations 0.0.1.pre4 → 0.0.1.pre5

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,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'fileutils'
5
+ require 'colorize'
6
+
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: #{File.basename($0)} [path]"
9
+
10
+ opts.on("-h", "--help", "Displays this help info") do
11
+ puts opts
12
+ exit 0
13
+ end
14
+
15
+ begin
16
+ opts.parse!(ARGV)
17
+ rescue OptionParser::ParseError => e
18
+ warn e.message
19
+ puts opts
20
+ exit 1
21
+ end
22
+ end
23
+
24
+ if ARGV.empty?
25
+ abort "Please specify the directory of a rails appilcation, e.g. `#{File.basename($0)} .'"
26
+ elsif !File.directory?(ARGV.first)
27
+ abort "`#{ARGV.first}' is not a directory."
28
+ elsif ARGV.length > 1
29
+ abort "Too many arguments; please specify only the directory of the rails application."
30
+ end
31
+
32
+ rails_root = ARGV.first
33
+
34
+ # create cassandra.yaml
35
+ if File.exists?(File.expand_path('config/cassandra.yml', rails_root))
36
+ puts "[skip] 'config/cassandra.yml' already exists".yellow
37
+ else
38
+ puts "[new] creating 'config/cassandra.yml' (please update with your own configurations!)".green
39
+ FileUtils.cp(
40
+ File.expand_path('../template/cassandra.yml', File.dirname(__FILE__)),
41
+ File.expand_path('config/cassandra.yml', rails_root)
42
+ )
43
+ end
44
+
45
+ # create db/cassandra_migrations
46
+ if File.exists?(File.expand_path('db/cassandra_migrate', rails_root))
47
+ puts "[skip] 'db/cassandra_migrate' already exists".yellow
48
+ else
49
+ puts "[new] creating 'db/cassandra_migrate' directory".green
50
+ FileUtils.mkdir(File.expand_path('db/cassandra_migrate', rails_root))
51
+ end
52
+
53
+ puts '[done] prepared for cassandra!'.green
54
+ puts ''
55
+ puts 'Your steps from here are:'.green
56
+ puts ' 1. configure '.green + 'config/cassandra.yml'.red
57
+ puts ' 2. run '.green + 'rake cassandra:setup'.red + ' and try starting your application'.green
58
+ puts ' 3. create your first migration with '.green + 'rails g cassandra_migration'.red
59
+ puts ' 4. apply your migration with '.green + 'rake cassandra:migrate'.red
60
+ puts ' 5. run '.green + 'rake cassandra:test:prepare'.red + 'and start testing'.green
61
+ puts ' 6. have lots of fun!'.green.blink
62
+
@@ -2,5 +2,6 @@ require 'cassandra_migrations/config'
2
2
  require 'cassandra_migrations/errors'
3
3
  require 'cassandra_migrations/cassandra'
4
4
  require 'cassandra_migrations/migrator'
5
+ require 'cassandra_migrations/migration'
5
6
 
6
7
  require 'cassandra_migrations/railtie' if defined?(Rails)
@@ -5,7 +5,6 @@ require 'cql'
5
5
  require 'cassandra_migrations/cassandra/queries'
6
6
  require 'cassandra_migrations/cassandra/keyspace_operations'
7
7
 
8
-
9
8
  module CassandraMigrations
10
9
  module Cassandra
11
10
  extend Queries
@@ -10,7 +10,14 @@ module CassandraMigrations
10
10
 
11
11
  hash.each do |k,v|
12
12
  columns << k.to_s
13
- values << (v.is_a?(String) ? "'#{v.to_s}'" : v.to_s)
13
+
14
+ if v.respond_to?(:strftime)
15
+ values << "'#{v.strftime('%Y-%m-%d %H:%M:%S%z')}'"
16
+ elsif v.is_a?(String)
17
+ values << "'#{v}'"
18
+ else
19
+ values << v.to_s
20
+ end
14
21
  end
15
22
 
16
23
  execute("INSERT INTO #{table} (#{columns.join(', ')}) VALUES (#{values.join(', ')})")
@@ -38,5 +38,11 @@ module CassandraMigrations
38
38
  end
39
39
  end
40
40
 
41
+ class MigrationDefinitionError < CassandraError
42
+ def initialize(msg)
43
+ super(msg)
44
+ end
45
+ end
46
+
41
47
  end
42
48
  end
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+
3
+ require 'cassandra_migrations/migration/table_operations'
4
+ require 'cassandra_migrations/migration/column_operations'
5
+
6
+ module CassandraMigrations
7
+
8
+ # Base class for all cassandra migration
9
+ class Migration
10
+
11
+ include TableOperations
12
+ include ColumnOperations
13
+
14
+ # Makes +execute+ method directly available to migrations
15
+ delegate :execute, :to => Cassandra
16
+
17
+ # Makes +up+ work if the method in the migration is defined with self.up
18
+ def up
19
+ return unless self.class.respond_to?(:up)
20
+ self.class.instance_to_delegate = self
21
+ self.class.up
22
+ end
23
+
24
+ # Makes +down+ work if the method in the migration is defined with self.down
25
+ def down
26
+ return unless self.class.respond_to?(:down)
27
+ self.class.instance_to_delegate = self
28
+ self.class.down
29
+ end
30
+
31
+ # Class variable that holds an instance of Migration when the methods +up+ or
32
+ # +down+ are called on the class. The class then delegates missing method
33
+ # calls to this instance.
34
+ cattr_accessor :instance_to_delegate, :instance_accessor => false
35
+
36
+ # Delegate missing method calls to an instance. That's what enables the
37
+ # writing of migrations using both +def up+ and +def self.up+ sintax.
38
+ def self.method_missing(name, *args, &block)
39
+ if instance_to_delegate
40
+ instance_to_delegate.send(name, *args, &block)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ # Execute this migration in the named direction.
47
+ #
48
+ # The advantage of using this instead of directly calling up or down is that
49
+ # this method gives informative output and benchmarks the time taken.
50
+ def migrate(direction)
51
+ return unless respond_to?(direction)
52
+
53
+ case direction
54
+ when :up then announce_migration "migrating"
55
+ when :down then announce_migration "reverting"
56
+ end
57
+
58
+ time = Benchmark.measure { send(direction) }
59
+
60
+ case direction
61
+ when :up then announce_migration "migrated (%.4fs)" % time.real; puts
62
+ when :down then announce_migration "reverted (%.4fs)" % time.real; puts
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # Generates output labeled with name of migration and a line that goes up
69
+ # to 75 characters long in the terminal
70
+ def announce_migration(message)
71
+ text = "#{name}: #{message}"
72
+ length = [0, 75 - text.length].max
73
+ puts "== %s %s" % [text, "=" * length]
74
+ end
75
+
76
+ def announce_operation(message)
77
+ puts " " + message
78
+ end
79
+
80
+ def announce_suboperation(message)
81
+ puts " -> " + message
82
+ end
83
+
84
+ # Gets the name of the migration
85
+ def name
86
+ self.class.name
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'cassandra_migrations/migration/table_definition'
4
+
5
+ module CassandraMigrations
6
+ class Migration
7
+
8
+ # Module grouping methods used in migrations to make table operations like:
9
+ # - adding/removing columns
10
+ # - changing column types
11
+ # - renaming columns
12
+ module ColumnOperations
13
+
14
+ # Adds a column to a table.
15
+ #
16
+ # options: same options you would pass to create a table with that column
17
+ # (i.e. :limit might be applicable)
18
+
19
+ def add_column(table_name, column_name, type, options = {})
20
+ table_definition = TableDefinition.new
21
+
22
+ if !table_definition.respond_to?(type)
23
+ raise Errors::MigrationDefinitionError("Type '#{type}' is not valid for cassandra migration.")
24
+ end
25
+
26
+ table_definition.send(type, column_name, options)
27
+
28
+ announce_operation "add_column(#{column_name}, #{type})"
29
+
30
+ cql = "ALTER TABLE #{table_name} ADD "
31
+ cql << table_definition.to_add_column_cql
32
+ announce_suboperation cql
33
+
34
+ execute cql
35
+ end
36
+
37
+ # Removes a column from the table
38
+ def remove_column(table_name, column_name)
39
+ announce_operation "drop_table(#{table_name})"
40
+
41
+ cql = "ALTER TABLE #{table_name} DROP #{column_name}"
42
+ announce_suboperation cql
43
+
44
+ execute cql
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,111 @@
1
+ # encoding: utf-8
2
+
3
+ module CassandraMigrations
4
+ class Migration
5
+
6
+ # Used to define a table in a migration of table creation or to
7
+ # add columns to an existing table.
8
+ #
9
+ # An instance of this class is passed to the block of the method
10
+ # +create_table+, available on every migration.
11
+ #
12
+ # This class is also internally used in the method +add_column+.
13
+
14
+ class TableDefinition
15
+
16
+ def initialize()
17
+ @columns_name_type_hash = {}
18
+ @primary_keys = []
19
+ end
20
+
21
+ def to_create_cql
22
+ cql = []
23
+
24
+ if !@columns_name_type_hash.empty?
25
+ @columns_name_type_hash.each do |column_name, type|
26
+ cql << "#{column_name} #{type}"
27
+ end
28
+ else
29
+ raise Errors::MigrationDefinitionError('No columns defined for table.')
30
+ end
31
+
32
+ if !@primary_keys.empty?
33
+ cql << "PRIMARY KEY(#{@primary_keys.join(', ')})"
34
+ else
35
+ raise Errors::MigrationDefinitionError('No primary key defined.')
36
+ end
37
+
38
+ cql.join(', ')
39
+ end
40
+
41
+ def to_add_column_cql
42
+ cql = ""
43
+
44
+ if @columns_name_type_hash.size == 1
45
+ cql = "#{@columns_name_type_hash.keys.first} #{@columns_name_type_hash.values.first}"
46
+ elsif @columns_name_type_hash.empty?
47
+ raise Errors::MigrationDefinitionError('No column to add.')
48
+ else
49
+ raise Errors::MigrationDefinitionError('Only one column ca be added at once.')
50
+ end
51
+
52
+ cql
53
+ end
54
+
55
+ def boolean(column_name, options={})
56
+ @columns_name_type_hash[column_name.to_sym] = :boolean
57
+ define_primary_keys(column_name) if options[:primary_key]
58
+ end
59
+
60
+ def integer(column_name, options={})
61
+ if options[:limit].nil? || options[:limit] == 4
62
+ @columns_name_type_hash[column_name.to_sym] = :int
63
+ elsif options[:limit] == 8
64
+ @columns_name_type_hash[column_name.to_sym] = :bigint
65
+ else
66
+ raise Errors::MigrationDefinitionError(':limit option should be 4 or 8 for integers.')
67
+ end
68
+ define_primary_keys(column_name) if options[:primary_key]
69
+ end
70
+
71
+ def float(column_name, options={})
72
+ if options[:limit].nil? || options[:limit] == 4
73
+ @columns_name_type_hash[column_name.to_sym] = :float
74
+ elsif options[:limit] == 8
75
+ @columns_name_type_hash[column_name.to_sym] = :double
76
+ else
77
+ raise Errors::MigrationDefinitionError(':limit option should be 4 or 8 for floats.')
78
+ end
79
+ define_primary_keys(column_name) if options[:primary_key]
80
+ end
81
+
82
+ def string(column_name, options={})
83
+ @columns_name_type_hash[column_name.to_sym] = :varchar
84
+ define_primary_keys(column_name) if options[:primary_key]
85
+ end
86
+
87
+ def text(column_name, options={})
88
+ @columns_name_type_hash[column_name.to_sym] = :text
89
+ define_primary_keys(column_name) if options[:primary_key]
90
+ end
91
+
92
+ def datetime(column_name, options={})
93
+ @columns_name_type_hash[column_name.to_sym] = :timestamp
94
+ define_primary_keys(column_name) if options[:primary_key]
95
+ end
96
+
97
+ def timestamp(column_name, options={})
98
+ @columns_name_type_hash[column_name.to_sym] = :timestamp
99
+ define_primary_keys(column_name) if options[:primary_key]
100
+ end
101
+
102
+ def define_primary_keys(*keys)
103
+ if !@primary_keys.empty?
104
+ raise Errors::MigrationDefinitionError('Primary key defined twice for the same table.')
105
+ end
106
+
107
+ @primary_keys = keys.flatten
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require 'cassandra_migrations/migration/table_definition'
4
+
5
+ module CassandraMigrations
6
+ class Migration
7
+
8
+ # Module grouping methods used in migrations to make table operations like:
9
+ # - creating tables
10
+ # - dropping tables
11
+ module TableOperations
12
+
13
+ # Creates a new table in the keyspace
14
+ #
15
+ # options:
16
+ # - :primary_keys: single value or array (for compound primary keys). If
17
+ # not defined, some column must be chosen as primary key in the table definition.
18
+
19
+ def create_table(table_name, options = {})
20
+ table_definition = TableDefinition.new
21
+ table_definition.define_primary_keys(options[:primary_keys]) if options[:primary_keys]
22
+
23
+ yield table_definition if block_given?
24
+
25
+ announce_operation "create_table(#{table_name})"
26
+
27
+ create_cql = "CREATE TABLE #{table_name} ("
28
+ create_cql << table_definition.to_create_cql
29
+ create_cql << ")"
30
+
31
+ announce_suboperation create_cql
32
+
33
+ execute create_cql
34
+ end
35
+
36
+ # Drops a table
37
+ def drop_table(table_name)
38
+ announce_operation "drop_table(#{table_name})"
39
+ drop_cql = "DROP TABLE #{table_name}"
40
+ announce_suboperation drop_cql
41
+
42
+ execute drop_cql
43
+ end
44
+ end
45
+ end
46
+ end
@@ -31,7 +31,7 @@ module CassandraMigrations
31
31
  if !executed_migrations.empty?
32
32
  count.times do |i|
33
33
  if executed_migrations[i]
34
- down(executed_migrations[i], executed_migrations[i])
34
+ down(executed_migrations[i], executed_migrations[i+1])
35
35
  down_count += 1
36
36
  end
37
37
  end
@@ -56,7 +56,7 @@ private
56
56
  # load migration
57
57
  require migration_name
58
58
  # run migration
59
- get_class_from_migration_name(migration_name).up
59
+ get_class_from_migration_name(migration_name).new.migrate(:up)
60
60
 
61
61
  # update version
62
62
  Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => get_version_from_migration_name(migration_name).to_s})
@@ -66,8 +66,8 @@ private
66
66
  # load migration
67
67
  require migration_name
68
68
  # run migration
69
- get_class_from_migration_name(migration_name).down
70
-
69
+ get_class_from_migration_name(migration_name).new.migrate(:down)
70
+
71
71
  # downgrade version
72
72
  if previous_migration_name
73
73
  Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => get_version_from_migration_name(previous_migration_name).to_s})
@@ -13,4 +13,10 @@ class CassandraMigrations::Railtie < ::Rails::Railtie
13
13
  end
14
14
  end
15
15
 
16
+ generators do
17
+ Dir[File.expand_path("railtie/**/*_generator.rb", File.dirname(__FILE__))].each do |file|
18
+ require file
19
+ end
20
+ end
21
+
16
22
  end
@@ -0,0 +1,23 @@
1
+ Description:
2
+ Stubs out a new cassandra migration.
3
+
4
+ Pass the migration name, either CamelCased or under_scored. A migration
5
+ class is generated in db/cassandra_migrate, prefixed by a timestamp of the
6
+ current date and time
7
+
8
+ Example:
9
+ rails generate migration create_tweets
10
+
11
+ This will create:
12
+ db/cassandra_migrate/20080514090912_create_tweets.rb
13
+
14
+ Inside this file we'll have a migration ready for completion:
15
+ class CreateTweets << CassandraMigrations::Migration
16
+ def up
17
+
18
+ end
19
+
20
+ def down
21
+
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ class CassandraMigrationGenerator < Rails::Generators::Base
2
+ source_root File.expand_path('templates', File.dirname(__FILE__))
3
+
4
+ argument :migration_name, :type => :string
5
+
6
+ # Interpolates template and creates migration in the application
7
+ #
8
+ # Any public method in the generator is run automatically when
9
+ # the generator is run. To understand fully see
10
+ # http://asciicasts.com/episodes/218-making-generators-in-rails-3
11
+
12
+ def generate_migration
13
+ file_name = "#{Time.current.utc.strftime('%Y%m%d%H%M%S')}_#{migration_name.underscore}"
14
+ @migration_class_name = migration_name.camelize
15
+
16
+ template "empty_migration.rb.erb", "db/cassandra_migrate/#{file_name}.rb"
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ class <%= @migration_class_name %> < CassandraMigrations::Migration
2
+ def up
3
+
4
+ end
5
+
6
+ def down
7
+
8
+ end
9
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cassandra_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre4
4
+ version: 0.0.1.pre5
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -59,6 +59,22 @@ dependencies:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: '3.2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: colorize
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '0.5'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '0.5'
62
78
  - !ruby/object:Gem::Dependency
63
79
  name: rspec
64
80
  requirement: !ruby/object:Gem::Requirement
@@ -94,22 +110,31 @@ dependencies:
94
110
  description: A gem to manage Cassandra database schema for Rails. This gem offers
95
111
  migrations and environment specific databases out-of-the-box for Rails users.
96
112
  email: guberthenrique@hotmail.com
97
- executables: []
113
+ executables:
114
+ - prepare_for_cassandra
98
115
  extensions: []
99
116
  extra_rdoc_files: []
100
117
  files:
101
118
  - lib/cassandra_migrations.rb
119
+ - lib/cassandra_migrations/migration.rb
102
120
  - lib/cassandra_migrations/migrator.rb
103
121
  - lib/cassandra_migrations/cassandra.rb
104
122
  - lib/cassandra_migrations/railtie.rb
105
123
  - lib/cassandra_migrations/cassandra/keyspace_operations.rb
106
124
  - lib/cassandra_migrations/cassandra/queries.rb
107
125
  - lib/cassandra_migrations/config.rb
126
+ - lib/cassandra_migrations/migration/table_operations.rb
127
+ - lib/cassandra_migrations/migration/column_operations.rb
128
+ - lib/cassandra_migrations/migration/table_definition.rb
108
129
  - lib/cassandra_migrations/railtie/initializer.rb
130
+ - lib/cassandra_migrations/railtie/generators/cassandra_migration/cassandra_migration_generator.rb
131
+ - lib/cassandra_migrations/railtie/generators/cassandra_migration/templates/empty_migration.rb.erb
132
+ - lib/cassandra_migrations/railtie/generators/cassandra_migration/USAGE
109
133
  - lib/cassandra_migrations/railtie/tasks.rake
110
134
  - lib/cassandra_migrations/errors.rb
111
135
  - spec/cassandra_migrations_spec.rb
112
136
  - spec/cassandra_migrations/cassandra_spec.rb
137
+ - bin/prepare_for_cassandra
113
138
  homepage: https://github.com/hsgubert/cassandra_migrations
114
139
  licenses:
115
140
  - MIT