cequel 0.5.6 → 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cequel.rb +5 -8
  3. data/lib/cequel/errors.rb +1 -0
  4. data/lib/cequel/metal.rb +17 -0
  5. data/lib/cequel/metal/batch.rb +62 -0
  6. data/lib/cequel/metal/cql_row_specification.rb +26 -0
  7. data/lib/cequel/metal/data_set.rb +461 -0
  8. data/lib/cequel/metal/deleter.rb +47 -0
  9. data/lib/cequel/metal/incrementer.rb +35 -0
  10. data/lib/cequel/metal/inserter.rb +53 -0
  11. data/lib/cequel/metal/keyspace.rb +213 -0
  12. data/lib/cequel/metal/row.rb +48 -0
  13. data/lib/cequel/metal/row_specification.rb +37 -0
  14. data/lib/cequel/metal/statement.rb +30 -0
  15. data/lib/cequel/metal/updater.rb +65 -0
  16. data/lib/cequel/metal/writer.rb +73 -0
  17. data/lib/cequel/model.rb +12 -84
  18. data/lib/cequel/model/association_collection.rb +23 -0
  19. data/lib/cequel/model/associations.rb +84 -80
  20. data/lib/cequel/model/base.rb +74 -0
  21. data/lib/cequel/model/belongs_to_association.rb +31 -0
  22. data/lib/cequel/model/callbacks.rb +14 -10
  23. data/lib/cequel/model/collection.rb +255 -0
  24. data/lib/cequel/model/errors.rb +6 -6
  25. data/lib/cequel/model/has_many_association.rb +26 -0
  26. data/lib/cequel/model/mass_assignment.rb +31 -0
  27. data/lib/cequel/model/persistence.rb +119 -115
  28. data/lib/cequel/model/properties.rb +89 -87
  29. data/lib/cequel/model/railtie.rb +21 -14
  30. data/lib/cequel/model/record_set.rb +285 -0
  31. data/lib/cequel/model/schema.rb +33 -0
  32. data/lib/cequel/model/scoped.rb +5 -48
  33. data/lib/cequel/model/validations.rb +18 -18
  34. data/lib/cequel/schema.rb +15 -0
  35. data/lib/cequel/schema/column.rb +135 -0
  36. data/lib/cequel/schema/create_table_dsl.rb +56 -0
  37. data/lib/cequel/schema/keyspace.rb +50 -0
  38. data/lib/cequel/schema/table.rb +120 -0
  39. data/lib/cequel/schema/table_property.rb +67 -0
  40. data/lib/cequel/schema/table_reader.rb +139 -0
  41. data/lib/cequel/schema/table_synchronizer.rb +114 -0
  42. data/lib/cequel/schema/table_updater.rb +83 -0
  43. data/lib/cequel/schema/table_writer.rb +80 -0
  44. data/lib/cequel/schema/update_table_dsl.rb +60 -0
  45. data/lib/cequel/type.rb +232 -0
  46. data/lib/cequel/version.rb +1 -1
  47. data/spec/environment.rb +5 -1
  48. data/spec/examples/metal/data_set_spec.rb +608 -0
  49. data/spec/examples/model/associations_spec.rb +84 -74
  50. data/spec/examples/model/callbacks_spec.rb +66 -59
  51. data/spec/examples/model/list_spec.rb +393 -0
  52. data/spec/examples/model/map_spec.rb +229 -0
  53. data/spec/examples/model/mass_assignment_spec.rb +55 -0
  54. data/spec/examples/model/naming_spec.rb +11 -4
  55. data/spec/examples/model/persistence_spec.rb +140 -150
  56. data/spec/examples/model/properties_spec.rb +122 -75
  57. data/spec/examples/model/record_set_spec.rb +285 -0
  58. data/spec/examples/model/schema_spec.rb +44 -0
  59. data/spec/examples/model/serialization_spec.rb +20 -14
  60. data/spec/examples/model/set_spec.rb +133 -0
  61. data/spec/examples/model/spec_helper.rb +0 -10
  62. data/spec/examples/model/validations_spec.rb +51 -38
  63. data/spec/examples/schema/table_reader_spec.rb +328 -0
  64. data/spec/examples/schema/table_synchronizer_spec.rb +172 -0
  65. data/spec/examples/schema/table_updater_spec.rb +157 -0
  66. data/spec/examples/schema/table_writer_spec.rb +225 -0
  67. data/spec/examples/spec_helper.rb +29 -0
  68. data/spec/examples/type_spec.rb +204 -0
  69. data/spec/support/helpers.rb +67 -8
  70. metadata +121 -152
  71. data/lib/cequel/batch.rb +0 -58
  72. data/lib/cequel/cql_row_specification.rb +0 -22
  73. data/lib/cequel/data_set.rb +0 -371
  74. data/lib/cequel/keyspace.rb +0 -205
  75. data/lib/cequel/model/class_internals.rb +0 -49
  76. data/lib/cequel/model/column.rb +0 -20
  77. data/lib/cequel/model/counter.rb +0 -35
  78. data/lib/cequel/model/dictionary.rb +0 -126
  79. data/lib/cequel/model/dirty.rb +0 -53
  80. data/lib/cequel/model/dynamic.rb +0 -31
  81. data/lib/cequel/model/inheritable.rb +0 -48
  82. data/lib/cequel/model/instance_internals.rb +0 -23
  83. data/lib/cequel/model/local_association.rb +0 -42
  84. data/lib/cequel/model/magic.rb +0 -79
  85. data/lib/cequel/model/mass_assignment_security.rb +0 -21
  86. data/lib/cequel/model/naming.rb +0 -17
  87. data/lib/cequel/model/observer.rb +0 -42
  88. data/lib/cequel/model/readable_dictionary.rb +0 -182
  89. data/lib/cequel/model/remote_association.rb +0 -40
  90. data/lib/cequel/model/scope.rb +0 -362
  91. data/lib/cequel/model/subclass_internals.rb +0 -45
  92. data/lib/cequel/model/timestamps.rb +0 -52
  93. data/lib/cequel/model/translation.rb +0 -17
  94. data/lib/cequel/row_specification.rb +0 -63
  95. data/lib/cequel/statement.rb +0 -23
  96. data/spec/examples/data_set_spec.rb +0 -444
  97. data/spec/examples/keyspace_spec.rb +0 -84
  98. data/spec/examples/model/counter_spec.rb +0 -94
  99. data/spec/examples/model/dictionary_spec.rb +0 -301
  100. data/spec/examples/model/dirty_spec.rb +0 -39
  101. data/spec/examples/model/dynamic_spec.rb +0 -41
  102. data/spec/examples/model/inheritable_spec.rb +0 -45
  103. data/spec/examples/model/magic_spec.rb +0 -199
  104. data/spec/examples/model/mass_assignment_security_spec.rb +0 -13
  105. data/spec/examples/model/observer_spec.rb +0 -86
  106. data/spec/examples/model/scope_spec.rb +0 -677
  107. data/spec/examples/model/timestamps_spec.rb +0 -52
  108. data/spec/examples/model/translation_spec.rb +0 -23
@@ -0,0 +1,47 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ class Deleter < Writer
6
+
7
+ def delete_row
8
+ @delete_row = true
9
+ end
10
+
11
+ def delete_columns(*columns)
12
+ statements.concat(columns)
13
+ end
14
+
15
+ def list_remove_at(column, *positions)
16
+ statements.concat(positions.map { |position| "#{column}[#{position}]" })
17
+ end
18
+
19
+ def map_remove(column, *keys)
20
+ statements.concat(keys.length.times.map { "#{column}[?]" })
21
+ bind_vars.concat(keys)
22
+ end
23
+
24
+ private
25
+
26
+ def write_to_statement(statement)
27
+ if @delete_row
28
+ statement.append("DELETE FROM #{table_name}")
29
+ elsif statements.empty?
30
+ raise ArgumentError, "No targets given for deletion!"
31
+ else
32
+ statement.append("DELETE ").
33
+ append(statements.join(','), *bind_vars).
34
+ append(" FROM #{table_name}")
35
+ end
36
+ statement.append(generate_upsert_options)
37
+ end
38
+
39
+ def empty?
40
+ super && !@delete_row
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,35 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ class Incrementer < Writer
6
+
7
+ def increment(data)
8
+ data.each_pair do |column_name, delta|
9
+ operator = delta < 0 ? '-' : '+'
10
+ statements << "#{column_name} = #{column_name} #{operator} ?"
11
+ bind_vars << delta.abs
12
+ end
13
+ end
14
+
15
+ def decrement(data)
16
+ increment(Hash[data.map { |column, count| [column, -count] }])
17
+ end
18
+
19
+ private
20
+
21
+ def write_to_statement(statement)
22
+ statement.
23
+ append("UPDATE #{table_name}").
24
+ append(generate_upsert_options).
25
+ append(
26
+ " SET " << statements.join(', '),
27
+ *bind_vars
28
+ )
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,53 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ class Inserter < Writer
6
+
7
+ def initialize(data_set, options = {})
8
+ @row = {}
9
+ super
10
+ end
11
+
12
+ def execute
13
+ statement = Statement.new
14
+ write_to_statement(statement)
15
+ data_set.write(*statement.args)
16
+ end
17
+
18
+ def insert(data)
19
+ @row.merge!(data.symbolize_keys)
20
+ end
21
+
22
+ private
23
+ attr_reader :row
24
+
25
+ def column_names
26
+ row.keys
27
+ end
28
+
29
+ def statements
30
+ [].tap do |statements|
31
+ row.each_pair do |column_name, value|
32
+ column_names << column_name
33
+ prepare_upsert_value(value) do |statement, *values|
34
+ statements << statement
35
+ bind_vars.concat(values)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def write_to_statement(statement)
42
+ statement.append("INSERT INTO #{table_name}")
43
+ statement.append(
44
+ " (#{column_names.join(', ')}) VALUES (#{statements.join(', ')}) ",
45
+ *bind_vars)
46
+ statement.append(generate_upsert_options)
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,213 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ #
6
+ # Handle to a Cassandra keyspace.
7
+ #
8
+ class Keyspace
9
+
10
+ #
11
+ # @api private
12
+ # @see Cequel.connect
13
+ #
14
+ def initialize(configuration={})
15
+ configure(configuration)
16
+ end
17
+
18
+ def name
19
+ @keyspace
20
+ end
21
+
22
+ def connection=(connection)
23
+ @connection = connection
24
+ end
25
+
26
+ def configure(configuration = {})
27
+ @configuration = configuration
28
+ @hosts = configuration[:host] || configuration[:hosts]
29
+ @thrift_options = configuration[:thrift].try(:symbolize_keys) || {}
30
+ @keyspace = configuration[:keyspace]
31
+ # reset the connections
32
+ clear_active_connections!
33
+ end
34
+
35
+ def schema
36
+ Schema::Keyspace.new(self)
37
+ end
38
+
39
+ def logger=(logger)
40
+ @logger = logger
41
+ end
42
+
43
+ def logger
44
+ @logger
45
+ end
46
+
47
+ def slowlog=(slowlog)
48
+ @slowlog = slowlog
49
+ end
50
+
51
+ def slowlog
52
+ @slowlog
53
+ end
54
+
55
+ def slowlog_threshold=(slowlog_threshold)
56
+ @slowlog_threshold = slowlog_threshold
57
+ end
58
+
59
+ def slowlog_threshold
60
+ @slowlog_threshold
61
+ end
62
+
63
+ def connection_pool
64
+ return @connection_pool if defined? @connection_pool
65
+ if @configuration[:pool]
66
+ options = {
67
+ :size => @configuration[:pool] || 10,
68
+ :timeout => @configuration[:pool_timeout] || 5
69
+ }
70
+ @connection_pool = ConnectionPool.new(options) do
71
+ build_connection
72
+ end
73
+ else
74
+ @connection_pool = nil
75
+ end
76
+ end
77
+
78
+ def connection
79
+ @connection ||= build_connection
80
+ end
81
+
82
+ def clear_active_connections!
83
+ remove_instance_variable(:@connection) if defined? @connection
84
+ remove_instance_variable(:@connection_pool) if defined? @connection_pool
85
+ end
86
+
87
+ def with_connection(&block)
88
+ if connection_pool
89
+ connection_pool.with(&block)
90
+ else
91
+ yield connection
92
+ end
93
+ end
94
+
95
+ #
96
+ # Get DataSet encapsulating a column family in this keyspace
97
+ #
98
+ # @param column_family_name [Symbol] the name of the column family
99
+ # @return [DataSet] a column family
100
+ #
101
+ def [](column_family_name)
102
+ DataSet.new(column_family_name.to_sym, self)
103
+ end
104
+
105
+ #
106
+ # Execute a CQL query in this keyspace.
107
+ #
108
+ # @param statement [String] CQL string
109
+ # @param *bind_vars [Object] values for bind variables
110
+ #
111
+ def execute(statement, *bind_vars)
112
+ log('CQL', statement, *bind_vars) do
113
+ with_connection do |conn|
114
+ conn.execute(statement, *bind_vars)
115
+ end
116
+ end
117
+ end
118
+
119
+ #
120
+ # Write data to this keyspace using a CQL query. Will be included the
121
+ # current batch operation if one is present.
122
+ #
123
+ # @param (see #execute)
124
+ #
125
+ def write(statement, *bind_vars)
126
+ if get_batch
127
+ get_batch.execute(statement, *bind_vars)
128
+ else
129
+ execute(statement, *bind_vars)
130
+ end
131
+ end
132
+
133
+ #
134
+ # Execute write operations in a batch. Any inserts, updates, and deletes
135
+ # inside this method's block will be executed inside a CQL BATCH operation.
136
+ #
137
+ # @param options [Hash]
138
+ # @option options [Fixnum] :auto_apply Automatically send batch to Cassandra after this many statements
139
+ #
140
+ # @example Perform inserts in a batch
141
+ # DB.batch do
142
+ # DB[:posts].insert(:id => 1, :title => 'One')
143
+ # DB[:posts].insert(:id => 2, :title => 'Two')
144
+ # end
145
+ #
146
+ def batch(options = {})
147
+ old_batch = get_batch
148
+ new_batch = Batch.new(self, options)
149
+ set_batch(new_batch)
150
+ yield
151
+ new_batch.apply
152
+ ensure
153
+ set_batch(old_batch)
154
+ end
155
+
156
+ private
157
+
158
+ def build_connection
159
+ options = {:cql_version => '3.0.0'}
160
+ options[:keyspace] = @keyspace if @keyspace
161
+ CassandraCQL::Database.new(
162
+ @hosts,
163
+ options,
164
+ @thrift_options
165
+ )
166
+ end
167
+
168
+ def get_batch
169
+ ::Thread.current[batch_key]
170
+ end
171
+
172
+ def set_batch(batch)
173
+ ::Thread.current[batch_key] = batch
174
+ end
175
+
176
+ def batch_key
177
+ :"cequel-batch-#{object_id}"
178
+ end
179
+
180
+ def log(label, statement, *bind_vars)
181
+ return yield unless logger || slowlog
182
+ response = nil
183
+ begin
184
+ time = Benchmark.ms { response = yield }
185
+ rescue Exception => e
186
+ generate_message = proc do
187
+ sprintf(
188
+ '%s (ERROR) %s', label,
189
+ CassandraCQL::Statement.sanitize(statement, bind_vars)
190
+ )
191
+ end
192
+ logger.debug(&generate_message) if self.logger
193
+ raise
194
+ end
195
+ generate_message = proc do
196
+ sprintf(
197
+ '%s (%dms) %s', label, time.to_i,
198
+ CassandraCQL::Statement.sanitize(statement, bind_vars)
199
+ )
200
+ end
201
+ logger.debug(&generate_message) if self.logger
202
+ threshold = self.slowlog_threshold || 2000
203
+ if slowlog && time >= threshold
204
+ slowlog.warn(&generate_message)
205
+ end
206
+ response
207
+ end
208
+
209
+ end
210
+
211
+ end
212
+
213
+ end
@@ -0,0 +1,48 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ class Row < ActiveSupport::HashWithIndifferentAccess
6
+
7
+ def self.from_result_row(result_row)
8
+ if result_row
9
+ new.tap do |row|
10
+ result_row.column_names.zip(result_row.column_values) do |name, value|
11
+ if name =~ /^(ttl|writetime)\((.+)\)$/
12
+ if $1 == 'ttl' then row.set_ttl($2, value)
13
+ else row.set_writetime($2, value)
14
+ end
15
+ else row[name] = value
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def initialize(*_)
23
+ super
24
+ @ttls = ActiveSupport::HashWithIndifferentAccess.new
25
+ @writetimes = ActiveSupport::HashWithIndifferentAccess.new
26
+ end
27
+
28
+ def ttl(column)
29
+ @ttls[column]
30
+ end
31
+
32
+ def writetime(column)
33
+ @writetimes[column]
34
+ end
35
+
36
+ def set_ttl(column, value)
37
+ @ttls[column] = value
38
+ end
39
+
40
+ def set_writetime(column, value)
41
+ @writetimes[column] = value
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,37 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ #
6
+ # @private
7
+ #
8
+ class RowSpecification
9
+
10
+ def self.build(column_values)
11
+ column_values.map { |column, value| new(column, value) }
12
+ end
13
+
14
+ attr_reader :column, :value
15
+
16
+ def initialize(column, value)
17
+ @column, @value = column, value
18
+ end
19
+
20
+ def cql
21
+ case @value
22
+ when Array
23
+ if @value.length == 1
24
+ ["#{@column} = ?", @value.first]
25
+ else
26
+ ["#{@column} IN (?)", @value]
27
+ end
28
+ else
29
+ ["#{@column} = ?", @value]
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,30 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ class Statement
6
+ attr_reader :bind_vars
7
+
8
+ def initialize
9
+ @cql, @bind_vars = StringIO.new, []
10
+ end
11
+
12
+ def cql
13
+ @cql.string
14
+ end
15
+
16
+ def append(cql, *bind_vars)
17
+ @cql << cql
18
+ @bind_vars.concat(bind_vars)
19
+ self
20
+ end
21
+
22
+ def args
23
+ [cql, *bind_vars]
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end