datastax_rails 1.0.8 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/datastax_rails/associations/belongs_to_association.rb +1 -0
- data/lib/datastax_rails/attribute_methods/definition.rb +1 -1
- data/lib/datastax_rails/attribute_methods/dirty.rb +1 -0
- data/lib/datastax_rails/attribute_methods/typecasting.rb +8 -0
- data/lib/datastax_rails/attribute_methods.rb +5 -1
- data/lib/datastax_rails/base.rb +3 -1
- data/lib/datastax_rails/cql/select.rb +1 -0
- data/lib/datastax_rails/identity/abstract_key_factory.rb +6 -4
- data/lib/datastax_rails/identity.rb +12 -2
- data/lib/datastax_rails/persistence.rb +49 -8
- data/lib/datastax_rails/reflection.rb +1 -1
- data/lib/datastax_rails/relation.rb +13 -3
- data/lib/datastax_rails/types/binary_type.rb +19 -1
- data/lib/datastax_rails/version.rb +1 -1
- data/spec/dummy/log/development.log +89636 -0
- data/spec/dummy/log/test.log +260 -0
- data/spec/support/models.rb +1 -0
- metadata +4 -4
@@ -12,7 +12,7 @@ module DatastaxRails
|
|
12
12
|
value ||= coder.default
|
13
13
|
return unless value
|
14
14
|
|
15
|
-
value = value.kind_of?(String) ? coder.decode(value) : value
|
15
|
+
value = (value.kind_of?(String) || value.kind_of?(Array)) ? coder.decode(value) : value
|
16
16
|
coder.wrap(record, name, value)
|
17
17
|
end
|
18
18
|
end
|
@@ -37,6 +37,14 @@ module DatastaxRails
|
|
37
37
|
self.models << child
|
38
38
|
end
|
39
39
|
|
40
|
+
# Casts a single attribute according to the appropriate coder.
|
41
|
+
#
|
42
|
+
# @param [DatastaxRails::Base] record the record to which this attribute belongs
|
43
|
+
# @param [String] name the name of the attribute
|
44
|
+
# @param [Object] value the value of the attribute prior to typecasting
|
45
|
+
#
|
46
|
+
# @return the typecasted value
|
47
|
+
# @raise [NoMethodError] if the attribute is unknown
|
40
48
|
def typecast_attribute(record, name, value)
|
41
49
|
if attribute_definition = attribute_definitions[name.to_sym]
|
42
50
|
attribute_definition.instantiate(record, value)
|
@@ -51,13 +51,17 @@ module DatastaxRails
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
# Casts the attribute and stores it in the attribute hash.
|
54
55
|
def write_attribute(name, value)
|
55
56
|
@attributes[name.to_s] = self.class.typecast_attribute(self, name, value)
|
56
57
|
end
|
57
58
|
|
59
|
+
# Returns the attribute out of the attribute hash. If the attribute is lazy loaded and hasn't
|
60
|
+
# been loaded yet it will be done so now.
|
58
61
|
def read_attribute(name)
|
59
|
-
if(
|
62
|
+
if(!loaded_attributes[name] && persisted? && !key.blank?)
|
60
63
|
@attributes[name.to_s] = self.class.select(name).with_cassandra.find(self.id).read_attribute(name)
|
64
|
+
loaded_attributes[name] = true
|
61
65
|
end
|
62
66
|
|
63
67
|
@attributes[name.to_s]
|
data/lib/datastax_rails/base.rb
CHANGED
@@ -318,11 +318,13 @@ module DatastaxRails #:nodoc:
|
|
318
318
|
self.models = []
|
319
319
|
|
320
320
|
attr_reader :attributes
|
321
|
+
attr_reader :loaded_attributes
|
321
322
|
attr_accessor :key
|
322
323
|
|
323
324
|
def initialize(attributes = {}, options = {})
|
324
325
|
@key = attributes.delete(:key)
|
325
|
-
@attributes = {}
|
326
|
+
@attributes = {}.with_indifferent_access
|
327
|
+
@loaded_attributes = {}.with_indifferent_access
|
326
328
|
|
327
329
|
@relation = nil
|
328
330
|
@new_record = true
|
@@ -5,8 +5,9 @@ module DatastaxRails
|
|
5
5
|
# Next key takes an object and returns the key object it should use.
|
6
6
|
# object will be ignored with synthetic keys but could be useful with natural ones
|
7
7
|
#
|
8
|
-
# @
|
9
|
-
# @
|
8
|
+
# @abstract
|
9
|
+
# @param [DatastaxRails::Base] object the object that needs a new key
|
10
|
+
# @return [DatastaxRails::Identity::Key] the key
|
10
11
|
#
|
11
12
|
def next_key(object)
|
12
13
|
raise NotImplementedError, "#{self.class.name}#next_key isn't implemented."
|
@@ -14,8 +15,9 @@ module DatastaxRails
|
|
14
15
|
|
15
16
|
# Parse should create a new key object from the 'to_param' format
|
16
17
|
#
|
17
|
-
# @
|
18
|
-
# @
|
18
|
+
# @abstract
|
19
|
+
# @param [String] string the result of calling key.to_param
|
20
|
+
# @return [DatastaxRails::Identity::Key] the parsed key
|
19
21
|
#
|
20
22
|
def parse(string)
|
21
23
|
raise NotImplementedError, "#{self.class.name}#parse isn't implemented."
|
@@ -13,8 +13,8 @@ module DatastaxRails #:nodoc:
|
|
13
13
|
module ClassMethods
|
14
14
|
# Indicate what kind of key the model will have: uuid or natural
|
15
15
|
#
|
16
|
-
# @param [:uuid, :natural] the type of key
|
17
|
-
# @param the options you want to pass along to the key factory (like :attributes => :name, for a natural key).
|
16
|
+
# @param [:uuid, :natural] name_or_factory the type of key
|
17
|
+
# @param [Hash] options the options you want to pass along to the key factory (like :attributes => :name, for a natural key).
|
18
18
|
#
|
19
19
|
def key(name_or_factory = :uuid, *options)
|
20
20
|
@key_factory = case name_or_factory
|
@@ -29,12 +29,22 @@ module DatastaxRails #:nodoc:
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
# The next key for the given object. Delegates the actual work to the factory which may
|
33
|
+
# or may not use the passed in object to generate the key.
|
34
|
+
#
|
35
|
+
# @param [DatastaxRails::Base] object the object for which the key is being generated
|
36
|
+
# @return [String] a key for this object
|
32
37
|
def next_key(object = nil)
|
33
38
|
@key_factory.next_key(object).tap do |key|
|
34
39
|
raise "Keys may not be nil" if key.nil?
|
35
40
|
end
|
36
41
|
end
|
37
42
|
|
43
|
+
# Parses out a key from the given string. Delegates the actual work to the factory.
|
44
|
+
# Return type varies depending on what type of key is used.
|
45
|
+
#
|
46
|
+
# @param [String] string a string representing a primary key
|
47
|
+
# @return an object representing the same key
|
38
48
|
def parse_key(string)
|
39
49
|
@key_factory.parse(string)
|
40
50
|
end
|
@@ -7,7 +7,18 @@ module DatastaxRails
|
|
7
7
|
end
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
-
# Removes one or more records with corresponding keys
|
10
|
+
# Removes one or more records with corresponding keys. Last parameter can be a hash
|
11
|
+
# specifying the consistency level.
|
12
|
+
#
|
13
|
+
# Model.remove('12345','67890', :consistency => 'LOCAL_QUORUM)
|
14
|
+
#
|
15
|
+
# @overload remove(*keys, options)
|
16
|
+
# Removes one or more keys with the given options
|
17
|
+
# @param [String] keys one or more keys to delete
|
18
|
+
# @param [Hash] options generally the consistency level to set
|
19
|
+
# @overload remove(*keys)
|
20
|
+
# Removes one or more keys with the default options
|
21
|
+
# @param [String] keys one or more keys to delete
|
11
22
|
def remove(*keys)
|
12
23
|
options = {}
|
13
24
|
if keys.last.is_a?(Hash)
|
@@ -28,12 +39,12 @@ module DatastaxRails
|
|
28
39
|
end
|
29
40
|
|
30
41
|
# Truncates the column_family associated with this class
|
31
|
-
def
|
42
|
+
def truncate
|
32
43
|
ActiveSupport::Notifications.instrument("truncate.datastax_rails", :column_family => column_family) do
|
33
44
|
cql.truncate.execute
|
34
45
|
end
|
35
46
|
end
|
36
|
-
alias :
|
47
|
+
alias :delete_all :truncate
|
37
48
|
|
38
49
|
def create(attributes = {}, options = {}, &block)
|
39
50
|
new(attributes, &block).tap do |object|
|
@@ -41,6 +52,13 @@ module DatastaxRails
|
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
55
|
+
# Write a record to cassandra. Can be either an insert or an update (they are exactly the same to cassandra)
|
56
|
+
#
|
57
|
+
# @param [String] key the primary key for the record
|
58
|
+
# @param [Hash] attributes a hash containing the columns to set on the record
|
59
|
+
# @param [Hash] options a hash containing various options
|
60
|
+
# @option options [Symbol] :consistency the consistency to set for the Cassandra operation (e.g., ALL)
|
61
|
+
# @option options [String] :schema_version the version of the schema to set for this record
|
44
62
|
def write(key, attributes, options = {})
|
45
63
|
key.tap do |key|
|
46
64
|
attributes = encode_attributes(attributes, options[:schema_version])
|
@@ -59,13 +77,14 @@ module DatastaxRails
|
|
59
77
|
end
|
60
78
|
end
|
61
79
|
|
62
|
-
def instantiate(key, attributes)
|
80
|
+
def instantiate(key, attributes, selected_attributes = [])
|
63
81
|
allocate.tap do |object|
|
82
|
+
object.instance_variable_set("@loaded_attributes", {}.with_indifferent_access)
|
64
83
|
object.instance_variable_set("@schema_version", attributes.delete('schema_version'))
|
65
84
|
object.instance_variable_set("@key", parse_key(key)) if key
|
66
85
|
object.instance_variable_set("@new_record", false)
|
67
86
|
object.instance_variable_set("@destroyed", false)
|
68
|
-
object.instance_variable_set("@attributes", typecast_attributes(object, attributes))
|
87
|
+
object.instance_variable_set("@attributes", typecast_attributes(object, attributes, selected_attributes))
|
69
88
|
end
|
70
89
|
end
|
71
90
|
|
@@ -75,15 +94,37 @@ module DatastaxRails
|
|
75
94
|
if value.nil?
|
76
95
|
encoded[column_name.to_s] = ""
|
77
96
|
else
|
78
|
-
|
97
|
+
encoded_value = attribute_definitions[column_name.to_sym].coder.encode(value)
|
98
|
+
if(encoded_value.is_a?(Array))
|
99
|
+
encoded_value.each_with_index do |chunk,i|
|
100
|
+
encoded[column_name.to_s + "_chunk_#{'%05d' % i}"] = chunk
|
101
|
+
end
|
102
|
+
else
|
103
|
+
encoded[column_name.to_s] = encoded_value
|
104
|
+
end
|
79
105
|
end
|
80
106
|
end
|
81
107
|
encoded
|
82
108
|
end
|
83
109
|
|
84
|
-
def typecast_attributes(object, attributes)
|
110
|
+
def typecast_attributes(object, attributes, selected_attributes = [])
|
85
111
|
attributes = attributes.symbolize_keys
|
86
|
-
|
112
|
+
casted = {}
|
113
|
+
|
114
|
+
selected_attributes.each do |att|
|
115
|
+
object.loaded_attributes[att] = true
|
116
|
+
end
|
117
|
+
|
118
|
+
attribute_definitions.each do |k,definition|
|
119
|
+
if(definition.coder.is_a?(DatastaxRails::Types::BinaryType))
|
120
|
+
# Need to handle possibly chunked data
|
121
|
+
chunks = attributes.select {|key,value| key.to_s =~ /#{k.to_s}_chunk_\d+/ }.sort {|a,b| a.first.to_s <=> b.first.to_s}.collect {|c| c.last}
|
122
|
+
casted[k.to_s] = definition.instantiate(object, chunks)
|
123
|
+
else
|
124
|
+
casted[k.to_s] = definition.instantiate(object, attributes[k])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
casted
|
87
128
|
end
|
88
129
|
end
|
89
130
|
|
@@ -131,7 +131,7 @@ module DatastaxRails
|
|
131
131
|
|
132
132
|
# Holds all the meta-data about an association as it was specified in the
|
133
133
|
# DatastaxRails class.
|
134
|
-
class AssociationReflection < MacroReflection
|
134
|
+
class AssociationReflection < MacroReflection
|
135
135
|
# Returns the target association's class.
|
136
136
|
#
|
137
137
|
# class Author < DatastaxRails::Base
|
@@ -222,14 +222,22 @@ module DatastaxRails
|
|
222
222
|
# For ad-hoc queries, you will have to use Solr.
|
223
223
|
def query_via_cql
|
224
224
|
select_columns = select_values.empty? ? (@klass.attribute_definitions.keys - @klass.lazy_attributes) : select_values.flatten
|
225
|
-
|
225
|
+
select = []
|
226
|
+
select_columns.each do |col|
|
227
|
+
if @klass.attribute_definitions[col.to_sym] && @klass.attribute_definitions[col.to_sym].coder.is_a?(DatastaxRails::Types::BinaryType)
|
228
|
+
select << "'#{col}_chunk_00000' .. '#{col}_chunk_99999'"
|
229
|
+
else
|
230
|
+
select << col
|
231
|
+
end
|
232
|
+
end
|
233
|
+
cql = @cql.select(select)
|
226
234
|
cql.using(@consistency_value) if @consistency_value
|
227
235
|
@where_values.each do |wv|
|
228
236
|
cql.conditions(wv)
|
229
237
|
end
|
230
238
|
results = []
|
231
239
|
CassandraCQL::Result.new(cql.execute).fetch do |row|
|
232
|
-
results << @klass.instantiate(row.row.key,row.to_hash)
|
240
|
+
results << @klass.instantiate(row.row.key, row.to_hash, select_columns)
|
233
241
|
end
|
234
242
|
results
|
235
243
|
end
|
@@ -315,6 +323,8 @@ module DatastaxRails
|
|
315
323
|
params[:fq] = filter_queries
|
316
324
|
end
|
317
325
|
|
326
|
+
select_columns = select_values.empty? ? (@klass.attribute_definitions.keys - @klass.lazy_attributes) : select_values.flatten
|
327
|
+
|
318
328
|
#TODO Need to escape URL stuff (I think)
|
319
329
|
response = rsolr.paginate(@page_value, @per_page_value, 'select', :params => params)["response"]
|
320
330
|
results = DatastaxRails::Collection.new
|
@@ -328,7 +338,7 @@ module DatastaxRails
|
|
328
338
|
else
|
329
339
|
response['docs'].each do |doc|
|
330
340
|
key = doc.delete('id')
|
331
|
-
results << @klass.instantiate(key,doc)
|
341
|
+
results << @klass.instantiate(key,doc, select_columns)
|
332
342
|
end
|
333
343
|
end
|
334
344
|
results
|
@@ -4,7 +4,25 @@ module DatastaxRails
|
|
4
4
|
DEFAULTS = {:solr_type => false, :indexed => false, :stored => false, :multi_valued => false, :sortable => false, :tokenized => false, :fulltext => false}
|
5
5
|
def encode(str)
|
6
6
|
raise ArgumentError.new("#{self} requires a String") unless str.kind_of?(String)
|
7
|
-
str
|
7
|
+
io = StringIO.new(Base64.encode64(str))
|
8
|
+
chunks = []
|
9
|
+
while chunk = io.read(1.megabyte)
|
10
|
+
chunks << chunk
|
11
|
+
end
|
12
|
+
chunks
|
13
|
+
end
|
14
|
+
|
15
|
+
def decode(arr)
|
16
|
+
if(arr.is_a?(Array))
|
17
|
+
io = StringIO.new("","w+")
|
18
|
+
arr.each do |chunk|
|
19
|
+
io.write(chunk)
|
20
|
+
end
|
21
|
+
io.rewind
|
22
|
+
Base64.decode64(io.read)
|
23
|
+
else
|
24
|
+
arr
|
25
|
+
end
|
8
26
|
end
|
9
27
|
|
10
28
|
def wrap(record, name, value)
|