active_column 0.1.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,39 +9,51 @@ module ActiveColumn
9
9
  :long => 'LongType',
10
10
  :string => 'BytesType' }
11
11
 
12
- def initialize
13
- @cassandra = ActiveColumn.connection
12
+ def initialize(keyspace)
13
+ raise 'Cannot operate on system keyspace' if keyspace == 'system'
14
+ @keyspace = keyspace
14
15
  end
15
16
 
16
17
  def exists?(name)
17
- @cassandra.schema.cf_defs.find { |cf_def| cf_def.name == name.to_s }
18
+ connection.schema.cf_defs.find { |cf_def| cf_def.name == name.to_s }
18
19
  end
19
20
 
20
- def create(name, options = {})
21
- opts = { :name => name.to_s,
22
- :keyspace => @cassandra.keyspace,
23
- :comparator_type => 'TimeUUIDType' }.merge(options)
21
+ def create(name, &block)
22
+ cf = Cassandra::ColumnFamily.new
23
+ cf.name = name.to_s
24
+ cf.keyspace = @keyspace.to_s
25
+ cf.comparator_type = 'TimeUUIDType'
24
26
 
25
- cf = Cassandra::ColumnFamily.new.with_fields(opts)
26
- @cassandra.add_column_family(cf)
27
+ block.call cf if block
28
+
29
+ post_process_column_family(cf)
30
+ connection.add_column_family(cf)
27
31
  end
28
32
 
29
33
  def drop(name)
30
- @cassandra.drop_column_family(name.to_s)
34
+ connection.drop_column_family(name.to_s)
35
+ end
36
+
37
+ def rename(old_name, new_name)
38
+ connection.rename_column_family(old_name.to_s, new_name.to_s)
31
39
  end
32
40
 
33
41
  def clear(name)
34
- @cassandra.truncate!(name.to_s)
42
+ connection.truncate!(name.to_s)
35
43
  end
36
44
 
37
45
  private
38
46
 
39
- def post_process_options(options)
40
- type = options[:comparator_type]
47
+ def connection
48
+ ActiveColumn.connection
49
+ end
50
+
51
+ def post_process_column_family(cf)
52
+ type = cf.comparator_type
41
53
  if type && COMPARATOR_TYPES.has_key?(type)
42
- options[:comparator_type] = COMPARATOR_TYPES[type]
54
+ cf.comparator_type = COMPARATOR_TYPES[type]
43
55
  end
44
- options
56
+ cf
45
57
  end
46
58
 
47
59
  end
@@ -54,11 +66,10 @@ class Cassandra
54
66
  class ColumnFamily
55
67
  def with_fields(options)
56
68
  struct_fields.collect { |f| f[1][:name] }.each do |f|
57
- send("#{f}=", options[f.to_sym])
69
+ send("#{f}=", options[f.to_sym] || options[f.to_s])
58
70
  end
59
71
  self
60
72
  end
61
73
  end
62
74
  end
63
75
 
64
-
@@ -3,6 +3,16 @@ module ActiveColumn
3
3
  module Tasks
4
4
 
5
5
  class Keyspace
6
+ include ActiveColumn::Helpers
7
+
8
+ def self.parse(hash)
9
+ ks = Cassandra::Keyspace.new.with_fields hash
10
+ ks.cf_defs = []
11
+ hash['cf_defs'].each do |cf|
12
+ ks.cf_defs << Cassandra::ColumnFamily.new.with_fields(cf)
13
+ end
14
+ ks
15
+ end
6
16
 
7
17
  def initialize
8
18
  c = ActiveColumn.connection
@@ -14,6 +24,11 @@ module ActiveColumn
14
24
  end
15
25
 
16
26
  def create(name, options = {})
27
+ if exists? name
28
+ log "Keyspace '#{name}' already exists - cannot create"
29
+ return nil
30
+ end
31
+
17
32
  opts = { :name => name.to_s,
18
33
  :strategy_class => 'org.apache.cassandra.locator.LocalStrategy',
19
34
  :replication_factor => 1,
@@ -21,13 +36,18 @@ module ActiveColumn
21
36
 
22
37
  ks = Cassandra::Keyspace.new.with_fields(opts)
23
38
  @cassandra.add_keyspace ks
39
+ ks
24
40
  end
25
41
 
26
42
  def drop(name)
43
+ return log 'Cannot drop system keyspace' if name == 'system'
44
+ return log "Keyspace '#{name}' does not exist - cannot drop" if !exists? name
27
45
  @cassandra.drop_keyspace name.to_s
46
+ true
28
47
  end
29
48
 
30
49
  def set(name)
50
+ return log "Keyspace '#{name}' does not exist - cannot set" if !exists? name
31
51
  @cassandra.keyspace = name.to_s
32
52
  end
33
53
 
@@ -36,11 +56,26 @@ module ActiveColumn
36
56
  end
37
57
 
38
58
  def clear
39
- return puts 'Cannot clear system keyspace' if @cassandra.keyspace == 'system'
40
-
59
+ return log 'Cannot clear system keyspace' if @cassandra.keyspace == 'system'
41
60
  @cassandra.clear_keyspace!
42
61
  end
43
62
 
63
+ def schema_dump
64
+ @cassandra.schema
65
+ end
66
+
67
+ def schema_load(schema)
68
+ @cassandra.schema.cf_defs.each do |cf|
69
+ @cassandra.drop_column_family cf.name
70
+ end
71
+
72
+ keyspace = get
73
+ schema.cf_defs.each do |cf|
74
+ cf.keyspace = keyspace
75
+ @cassandra.add_column_family cf
76
+ end
77
+ end
78
+
44
79
  end
45
80
 
46
81
  end
@@ -51,7 +86,7 @@ class Cassandra
51
86
  class Keyspace
52
87
  def with_fields(options)
53
88
  struct_fields.collect { |f| f[1][:name] }.each do |f|
54
- send("#{f}=", options[f.to_sym])
89
+ send("#{f}=", options[f.to_sym] || options[f.to_s])
55
90
  end
56
91
  self
57
92
  end
@@ -0,0 +1,142 @@
1
+ require 'rake'
2
+ require 'active_column/tasks/keyspace'
3
+ require 'active_column/tasks/column_family'
4
+
5
+ namespace :ks do
6
+
7
+ if defined? ::Rails
8
+ task :configure => :environment do
9
+ configure
10
+ end
11
+ else
12
+ task :configure do
13
+ configure
14
+ end
15
+ end
16
+
17
+ task :set_keyspace => :configure do
18
+ set_keyspace
19
+ end
20
+
21
+ desc 'Create the keyspace in config/cassandra.yml for the current environment'
22
+ task :create => :configure do
23
+ ks = ActiveColumn::Tasks::Keyspace.new.create @config['keyspace'], @config
24
+ puts "Created keyspace: #{@config['keyspace']}" if ks
25
+ end
26
+
27
+ namespace :create do
28
+ desc 'Create keyspaces in config/cassandra.yml for all environments'
29
+ task :all => :configure do
30
+ @configs.values.each do |config|
31
+ ks = ActiveColumn::Tasks::Keyspace.new.create config['keyspace'], config
32
+ puts "Created keyspace: #{config['keyspace']}" if ks
33
+ end
34
+ end
35
+ end
36
+
37
+ desc 'Drop keyspace in config/cassandra.yml for the current environment'
38
+ task :drop => :configure do
39
+ dropped = ActiveColumn::Tasks::Keyspace.new.drop @config['keyspace']
40
+ puts "Dropped keyspace: #{@config['keyspace']}" if dropped
41
+ end
42
+
43
+ namespace :drop do
44
+ desc 'Drop keyspaces in config/cassandra.yml for all environments'
45
+ task :all => :configure do
46
+ @configs.values.each do |config|
47
+ dropped = ActiveColumn::Tasks::Keyspace.new.drop config['keyspace']
48
+ puts "Dropped keyspace: #{config['keyspace']}" if dropped
49
+ end
50
+ end
51
+ end
52
+
53
+ desc 'Migrate the keyspace (options: VERSION=x)'
54
+ task :migrate => :set_keyspace do
55
+ version = ( ENV['VERSION'] ? ENV['VERSION'].to_i : nil )
56
+ ActiveColumn::Migrator.migrate ActiveColumn::Migrator.migrations_path, version
57
+ schema_dump
58
+ end
59
+
60
+ desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n)'
61
+ task :rollback => :set_keyspace do
62
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
63
+ ActiveColumn::Migrator.rollback ActiveColumn::Migrator.migrations_path, step
64
+ schema_dump
65
+ end
66
+
67
+ desc 'Pushes the schema to the next version (specify steps w/ STEP=n)'
68
+ task :forward => :set_keyspace do
69
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
70
+ ActiveColumn::Migrator.forward ActiveColumn::Migrator.migrations_path, step
71
+ schema_dump
72
+ end
73
+
74
+ namespace :schema do
75
+ desc 'Create ks/schema.json file that can be portably used against any Cassandra instance supported by ActiveColumn'
76
+ task :dump => :configure do
77
+ schema_dump
78
+ end
79
+
80
+ desc 'Load ks/schema.json file into Cassandra'
81
+ task :load => :configure do
82
+ schema_load
83
+ end
84
+ end
85
+
86
+ namespace :test do
87
+ desc 'Load the development schema in to the test keyspace'
88
+ task :prepare => :configure do
89
+ schema_dump :development
90
+ schema_load :test
91
+ end
92
+ end
93
+
94
+ desc 'Retrieves the current schema version number'
95
+ task :version => :set_keyspace do
96
+ version = ActiveColumn::Migrator.current_version
97
+ puts "Current version: #{version}"
98
+ end
99
+
100
+ private
101
+
102
+ def current_root
103
+ return Rails.root.to_s if defined? ::Rails
104
+ '.'
105
+ end
106
+
107
+ def configure
108
+ @configs = YAML.load_file("#{current_root}/config/cassandra.yml")
109
+ @config = @configs[ActiveColumn::Helpers.current_env]
110
+ ActiveColumn.connect @config
111
+ end
112
+
113
+ def schema_dump(env = ActiveColumn::Helpers.current_env)
114
+ ks = set_keyspace env
115
+ File.open "#{current_root}/ks/schema.json", 'w' do |file|
116
+ basic_json = ks.schema_dump.to_json
117
+ formatted_json = JSON.pretty_generate(JSON.parse(basic_json))
118
+ file.puts formatted_json
119
+ end
120
+ end
121
+
122
+ def schema_load(env = ActiveColumn::Helpers.current_env)
123
+ ks = set_keyspace env
124
+ File.open "#{current_root}/ks/schema.json", 'r' do |file|
125
+ hash = JSON.parse(file.read(nil))
126
+ ks.schema_load ActiveColumn::Tasks::Keyspace.parse(hash)
127
+ end
128
+ end
129
+
130
+ def set_keyspace(env = ActiveColumn::Helpers.current_env)
131
+ config = @configs[env.to_s || 'development']
132
+ ks = ActiveColumn::Tasks::Keyspace.new
133
+ keyspace = config['keyspace']
134
+ unless ks.exists? keyspace
135
+ puts "Keyspace '#{keyspace}' does not exist - Try 'rake ks:create'"
136
+ exit 1
137
+ end
138
+ ks.set keyspace
139
+ ks
140
+ end
141
+
142
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveColumn
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2"
3
3
  end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+ require 'mocha'
3
+
4
+ describe ActiveColumn::Migration do
5
+ describe '.create_column_family' do
6
+
7
+ context 'given a block' do
8
+ before do
9
+ ActiveColumn.connection.expects(:add_column_family).with() do |cf|
10
+ cf.name == 'foo' && cf.comment = 'some comment'
11
+ end
12
+ end
13
+
14
+ it 'sends the settings to cassandra' do
15
+ ActiveColumn::Migration.create_column_family :foo do |cf|
16
+ cf.comment = 'some comment'
17
+ end
18
+ end
19
+ end
20
+
21
+ context 'given no block' do
22
+ before do
23
+ ActiveColumn.connection.expects(:add_column_family).with() do |cf|
24
+ cf.name == 'foo' && cf.comment.nil?
25
+ end
26
+ end
27
+
28
+ it 'sends the default settings to cassandra' do
29
+ ActiveColumn::Migration.create_column_family :foo
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ describe '.drop_column_family' do
36
+ context 'given a column family' do
37
+ before do
38
+ ActiveColumn.connection.expects(:drop_column_family).with('foo')
39
+ end
40
+
41
+ it 'drops it' do
42
+ ActiveColumn::Migration.drop_column_family :foo
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '.rename_column_family' do
48
+ context 'given a column family and a new name' do
49
+ before do
50
+ ActiveColumn.connection.expects(:rename_column_family).with('old_foo', 'new_foo')
51
+ end
52
+
53
+ it 'renames it' do
54
+ ActiveColumn::Migration.rename_column_family :old_foo, :new_foo
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,8 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  def translated_comparator(given)
4
- cf = ActiveColumn::Tasks::ColumnFamily.new
5
- cf.send(:post_process_options, { :comparator_type => given })[:comparator_type]
4
+ cf_tasks = ActiveColumn.column_family_tasks
5
+ cf = Cassandra::ColumnFamily.new
6
+ cf.comparator_type = given
7
+ cf_tasks.send(:post_process_column_family, cf).comparator_type
6
8
  end
7
9
 
8
10
  describe ActiveColumn::Tasks::ColumnFamily do
@@ -3,27 +3,36 @@ require 'spec_helper'
3
3
  describe ActiveColumn::Tasks::Keyspace do
4
4
 
5
5
  before do
6
- @ks = ActiveColumn::Tasks::Keyspace.new
6
+ @ks = ActiveColumn.keyspace_tasks
7
7
  end
8
8
 
9
- describe ".create" do
9
+ describe "#create" do
10
10
  context "given a keyspace" do
11
11
  before do
12
12
  @ks.drop :ks_create_test if @ks.exists?(:ks_create_test)
13
- @ks.create :ks_create_test
13
+ @keyspace = @ks.create :ks_create_test
14
+ @no_keyspace = @ks.create :ks_create_test
14
15
  end
15
16
 
16
17
  it "creates the keyspace" do
17
18
  @ks.exists?(:ks_create_test).should be
18
19
  end
19
20
 
21
+ it 'returns the keyspace' do
22
+ @keyspace.should be
23
+ end
24
+
25
+ it 'does not create duplicate keyspaces' do
26
+ @no_keyspace.should_not be
27
+ end
28
+
20
29
  after do
21
30
  @ks.drop :ks_create_test
22
31
  end
23
32
  end
24
33
  end
25
34
 
26
- describe '.drop' do
35
+ describe '#drop' do
27
36
  context 'given a keyspace' do
28
37
  before do
29
38
  @ks.create :ks_drop_test unless @ks.exists?(:ks_drop_test)
@@ -33,6 +42,97 @@ describe ActiveColumn::Tasks::Keyspace do
33
42
  it 'drops the keyspace' do
34
43
  @ks.exists?(:ks_drop_test).should_not be
35
44
  end
45
+
46
+ it 'gracefully does not drop the keyspace again' do
47
+ @ks.drop :ks_drop_test
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '.parse' do
53
+ context 'given a keyspace schema as a hash' do
54
+ before do
55
+ @hash = { 'name' => 'ks1',
56
+ 'cf_defs' => [ { 'name' => 'cf1', 'comment' => 'foo' },
57
+ { 'name' => 'cf2', 'comment' => 'bar' } ] }
58
+ @schema = ActiveColumn::Tasks::Keyspace.parse @hash
59
+ @cfdefs = @schema.cf_defs.sort { |a,b| a.name <=> b.name }
60
+ end
61
+
62
+ it 'returns a keyspace schema' do
63
+ @schema.should be_a(Cassandra::Keyspace)
64
+ @schema.name.should == 'ks1'
65
+ end
66
+
67
+ it 'returns all column families' do
68
+ @cfdefs.collect(&:name).should == [ 'cf1', 'cf2' ]
69
+ @cfdefs.collect(&:comment).should == [ 'foo', 'bar' ]
70
+ end
71
+ end
72
+ end
73
+
74
+ describe '#schema_dump' do
75
+ context 'given a keyspace' do
76
+ before do
77
+ @ks.drop :ks_schema_dump_test if @ks.exists?(:ks_schema_dump_test)
78
+ @ks.create :ks_schema_dump_test
79
+ @ks.set :ks_schema_dump_test
80
+ cf_tasks = ActiveColumn::Tasks::ColumnFamily.new :ks_schema_dump_test
81
+ cf_tasks.create(:cf1) { |cf| cf.comment = 'foo' }
82
+ cf_tasks.create(:cf2) { |cf| cf.comment = 'bar' }
83
+ @schema = @ks.schema_dump
84
+ @cfdefs = @schema.cf_defs.sort { |a,b| a.name <=> b.name }
85
+ end
86
+
87
+ it 'dumps the keyspace schema' do
88
+ @schema.should be
89
+ @schema.name.should == 'ks_schema_dump_test'
90
+ end
91
+
92
+ it 'dumps all column families' do
93
+ @cfdefs.collect(&:name).should == [ 'cf1', 'cf2' ]
94
+ @cfdefs.collect(&:comment).should == [ 'foo', 'bar' ]
95
+ end
96
+
97
+ after do
98
+ @ks.drop :ks_schema_dump_test
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#schema_load' do
104
+ context 'given a keyspace schema' do
105
+ before do
106
+ @ks.drop :ks_schema_load_test if @ks.exists?(:ks_schema_load_test)
107
+ @ks.create :ks_schema_load_test
108
+ @ks.set :ks_schema_load_test
109
+ cf_tasks = ActiveColumn::Tasks::ColumnFamily.new :ks_schema_load_test
110
+ cf_tasks.create(:cf1) { |cf| cf.comment = 'foo' }
111
+ cf_tasks.create(:cf2) { |cf| cf.comment = 'bar' }
112
+ schema = @ks.schema_dump
113
+
114
+ @ks.drop :ks_schema_load_test2 if @ks.exists?(:ks_schema_load_test2)
115
+ @ks.create :ks_schema_load_test2
116
+ @ks.set :ks_schema_load_test2
117
+ @ks.schema_load schema
118
+ @schema2 = @ks.schema_dump
119
+ @cfdefs2 = @schema2.cf_defs.sort { |a,b| a.name <=> b.name }
120
+ end
121
+
122
+ it 'loads the keyspace' do
123
+ @schema2.should be
124
+ @schema2.name.should == 'ks_schema_load_test2'
125
+ end
126
+
127
+ it 'loads all column families' do
128
+ @cfdefs2.collect(&:name).should == [ 'cf1', 'cf2' ]
129
+ @cfdefs2.collect(&:comment).should == [ 'foo', 'bar' ]
130
+ end
131
+
132
+ after do
133
+ @ks.drop :ks_schema_load_test
134
+ @ks.drop :ks_schema_load_test2
135
+ end
36
136
  end
37
137
  end
38
138
  end