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