active_column 0.1.1 → 0.2

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.
@@ -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