cequel 0.5.6 → 1.0.0.pre.1
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.
- checksums.yaml +7 -0
- data/lib/cequel.rb +5 -8
- data/lib/cequel/errors.rb +1 -0
- data/lib/cequel/metal.rb +17 -0
- data/lib/cequel/metal/batch.rb +62 -0
- data/lib/cequel/metal/cql_row_specification.rb +26 -0
- data/lib/cequel/metal/data_set.rb +461 -0
- data/lib/cequel/metal/deleter.rb +47 -0
- data/lib/cequel/metal/incrementer.rb +35 -0
- data/lib/cequel/metal/inserter.rb +53 -0
- data/lib/cequel/metal/keyspace.rb +213 -0
- data/lib/cequel/metal/row.rb +48 -0
- data/lib/cequel/metal/row_specification.rb +37 -0
- data/lib/cequel/metal/statement.rb +30 -0
- data/lib/cequel/metal/updater.rb +65 -0
- data/lib/cequel/metal/writer.rb +73 -0
- data/lib/cequel/model.rb +12 -84
- data/lib/cequel/model/association_collection.rb +23 -0
- data/lib/cequel/model/associations.rb +84 -80
- data/lib/cequel/model/base.rb +74 -0
- data/lib/cequel/model/belongs_to_association.rb +31 -0
- data/lib/cequel/model/callbacks.rb +14 -10
- data/lib/cequel/model/collection.rb +255 -0
- data/lib/cequel/model/errors.rb +6 -6
- data/lib/cequel/model/has_many_association.rb +26 -0
- data/lib/cequel/model/mass_assignment.rb +31 -0
- data/lib/cequel/model/persistence.rb +119 -115
- data/lib/cequel/model/properties.rb +89 -87
- data/lib/cequel/model/railtie.rb +21 -14
- data/lib/cequel/model/record_set.rb +285 -0
- data/lib/cequel/model/schema.rb +33 -0
- data/lib/cequel/model/scoped.rb +5 -48
- data/lib/cequel/model/validations.rb +18 -18
- data/lib/cequel/schema.rb +15 -0
- data/lib/cequel/schema/column.rb +135 -0
- data/lib/cequel/schema/create_table_dsl.rb +56 -0
- data/lib/cequel/schema/keyspace.rb +50 -0
- data/lib/cequel/schema/table.rb +120 -0
- data/lib/cequel/schema/table_property.rb +67 -0
- data/lib/cequel/schema/table_reader.rb +139 -0
- data/lib/cequel/schema/table_synchronizer.rb +114 -0
- data/lib/cequel/schema/table_updater.rb +83 -0
- data/lib/cequel/schema/table_writer.rb +80 -0
- data/lib/cequel/schema/update_table_dsl.rb +60 -0
- data/lib/cequel/type.rb +232 -0
- data/lib/cequel/version.rb +1 -1
- data/spec/environment.rb +5 -1
- data/spec/examples/metal/data_set_spec.rb +608 -0
- data/spec/examples/model/associations_spec.rb +84 -74
- data/spec/examples/model/callbacks_spec.rb +66 -59
- data/spec/examples/model/list_spec.rb +393 -0
- data/spec/examples/model/map_spec.rb +229 -0
- data/spec/examples/model/mass_assignment_spec.rb +55 -0
- data/spec/examples/model/naming_spec.rb +11 -4
- data/spec/examples/model/persistence_spec.rb +140 -150
- data/spec/examples/model/properties_spec.rb +122 -75
- data/spec/examples/model/record_set_spec.rb +285 -0
- data/spec/examples/model/schema_spec.rb +44 -0
- data/spec/examples/model/serialization_spec.rb +20 -14
- data/spec/examples/model/set_spec.rb +133 -0
- data/spec/examples/model/spec_helper.rb +0 -10
- data/spec/examples/model/validations_spec.rb +51 -38
- data/spec/examples/schema/table_reader_spec.rb +328 -0
- data/spec/examples/schema/table_synchronizer_spec.rb +172 -0
- data/spec/examples/schema/table_updater_spec.rb +157 -0
- data/spec/examples/schema/table_writer_spec.rb +225 -0
- data/spec/examples/spec_helper.rb +29 -0
- data/spec/examples/type_spec.rb +204 -0
- data/spec/support/helpers.rb +67 -8
- metadata +121 -152
- data/lib/cequel/batch.rb +0 -58
- data/lib/cequel/cql_row_specification.rb +0 -22
- data/lib/cequel/data_set.rb +0 -371
- data/lib/cequel/keyspace.rb +0 -205
- data/lib/cequel/model/class_internals.rb +0 -49
- data/lib/cequel/model/column.rb +0 -20
- data/lib/cequel/model/counter.rb +0 -35
- data/lib/cequel/model/dictionary.rb +0 -126
- data/lib/cequel/model/dirty.rb +0 -53
- data/lib/cequel/model/dynamic.rb +0 -31
- data/lib/cequel/model/inheritable.rb +0 -48
- data/lib/cequel/model/instance_internals.rb +0 -23
- data/lib/cequel/model/local_association.rb +0 -42
- data/lib/cequel/model/magic.rb +0 -79
- data/lib/cequel/model/mass_assignment_security.rb +0 -21
- data/lib/cequel/model/naming.rb +0 -17
- data/lib/cequel/model/observer.rb +0 -42
- data/lib/cequel/model/readable_dictionary.rb +0 -182
- data/lib/cequel/model/remote_association.rb +0 -40
- data/lib/cequel/model/scope.rb +0 -362
- data/lib/cequel/model/subclass_internals.rb +0 -45
- data/lib/cequel/model/timestamps.rb +0 -52
- data/lib/cequel/model/translation.rb +0 -17
- data/lib/cequel/row_specification.rb +0 -63
- data/lib/cequel/statement.rb +0 -23
- data/spec/examples/data_set_spec.rb +0 -444
- data/spec/examples/keyspace_spec.rb +0 -84
- data/spec/examples/model/counter_spec.rb +0 -94
- data/spec/examples/model/dictionary_spec.rb +0 -301
- data/spec/examples/model/dirty_spec.rb +0 -39
- data/spec/examples/model/dynamic_spec.rb +0 -41
- data/spec/examples/model/inheritable_spec.rb +0 -45
- data/spec/examples/model/magic_spec.rb +0 -199
- data/spec/examples/model/mass_assignment_security_spec.rb +0 -13
- data/spec/examples/model/observer_spec.rb +0 -86
- data/spec/examples/model/scope_spec.rb +0 -677
- data/spec/examples/model/timestamps_spec.rb +0 -52
- 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
|