sessionm-cassandra_object 2.2.6

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.
Files changed (83) hide show
  1. data/.gitignore +2 -0
  2. data/CHANGELOG +3 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE +13 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.markdown +12 -0
  7. data/Rakefile +15 -0
  8. data/lib/cassandra_object/associations/one_to_many.rb +146 -0
  9. data/lib/cassandra_object/associations/one_to_one.rb +85 -0
  10. data/lib/cassandra_object/associations.rb +50 -0
  11. data/lib/cassandra_object/attributes.rb +97 -0
  12. data/lib/cassandra_object/base.rb +97 -0
  13. data/lib/cassandra_object/batches.rb +31 -0
  14. data/lib/cassandra_object/callbacks.rb +27 -0
  15. data/lib/cassandra_object/collection.rb +8 -0
  16. data/lib/cassandra_object/connection.rb +29 -0
  17. data/lib/cassandra_object/consistency.rb +31 -0
  18. data/lib/cassandra_object/cursor.rb +90 -0
  19. data/lib/cassandra_object/dirty.rb +32 -0
  20. data/lib/cassandra_object/errors.rb +10 -0
  21. data/lib/cassandra_object/finder_methods.rb +72 -0
  22. data/lib/cassandra_object/generators/migration_generator.rb +31 -0
  23. data/lib/cassandra_object/generators/templates/migration.rb.erb +11 -0
  24. data/lib/cassandra_object/identity/abstract_key_factory.rb +36 -0
  25. data/lib/cassandra_object/identity/custom_key_factory.rb +50 -0
  26. data/lib/cassandra_object/identity/hashed_natural_key_factory.rb +10 -0
  27. data/lib/cassandra_object/identity/key.rb +20 -0
  28. data/lib/cassandra_object/identity/natural_key_factory.rb +51 -0
  29. data/lib/cassandra_object/identity/uuid_key_factory.rb +39 -0
  30. data/lib/cassandra_object/identity.rb +52 -0
  31. data/lib/cassandra_object/log_subscriber.rb +37 -0
  32. data/lib/cassandra_object/migrations/migration.rb +15 -0
  33. data/lib/cassandra_object/migrations.rb +66 -0
  34. data/lib/cassandra_object/mocking.rb +15 -0
  35. data/lib/cassandra_object/persistence.rb +138 -0
  36. data/lib/cassandra_object/railtie.rb +11 -0
  37. data/lib/cassandra_object/schema/migration.rb +106 -0
  38. data/lib/cassandra_object/schema/migration_proxy.rb +25 -0
  39. data/lib/cassandra_object/schema/migrator.rb +213 -0
  40. data/lib/cassandra_object/schema.rb +37 -0
  41. data/lib/cassandra_object/serialization.rb +6 -0
  42. data/lib/cassandra_object/tasks/column_family.rb +90 -0
  43. data/lib/cassandra_object/tasks/keyspace.rb +89 -0
  44. data/lib/cassandra_object/tasks/ks.rake +121 -0
  45. data/lib/cassandra_object/timestamps.rb +19 -0
  46. data/lib/cassandra_object/type.rb +19 -0
  47. data/lib/cassandra_object/types/array_type.rb +16 -0
  48. data/lib/cassandra_object/types/boolean_type.rb +23 -0
  49. data/lib/cassandra_object/types/date_type.rb +20 -0
  50. data/lib/cassandra_object/types/float_type.rb +19 -0
  51. data/lib/cassandra_object/types/hash_type.rb +16 -0
  52. data/lib/cassandra_object/types/integer_type.rb +19 -0
  53. data/lib/cassandra_object/types/set_type.rb +22 -0
  54. data/lib/cassandra_object/types/string_type.rb +16 -0
  55. data/lib/cassandra_object/types/time_type.rb +27 -0
  56. data/lib/cassandra_object/types/time_with_zone_type.rb +18 -0
  57. data/lib/cassandra_object/types/utf8_string_type.rb +18 -0
  58. data/lib/cassandra_object/types.rb +11 -0
  59. data/lib/cassandra_object/validations.rb +46 -0
  60. data/lib/cassandra_object.rb +49 -0
  61. data/sessionm-cassandra_object.gemspec +26 -0
  62. data/test/active_model_test.rb +9 -0
  63. data/test/base_test.rb +28 -0
  64. data/test/batches_test.rb +30 -0
  65. data/test/connection_test.rb +28 -0
  66. data/test/consistency_test.rb +20 -0
  67. data/test/finder_methods_test.rb +49 -0
  68. data/test/identity_test.rb +30 -0
  69. data/test/persistence_test.rb +84 -0
  70. data/test/test_helper.rb +31 -0
  71. data/test/timestamps_test.rb +27 -0
  72. data/test/types/array_type_test.rb +15 -0
  73. data/test/types/boolean_type_test.rb +23 -0
  74. data/test/types/date_type_test.rb +4 -0
  75. data/test/types/float_type_test.rb +4 -0
  76. data/test/types/hash_type_test.rb +4 -0
  77. data/test/types/integer_type_test.rb +18 -0
  78. data/test/types/set_type_test.rb +17 -0
  79. data/test/types/string_type_test.rb +4 -0
  80. data/test/types/time_type_test.rb +4 -0
  81. data/test/types/utf8_string_type_test.rb +4 -0
  82. data/test/validations_test.rb +15 -0
  83. metadata +183 -0
@@ -0,0 +1,138 @@
1
+ module CassandraObject
2
+ module Persistence
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def remove(key)
7
+ ActiveSupport::Notifications.instrument("remove.cassandra_object", column_family: column_family, key: key) do
8
+ connection.remove(column_family, key.to_s, consistency: thrift_write_consistency)
9
+ end
10
+ end
11
+
12
+ def delete_all
13
+ ActiveSupport::Notifications.instrument("truncate.cassandra_object", column_family: column_family) do
14
+ connection.truncate!(column_family)
15
+ end
16
+ end
17
+
18
+ def create(attributes = {})
19
+ new(attributes).tap do |object|
20
+ object.save
21
+ end
22
+ end
23
+
24
+ def write(key, attributes, schema_version)
25
+ key.tap do |key|
26
+ attributes = encode_columns_hash(attributes, schema_version)
27
+ ActiveSupport::Notifications.instrument("insert.cassandra_object", column_family: column_family, key: key, attributes: attributes) do
28
+ connection.insert(column_family, key.to_s, attributes, consistency: thrift_write_consistency)
29
+ end
30
+ end
31
+ end
32
+
33
+ def instantiate(key, attributes)
34
+ # remove any attributes we don't know about. we would do this earlier, but we want to make such
35
+ # attributes available to migrations
36
+ attributes.delete_if { |k,_| model_attributes[k].nil? }
37
+
38
+ allocate.tap do |object|
39
+ object.instance_variable_set("@schema_version", attributes.delete('schema_version'))
40
+ object.instance_variable_set("@key", parse_key(key))
41
+ object.instance_variable_set("@new_record", false)
42
+ object.instance_variable_set("@destroyed", false)
43
+ object.instance_variable_set("@attributes", decode_columns_hash(attributes))
44
+ end
45
+ end
46
+
47
+ def encode_columns_hash(attributes, schema_version)
48
+ attributes.inject({}) do |memo, (column_name, value)|
49
+ # cassandra stores bytes, not strings, so it has no concept of encodings. The ruby thrift gem
50
+ # expects all strings to be encoded as ascii-8bit.
51
+ # don't attempt to encode columns that are nil
52
+ memo[column_name.to_s] = value.nil? ? '' : model_attributes[column_name].converter.encode(value).force_encoding('ASCII-8BIT')
53
+ memo
54
+ end.merge({"schema_version" => schema_version.to_s})
55
+ end
56
+
57
+ def decode_columns_hash(attributes)
58
+ Hash[attributes.map { |k, v| [k.to_s, model_attributes[k].converter.decode(v)] }]
59
+ end
60
+
61
+ def column_family_configuration
62
+ [{:Name => column_family, :CompareWith => "UTF8Type"}]
63
+ end
64
+ end
65
+
66
+ def new_record?
67
+ @new_record
68
+ end
69
+
70
+ def destroyed?
71
+ @destroyed
72
+ end
73
+
74
+ def persisted?
75
+ !(new_record? || destroyed?)
76
+ end
77
+
78
+ def save(*)
79
+ begin
80
+ create_or_update
81
+ rescue CassandraObject::RecordInvalid
82
+ false
83
+ end
84
+ end
85
+
86
+ def save!
87
+ create_or_update || raise(RecordNotSaved)
88
+ end
89
+
90
+ def destroy
91
+ self.class.remove(key)
92
+ @destroyed = true
93
+ freeze
94
+ end
95
+
96
+ def update_attribute(name, value)
97
+ name = name.to_s
98
+ send("#{name}=", value)
99
+ save(:validate => false)
100
+ end
101
+
102
+ def update_attributes(attributes)
103
+ self.attributes = attributes
104
+ save
105
+ end
106
+
107
+ def update_attributes!(attributes)
108
+ self.attributes = attributes
109
+ save!
110
+ end
111
+
112
+ def reload
113
+ @attributes.update(self.class.find(self.id).instance_variable_get('@attributes'))
114
+ end
115
+
116
+ private
117
+ def create_or_update
118
+ result = new_record? ? create : update
119
+ result != false
120
+ end
121
+
122
+ def create
123
+ @key ||= self.class.next_key(self)
124
+ write
125
+ @new_record = false
126
+ @key
127
+ end
128
+
129
+ def update
130
+ write
131
+ end
132
+
133
+ def write
134
+ changed_attributes = changed.inject({}) { |h, n| h[n] = read_attribute(n); h }
135
+ self.class.write(key, changed_attributes, schema_version)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,11 @@
1
+ module CassandraObject
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load 'cassandra_object/tasks/ks.rake'
5
+ end
6
+
7
+ generators do
8
+ require 'cassandra_object/generators/migration_generator'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,106 @@
1
+ module CassandraObject
2
+ module Schema
3
+
4
+ class Migration
5
+
6
+ @@verbose = true
7
+ cattr_accessor :verbose
8
+
9
+ # Returns the raw connection to Cassandra
10
+ def self.connection
11
+ CassandraObject::Base.connection
12
+ end
13
+
14
+ def self.migrate(direction)
15
+ return unless respond_to?(direction)
16
+
17
+ case direction
18
+ when :up then announce "migrating"
19
+ when :down then announce "reverting"
20
+ end
21
+
22
+ result = nil
23
+ time = Benchmark.measure { result = send("#{direction}") }
24
+
25
+ case direction
26
+ when :up then announce "migrated (%.4fs)" % time.real; write
27
+ when :down then announce "reverted (%.4fs)" % time.real; write
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ # Creates a new column family with the given name. Column family configurations can be set within
34
+ # a block like this:
35
+ #
36
+ # create_column_family(:users) do |cf|
37
+ # cf.comment = 'Users column family'
38
+ # cf.comparator_type = 'TimeUUIDType'
39
+ # end
40
+ #
41
+ # A complete list of available configuration settings is here:
42
+ #
43
+ # http://github.com/fauna/cassandra/blob/master/vendor/0.7/gen-rb/cassandra_types.rb
44
+ #
45
+ # Scroll down to the CfDef definition.
46
+ def self.create_column_family(name, &block)
47
+ say_with_time("create_column_family #{name}") do
48
+ column_family_tasks.create(name, &block)
49
+ end
50
+ end
51
+
52
+ # Drops the given column family
53
+ def self.drop_column_family(name)
54
+ say_with_time("drop_column_family #{name}") do
55
+ column_family_tasks.drop(name)
56
+ end
57
+ end
58
+
59
+ # Renames the column family from the old name to the new name
60
+ def self.rename_column_family(old_name, new_name)
61
+ say_with_time("rename_column_family #{name}") do
62
+ column_family_tasks.rename(old_name, new_name)
63
+ end
64
+ end
65
+
66
+ def self.write(text="")
67
+ puts(text) if verbose
68
+ end
69
+
70
+ def self.announce(message)
71
+ version = defined?(@version) ? @version : nil
72
+
73
+ text = "#{version} #{name}: #{message}"
74
+ length = [0, 75 - text.length].max
75
+ write "== %s %s" % [text, "=" * length]
76
+ end
77
+
78
+ def self.say(message, subitem=false)
79
+ write "#{subitem ? " ->" : "--"} #{message}"
80
+ end
81
+
82
+ def self.say_with_time(message)
83
+ say(message)
84
+ result = nil
85
+ time = Benchmark.measure { result = yield }
86
+ say "%.4fs" % time.real, :subitem
87
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
88
+ result
89
+ end
90
+
91
+ def self.suppress_messages
92
+ save, self.verbose = verbose, false
93
+ yield
94
+ ensure
95
+ self.verbose = save
96
+ end
97
+
98
+ private
99
+
100
+ def self.column_family_tasks
101
+ Tasks::ColumnFamily.new(CassandraObject::Base.connection.keyspace)
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,25 @@
1
+ module CassandraObject
2
+ module Schema
3
+
4
+ # MigrationProxy is used to defer loading of the actual migration classes
5
+ # until they are needed
6
+ class MigrationProxy
7
+
8
+ attr_accessor :name, :version, :filename
9
+
10
+ delegate :migrate, :announce, :write, :to=>:migration
11
+
12
+ private
13
+
14
+ def migration
15
+ @migration ||= load_migration
16
+ end
17
+
18
+ def load_migration
19
+ require(File.expand_path(filename))
20
+ name.constantize
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,213 @@
1
+ module CassandraObject
2
+ module Schema
3
+
4
+ class Migrator
5
+
6
+ def self.migrate(migrations_path, target_version = nil)
7
+ case
8
+ when target_version.nil?
9
+ up(migrations_path, target_version)
10
+ when current_version == 0 && target_version == 0
11
+ when current_version > target_version
12
+ down(migrations_path, target_version)
13
+ else
14
+ up(migrations_path, target_version)
15
+ end
16
+ end
17
+
18
+ def self.rollback(migrations_path, steps = 1)
19
+ move(:down, migrations_path, steps)
20
+ end
21
+
22
+ def self.forward(migrations_path, steps = 1)
23
+ move(:up, migrations_path, steps)
24
+ end
25
+
26
+ def self.up(migrations_path, target_version = nil)
27
+ new(:up, migrations_path, target_version).migrate
28
+ end
29
+
30
+ def self.down(migrations_path, target_version = nil)
31
+ new(:down, migrations_path, target_version).migrate
32
+ end
33
+
34
+ def self.run(direction, migrations_path, target_version)
35
+ new(direction, migrations_path, target_version).run
36
+ end
37
+
38
+ def self.migrations_path
39
+ 'ks/migrate'
40
+ end
41
+
42
+ def self.schema_migrations_column_family
43
+ :schema_migrations
44
+ end
45
+
46
+ def self.column_family_tasks
47
+ cas = CassandraObject::Base.connection
48
+ Tasks::ColumnFamily.new(cas.keyspace)
49
+ end
50
+
51
+ def self.get_all_versions
52
+ cas = CassandraObject::Base.connection
53
+ cas.get(schema_migrations_column_family, 'all').map {|(name, _value)| name.to_i}.sort
54
+ end
55
+
56
+ def self.current_version
57
+ sm_cf = schema_migrations_column_family
58
+ if column_family_tasks.exists?(sm_cf)
59
+ get_all_versions.max || 0
60
+ else
61
+ 0
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def self.move(direction, migrations_path, steps)
68
+ migrator = self.new(direction, migrations_path)
69
+ start_index = migrator.migrations.index(migrator.current_migration)
70
+
71
+ if start_index
72
+ finish = migrator.migrations[start_index + steps]
73
+ version = finish ? finish.version : 0
74
+ send(direction, migrations_path, version)
75
+ end
76
+ end
77
+
78
+ public
79
+
80
+ def initialize(direction, migrations_path, target_version = nil)
81
+ sm_cf = self.class.schema_migrations_column_family
82
+
83
+ unless column_family_tasks.exists?(sm_cf)
84
+ column_family_tasks.create(sm_cf) do |cf|
85
+ cf.comparator_type = 'LongType'
86
+ end
87
+ end
88
+
89
+ @direction, @migrations_path, @target_version = direction, migrations_path, target_version
90
+ end
91
+
92
+ def current_version
93
+ migrated.last || 0
94
+ end
95
+
96
+ def current_migration
97
+ migrations.detect { |m| m.version == current_version }
98
+ end
99
+
100
+ def run
101
+ target = migrations.detect { |m| m.version == @target_version }
102
+ raise UnknownMigrationVersionError.new(@target_version) if target.nil?
103
+ unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
104
+ target.migrate(@direction)
105
+ record_version_state_after_migrating(target)
106
+ end
107
+ end
108
+
109
+ def migrate
110
+ current = migrations.detect { |m| m.version == current_version }
111
+ target = migrations.detect { |m| m.version == @target_version }
112
+
113
+ if target.nil? && !@target_version.nil? && @target_version > 0
114
+ raise UnknownMigrationVersionError.new(@target_version)
115
+ end
116
+
117
+ start = up? ? 0 : (migrations.index(current) || 0)
118
+ finish = migrations.index(target) || migrations.size - 1
119
+ runnable = migrations[start..finish]
120
+
121
+ # skip the last migration if we're headed down, but not ALL the way down
122
+ runnable.pop if down? && !target.nil?
123
+
124
+ runnable.each do |migration|
125
+ #puts "Migrating to #{migration.name} (#{migration.version})"
126
+
127
+ # On our way up, we skip migrating the ones we've already migrated
128
+ next if up? && migrated.include?(migration.version.to_i)
129
+
130
+ # On our way down, we skip reverting the ones we've never migrated
131
+ if down? && !migrated.include?(migration.version.to_i)
132
+ migration.announce 'never migrated, skipping'; migration.write
133
+ next
134
+ end
135
+
136
+ migration.migrate(@direction)
137
+ record_version_state_after_migrating(migration)
138
+ end
139
+ end
140
+
141
+ def migrations
142
+ @migrations ||= begin
143
+ files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
144
+
145
+ migrations = files.inject([]) do |klasses, file|
146
+ version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
147
+
148
+ raise IllegalMigrationNameError.new(file) unless version
149
+ version = version.to_i
150
+
151
+ if klasses.detect { |m| m.version == version }
152
+ raise DuplicateMigrationVersionError.new(version)
153
+ end
154
+
155
+ if klasses.detect { |m| m.name == name.camelize }
156
+ raise DuplicateMigrationNameError.new(name.camelize)
157
+ end
158
+
159
+ migration = MigrationProxy.new
160
+ migration.name = name.camelize
161
+ migration.version = version
162
+ migration.filename = file
163
+ klasses << migration
164
+ end
165
+
166
+ migrations = migrations.sort_by { |m| m.version }
167
+ down? ? migrations.reverse : migrations
168
+ end
169
+ end
170
+
171
+ def pending_migrations
172
+ already_migrated = migrated
173
+ migrations.reject { |m| already_migrated.include?(m.version.to_i) }
174
+ end
175
+
176
+ def migrated
177
+ @migrated_versions ||= self.class.get_all_versions
178
+ end
179
+
180
+ private
181
+
182
+ def column_family_tasks
183
+ Tasks::ColumnFamily.new(connection.keyspace)
184
+ end
185
+
186
+ def connection
187
+ CassandraObject::Base.connection
188
+ end
189
+
190
+ def record_version_state_after_migrating(migration)
191
+ sm_cf = self.class.schema_migrations_column_family
192
+
193
+ @migrated_versions ||= []
194
+ if down?
195
+ @migrated_versions.delete(migration.version)
196
+ connection.remove sm_cf, 'all', migration.version
197
+ else
198
+ @migrated_versions.push(migration.version).sort!
199
+ connection.insert sm_cf, 'all', { migration.version => migration.name }
200
+ end
201
+ end
202
+
203
+ def up?
204
+ @direction == :up
205
+ end
206
+
207
+ def down?
208
+ @direction == :down
209
+ end
210
+
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,37 @@
1
+ module CassandraObject
2
+ module Schema
3
+ extend ActiveSupport::Autoload
4
+
5
+ class IrreversibleMigration < StandardError
6
+ end
7
+
8
+ class DuplicateMigrationVersionError < StandardError#:nodoc:
9
+ def initialize(version)
10
+ super("Multiple migrations have the version number #{version}")
11
+ end
12
+ end
13
+
14
+ class DuplicateMigrationNameError < StandardError#:nodoc:
15
+ def initialize(name)
16
+ super("Multiple migrations have the name #{name}")
17
+ end
18
+ end
19
+
20
+ class UnknownMigrationVersionError < StandardError #:nodoc:
21
+ def initialize(version)
22
+ super("No migration with version number #{version}")
23
+ end
24
+ end
25
+
26
+ class IllegalMigrationNameError < StandardError#:nodoc:
27
+ def initialize(name)
28
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
29
+ end
30
+ end
31
+
32
+ autoload :Migrator
33
+ autoload :Migration
34
+ autoload :MigrationProxy
35
+
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ module CassandraObject
2
+ module Serialization
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Serializers::JSON
5
+ end
6
+ end
@@ -0,0 +1,90 @@
1
+ module CassandraObject
2
+
3
+ module Tasks
4
+
5
+ class ColumnFamily
6
+
7
+ COMPARATOR_TYPES = { :time => 'TimeUUIDType',
8
+ :timestamp => 'TimeUUIDType',
9
+ :long => 'LongType',
10
+ :string => 'BytesType',
11
+ :utf8 => 'UTF8Type' }
12
+
13
+ COLUMN_TYPES = { :standard => 'Standard',
14
+ :super => 'Super' }
15
+
16
+ def initialize(keyspace)
17
+ @keyspace = keyspace
18
+ end
19
+
20
+ def exists?(name)
21
+ connection.schema.cf_defs.find { |cf_def| cf_def.name == name.to_s }
22
+ end
23
+
24
+ def create(name, &block)
25
+ cf = Cassandra::ColumnFamily.new
26
+ cf.name = name.to_s
27
+ cf.keyspace = @keyspace.to_s
28
+ cf.comparator_type = 'BytesType'
29
+ cf.column_type = 'Standard'
30
+
31
+ block.call cf if block
32
+
33
+ post_process_column_family(cf)
34
+ connection.add_column_family(cf)
35
+ end
36
+
37
+ def drop(name)
38
+ connection.drop_column_family(name.to_s)
39
+ end
40
+
41
+ def rename(old_name, new_name)
42
+ connection.rename_column_family(old_name.to_s, new_name.to_s)
43
+ end
44
+
45
+ def clear(name)
46
+ connection.truncate!(name.to_s)
47
+ end
48
+
49
+ private
50
+
51
+ def connection
52
+ CassandraObject::Base.connection
53
+ end
54
+
55
+ def post_process_column_family(cf)
56
+ comp_type = cf.comparator_type
57
+ if comp_type && COMPARATOR_TYPES.has_key?(comp_type)
58
+ cf.comparator_type = COMPARATOR_TYPES[comp_type]
59
+ end
60
+
61
+ comp_type = cf.subcomparator_type
62
+ if comp_type && COMPARATOR_TYPES.has_key?(comp_type)
63
+ cf.subcomparator_type = COMPARATOR_TYPES[comp_type]
64
+ end
65
+
66
+ col_type = cf.column_type
67
+ if col_type && COLUMN_TYPES.has_key?(col_type)
68
+ cf.column_type = COLUMN_TYPES[col_type]
69
+ end
70
+
71
+ cf
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ class Cassandra
81
+ class ColumnFamily
82
+ def with_fields(options)
83
+ struct_fields.collect { |f| f[1][:name] }.each do |f|
84
+ send("#{f}=", options[f.to_sym] || options[f.to_s])
85
+ end
86
+ self
87
+ end
88
+ end
89
+ end
90
+