datastax_rails 1.0.8 → 1.0.10
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.
- 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)
|