cequel 0.0.0 → 0.4.0
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/cequel.rb +16 -0
- data/lib/cequel/batch.rb +58 -0
- data/lib/cequel/cql_row_specification.rb +22 -0
- data/lib/cequel/data_set.rb +346 -0
- data/lib/cequel/errors.rb +4 -0
- data/lib/cequel/keyspace.rb +106 -0
- data/lib/cequel/model.rb +95 -0
- data/lib/cequel/model/associations.rb +120 -0
- data/lib/cequel/model/callbacks.rb +32 -0
- data/lib/cequel/model/class_internals.rb +48 -0
- data/lib/cequel/model/column.rb +20 -0
- data/lib/cequel/model/dictionary.rb +202 -0
- data/lib/cequel/model/dirty.rb +53 -0
- data/lib/cequel/model/dynamic.rb +31 -0
- data/lib/cequel/model/errors.rb +13 -0
- data/lib/cequel/model/inheritable.rb +48 -0
- data/lib/cequel/model/instance_internals.rb +23 -0
- data/lib/cequel/model/local_association.rb +42 -0
- data/lib/cequel/model/magic.rb +79 -0
- data/lib/cequel/model/mass_assignment_security.rb +21 -0
- data/lib/cequel/model/naming.rb +17 -0
- data/lib/cequel/model/observer.rb +42 -0
- data/lib/cequel/model/persistence.rb +173 -0
- data/lib/cequel/model/properties.rb +143 -0
- data/lib/cequel/model/railtie.rb +33 -0
- data/lib/cequel/model/remote_association.rb +40 -0
- data/lib/cequel/model/scope.rb +362 -0
- data/lib/cequel/model/scoped.rb +50 -0
- data/lib/cequel/model/subclass_internals.rb +45 -0
- data/lib/cequel/model/timestamps.rb +52 -0
- data/lib/cequel/model/translation.rb +17 -0
- data/lib/cequel/model/validations.rb +50 -0
- data/lib/cequel/new_relic_instrumentation.rb +22 -0
- data/lib/cequel/row_specification.rb +63 -0
- data/lib/cequel/statement.rb +23 -0
- data/lib/cequel/version.rb +3 -0
- data/spec/environment.rb +3 -0
- data/spec/examples/data_set_spec.rb +382 -0
- data/spec/examples/keyspace_spec.rb +63 -0
- data/spec/examples/model/associations_spec.rb +109 -0
- data/spec/examples/model/callbacks_spec.rb +79 -0
- data/spec/examples/model/dictionary_spec.rb +413 -0
- data/spec/examples/model/dirty_spec.rb +39 -0
- data/spec/examples/model/dynamic_spec.rb +41 -0
- data/spec/examples/model/inheritable_spec.rb +45 -0
- data/spec/examples/model/magic_spec.rb +199 -0
- data/spec/examples/model/mass_assignment_security_spec.rb +13 -0
- data/spec/examples/model/naming_spec.rb +9 -0
- data/spec/examples/model/observer_spec.rb +86 -0
- data/spec/examples/model/persistence_spec.rb +201 -0
- data/spec/examples/model/properties_spec.rb +81 -0
- data/spec/examples/model/scope_spec.rb +677 -0
- data/spec/examples/model/serialization_spec.rb +20 -0
- data/spec/examples/model/spec_helper.rb +12 -0
- data/spec/examples/model/timestamps_spec.rb +52 -0
- data/spec/examples/model/translation_spec.rb +23 -0
- data/spec/examples/model/validations_spec.rb +86 -0
- data/spec/examples/spec_helper.rb +9 -0
- data/spec/models/asset.rb +21 -0
- data/spec/models/asset_observer.rb +5 -0
- data/spec/models/blog.rb +14 -0
- data/spec/models/blog_posts.rb +6 -0
- data/spec/models/category.rb +9 -0
- data/spec/models/comment.rb +12 -0
- data/spec/models/photo.rb +5 -0
- data/spec/models/post.rb +88 -0
- data/spec/models/post_comments.rb +14 -0
- data/spec/models/post_observer.rb +43 -0
- data/spec/support/helpers.rb +26 -0
- data/spec/support/result_stub.rb +27 -0
- metadata +125 -23
@@ -0,0 +1,143 @@
|
|
1
|
+
module Cequel
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
module Properties
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
include ActiveModel::Conversion
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
def key(key_alias, type)
|
16
|
+
key_alias = key_alias.to_sym
|
17
|
+
@_cequel.key = Column.new(key_alias, type)
|
18
|
+
|
19
|
+
module_eval(<<-RUBY, __FILE__, __LINE__+1)
|
20
|
+
def #{key_alias}
|
21
|
+
@_cequel.key
|
22
|
+
end
|
23
|
+
|
24
|
+
def #{key_alias}=(key)
|
25
|
+
@_cequel.key = key
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_key
|
29
|
+
[@_cequel.key]
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
end
|
33
|
+
|
34
|
+
def column(name, type, options = {})
|
35
|
+
name = name.to_sym
|
36
|
+
@_cequel.add_column(name, type, options.symbolize_keys)
|
37
|
+
|
38
|
+
module_eval <<-RUBY, __FILE__, __LINE__+1
|
39
|
+
def #{name}
|
40
|
+
read_attribute(#{name.inspect})
|
41
|
+
end
|
42
|
+
|
43
|
+
def #{name}=(value)
|
44
|
+
write_attribute(#{name.inspect}, value)
|
45
|
+
end
|
46
|
+
RUBY
|
47
|
+
|
48
|
+
if type == :boolean
|
49
|
+
module_eval <<-RUBY, __FILE__, __LINE__+1 if type == :boolean
|
50
|
+
def #{name}?
|
51
|
+
!!read_attribute(#{name.inspect})
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def key_alias
|
58
|
+
key_column.name
|
59
|
+
end
|
60
|
+
|
61
|
+
def key_column
|
62
|
+
@_cequel.key
|
63
|
+
end
|
64
|
+
|
65
|
+
def column_names
|
66
|
+
[@_cequel.key.name, *@_cequel.columns.keys]
|
67
|
+
end
|
68
|
+
|
69
|
+
def columns
|
70
|
+
[@_cequel.key, *@_cequel.columns.values]
|
71
|
+
end
|
72
|
+
|
73
|
+
def type_column
|
74
|
+
@_cequel.type_column
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
def initialize(attributes = {})
|
80
|
+
super()
|
81
|
+
@_cequel.key = generate_key
|
82
|
+
self.class.columns.each do |column|
|
83
|
+
default = column.default
|
84
|
+
@_cequel.attributes[column.name] = default unless default.nil?
|
85
|
+
end
|
86
|
+
self.attributes = attributes
|
87
|
+
yield self if block_given?
|
88
|
+
end
|
89
|
+
|
90
|
+
def attributes
|
91
|
+
{self.class.key_alias => @_cequel.key}.with_indifferent_access.
|
92
|
+
merge(@_cequel.attributes)
|
93
|
+
end
|
94
|
+
|
95
|
+
def attributes=(attributes)
|
96
|
+
attributes.each_pair do |column_name, value|
|
97
|
+
__send__("#{column_name}=", value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def ==(other)
|
102
|
+
return false if self.class != other.class
|
103
|
+
self_key = self.__send__(self.class.key_column.name)
|
104
|
+
other_key = other.__send__(self.class.key_column.name)
|
105
|
+
self_key && other_key && self_key == other_key
|
106
|
+
end
|
107
|
+
|
108
|
+
def inspect
|
109
|
+
"#<#{self.class.name}".tap do |inspected|
|
110
|
+
attributes.each_pair do |column, value|
|
111
|
+
inspected_value =
|
112
|
+
case value
|
113
|
+
when CassandraCQL::UUID then value.to_guid
|
114
|
+
else value.inspect
|
115
|
+
end
|
116
|
+
inspected << " #{column}:#{inspected_value}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def write_attribute(column_name, value)
|
124
|
+
if value.nil?
|
125
|
+
@_cequel.attributes.delete(column_name)
|
126
|
+
else
|
127
|
+
@_cequel.attributes[column_name] = value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def read_attribute(column_name)
|
132
|
+
@_cequel.attributes[column_name.to_sym]
|
133
|
+
end
|
134
|
+
|
135
|
+
def generate_key
|
136
|
+
# Noop -- model classes can override if desired
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Cequel
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
|
7
|
+
config.cequel = Cequel::Model
|
8
|
+
|
9
|
+
initializer "cequel.configure_rails" do
|
10
|
+
config_path = Rails.root.join('config/cequel.yml').to_s
|
11
|
+
|
12
|
+
if File.exist?(config_path)
|
13
|
+
yaml = YAML.load_file(config_path)[Rails.env]
|
14
|
+
Cequel::Model.configure(yaml.symbolize_keys) if yaml
|
15
|
+
end
|
16
|
+
|
17
|
+
Cequel::Model.logger = Rails.logger
|
18
|
+
end
|
19
|
+
|
20
|
+
initializer "cequel.instantiate_observers" do
|
21
|
+
config.after_initialize do
|
22
|
+
::Cequel::Model.instantiate_observers
|
23
|
+
|
24
|
+
ActionDispatch::Callbacks.to_prepare do
|
25
|
+
::Cequel::Model.instantiate_observers
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Cequel
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class RemoteAssociation
|
6
|
+
|
7
|
+
attr_reader :name, :class_name, :dependent
|
8
|
+
|
9
|
+
def initialize(name, owning_class, options)
|
10
|
+
@name, @owning_class = name, owning_class
|
11
|
+
@class_name = options[:class_name] || name.to_s.classify.to_sym
|
12
|
+
@foreign_key_name = options[:foreign_key]
|
13
|
+
@dependent = options[:dependent]
|
14
|
+
end
|
15
|
+
|
16
|
+
def scope(instance)
|
17
|
+
clazz.where(foreign_key_name => instance.__send__(primary_key_name))
|
18
|
+
end
|
19
|
+
|
20
|
+
def primary_key
|
21
|
+
@primary_key ||= @owning_class.key_column
|
22
|
+
end
|
23
|
+
|
24
|
+
def primary_key_name
|
25
|
+
@primary_key_name ||= primary_key.name
|
26
|
+
end
|
27
|
+
|
28
|
+
def foreign_key_name
|
29
|
+
@foreign_key_name ||= :"#{@owning_class.name.underscore}_id"
|
30
|
+
end
|
31
|
+
|
32
|
+
def clazz
|
33
|
+
@clazz ||= class_name.to_s.constantize
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,362 @@
|
|
1
|
+
module Cequel
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Scope < BasicObject
|
6
|
+
|
7
|
+
include ::Enumerable
|
8
|
+
|
9
|
+
def initialize(clazz, data_sets)
|
10
|
+
@clazz, @data_sets = clazz, data_sets
|
11
|
+
@index_preference_applied = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block)
|
15
|
+
if block
|
16
|
+
each_row do |row|
|
17
|
+
result = hydrate(row)
|
18
|
+
yield result if result
|
19
|
+
end
|
20
|
+
else
|
21
|
+
::Enumerator.new(self, :each)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def each_row(&block)
|
26
|
+
if block
|
27
|
+
apply_index_preference!
|
28
|
+
@data_sets.each do |data_set|
|
29
|
+
data_set.each(&block)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
::Enumerator.new(self, :each_row)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def find_in_batches(options = {})
|
37
|
+
unless ::Kernel.block_given?
|
38
|
+
return ::Enumerator.new(self, :find_in_batches, options)
|
39
|
+
end
|
40
|
+
find_rows_in_batches(options) do |batch|
|
41
|
+
results = batch.map { |row| hydrate(row) }.compact
|
42
|
+
yield results if results.any?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_rows_in_batches(options = {}, &block)
|
47
|
+
return ::Enumerator.new(self, :find_rows_in_batches, options) if block.nil?
|
48
|
+
batch_size = options[:batch_size] || 1000
|
49
|
+
apply_index_preference!
|
50
|
+
@data_sets.each do |data_set|
|
51
|
+
keys = lookup_keys(data_set)
|
52
|
+
if keys
|
53
|
+
find_rows_in_key_batches(data_set, keys, batch_size, &block)
|
54
|
+
else
|
55
|
+
find_rows_in_range_batches(data_set, batch_size, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_each(options = {}, &block)
|
62
|
+
unless ::Kernel.block_given?
|
63
|
+
return ::Enumerator.new(self, :find_each, options)
|
64
|
+
end
|
65
|
+
find_in_batches(options) { |batch| batch.each(&block) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_each_row(options = {}, &block)
|
69
|
+
unless ::Kernel.block_given?
|
70
|
+
return ::Enumerator.new(self, :find_each_row, options)
|
71
|
+
end
|
72
|
+
find_rows_in_batches(options) { |batch| batch.each(&block) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def first
|
76
|
+
apply_index_preference!
|
77
|
+
@data_sets.each do |data_set|
|
78
|
+
row = hydrate(data_set.first)
|
79
|
+
return row if row
|
80
|
+
end
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def count
|
85
|
+
if restriction_columns == [@clazz.key_alias]
|
86
|
+
::Kernel.raise ::Cequel::Model::InvalidQuery,
|
87
|
+
"Meaningless to perform count with key row restrictions"
|
88
|
+
end
|
89
|
+
apply_index_preference!
|
90
|
+
@data_sets.inject(0) { |count, data_set| count + data_set.count }
|
91
|
+
end
|
92
|
+
|
93
|
+
def size
|
94
|
+
count
|
95
|
+
end
|
96
|
+
|
97
|
+
def length
|
98
|
+
to_a.length
|
99
|
+
end
|
100
|
+
|
101
|
+
def update_all(changes)
|
102
|
+
keys = keys()
|
103
|
+
unless keys.empty?
|
104
|
+
@clazz.column_family.where(key_alias => keys).update(changes)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def destroy_all
|
109
|
+
each { |instance| instance.destroy }
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete_all
|
113
|
+
if @data_sets.length == 1
|
114
|
+
if @data_sets.first.row_specifications.length == 0
|
115
|
+
return @data_sets.first.truncate
|
116
|
+
end
|
117
|
+
end
|
118
|
+
keys = keys()
|
119
|
+
if keys.empty?
|
120
|
+
@data_sets.each { |data_set| data_set.delete }
|
121
|
+
else
|
122
|
+
@clazz.column_family.where(key_alias => keys).delete
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def find(*keys, &block)
|
127
|
+
if block then super
|
128
|
+
else with_scope(self) { @clazz.find(*keys) }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def any?(&block)
|
133
|
+
if block then super
|
134
|
+
else count > 0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def none?(&block)
|
139
|
+
if block then super
|
140
|
+
else empty?
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def empty?
|
145
|
+
count == 0
|
146
|
+
end
|
147
|
+
|
148
|
+
def one?(&block)
|
149
|
+
if block then super
|
150
|
+
else count == 1
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def keys
|
155
|
+
key_alias = @clazz.key_alias
|
156
|
+
[].tap do |keys|
|
157
|
+
@data_sets.each do |data_set|
|
158
|
+
lookup_keys = lookup_keys(data_set)
|
159
|
+
if lookup_keys
|
160
|
+
keys.concat(lookup_keys)
|
161
|
+
next
|
162
|
+
end
|
163
|
+
data_set.select!(key_alias).each { |row| keys << row[key_alias] }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def lookup_keys(data_set)
|
169
|
+
if data_set.row_specifications.length == 1
|
170
|
+
specification = data_set.row_specifications.first
|
171
|
+
if specification.respond_to?(:column)
|
172
|
+
if specification.column == key_alias
|
173
|
+
::Kernel.Array(specification.value)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def inspect
|
180
|
+
to_a.inspect
|
181
|
+
end
|
182
|
+
|
183
|
+
def ==(other)
|
184
|
+
to_a == other.to_a
|
185
|
+
end
|
186
|
+
|
187
|
+
def select(*rows, &block)
|
188
|
+
if block then super
|
189
|
+
else scoped { |data_set| data_set.select(*rows) }.validate!
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def select!(*rows)
|
194
|
+
scoped { |data_set| data_set.select!(*rows) }.validate!
|
195
|
+
end
|
196
|
+
|
197
|
+
def consistency(consistency)
|
198
|
+
scoped { |data_set| data_set.consistency(consistency) }
|
199
|
+
end
|
200
|
+
|
201
|
+
def where(*row_specification)
|
202
|
+
if row_specification.length == 1 && ::Hash === row_specification.first
|
203
|
+
row_specification.first.each_pair.inject(self) do |scope, (column, value)|
|
204
|
+
scope.where_column_equals(column, value)
|
205
|
+
end
|
206
|
+
else
|
207
|
+
scoped { |data_set| data_set.where(*row_specification) }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def where!(*row_specification)
|
212
|
+
scoped { |data_set| data_set.where!(*row_specification) }
|
213
|
+
end
|
214
|
+
|
215
|
+
def limit(*row_specification)
|
216
|
+
scoped { |data_set| data_set.limit(*row_specification) }
|
217
|
+
end
|
218
|
+
|
219
|
+
def scoped(&block)
|
220
|
+
new_data_sets = @data_sets.map(&block)
|
221
|
+
Scope.new(@clazz, new_data_sets)
|
222
|
+
end
|
223
|
+
|
224
|
+
def nil?
|
225
|
+
false # for ActiveSupport delegation
|
226
|
+
end
|
227
|
+
|
228
|
+
def method_missing(method, *args, &block)
|
229
|
+
if @clazz.respond_to?(method)
|
230
|
+
@clazz.with_scope(self) do
|
231
|
+
@clazz.__send__(method, *args, &block)
|
232
|
+
end
|
233
|
+
else
|
234
|
+
super
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
protected
|
239
|
+
|
240
|
+
def validate!
|
241
|
+
columns = restriction_columns
|
242
|
+
key_column = restriction_columns.include?(@clazz.key_alias)
|
243
|
+
non_key_column = restriction_columns.any? do |column|
|
244
|
+
column != @clazz.key_alias
|
245
|
+
end
|
246
|
+
if key_column
|
247
|
+
if non_key_column
|
248
|
+
::Kernel.raise InvalidQuery,
|
249
|
+
"Can't select by key and non-key columns in the same query"
|
250
|
+
elsif key_only_select?
|
251
|
+
::Kernel.raise InvalidQuery,
|
252
|
+
"Meaningless to select only key column with key row specification"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
self
|
256
|
+
end
|
257
|
+
|
258
|
+
def where_column_equals(column, value)
|
259
|
+
if [] == value
|
260
|
+
Scope.new(@clazz, [])
|
261
|
+
elsif column.to_sym != @clazz.key_alias && ::Array === value
|
262
|
+
new_data_sets = []
|
263
|
+
@data_sets.each do |data_set|
|
264
|
+
value.each do |element|
|
265
|
+
new_data_sets << data_set.where(column => element)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
Scope.new(@clazz, new_data_sets)
|
269
|
+
else
|
270
|
+
scoped { |data_set| data_set.where(column => value) }
|
271
|
+
end.validate!
|
272
|
+
end
|
273
|
+
|
274
|
+
private
|
275
|
+
|
276
|
+
def find_rows_in_range_batches(data_set, batch_size)
|
277
|
+
key_alias = @clazz.key_alias
|
278
|
+
key_alias = key_alias.upcase if key_alias =~ /key/i
|
279
|
+
scope = data_set.limit(batch_size)
|
280
|
+
unless data_set.select_columns.empty? ||
|
281
|
+
data_set.select_columns.include?(key_alias)
|
282
|
+
|
283
|
+
scope = scope.select(key_alias)
|
284
|
+
end
|
285
|
+
|
286
|
+
batch_scope = scope
|
287
|
+
last_key = nil
|
288
|
+
begin
|
289
|
+
batch_rows = batch_scope.to_a
|
290
|
+
break if batch_rows.empty?
|
291
|
+
if batch_rows.first[key_alias] == last_key
|
292
|
+
yield batch_rows[1..-1]
|
293
|
+
else
|
294
|
+
yield batch_rows
|
295
|
+
end
|
296
|
+
last_key = batch_rows.last[key_alias]
|
297
|
+
batch_scope =
|
298
|
+
scope.where("? > ?", key_alias, last_key)
|
299
|
+
end while batch_rows.length == batch_size
|
300
|
+
end
|
301
|
+
|
302
|
+
def find_rows_in_key_batches(data_set, keys, batch_size)
|
303
|
+
key_alias = @clazz.key_alias
|
304
|
+
keys.each_slice(batch_size) do |key_slice|
|
305
|
+
yield data_set.where!(key_alias => key_slice).to_a
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def key_only_select?
|
310
|
+
key_only_select = @data_sets.all? do |data_set|
|
311
|
+
data_set.select_columns == [@clazz.key_alias]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def restriction_columns
|
316
|
+
[].tap do |columns|
|
317
|
+
@data_sets.each do |data_set|
|
318
|
+
data_set.row_specifications.each do |specification|
|
319
|
+
if specification.respond_to?(:column)
|
320
|
+
columns << specification.column
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def hydrate(row)
|
328
|
+
return if row.nil?
|
329
|
+
key_alias = @clazz.key_alias.to_s
|
330
|
+
key_alias = key_alias.upcase if key_alias =~ /^key$/i
|
331
|
+
row.reject! { |k, v| v.nil? }
|
332
|
+
if row.keys.any? && (key_only_select? || row.keys != [key_alias])
|
333
|
+
@clazz._hydrate(row)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def apply_index_preference!
|
338
|
+
return if @index_preference_applied
|
339
|
+
# XXX seems ugly to do the in-place sort here.
|
340
|
+
preference = @clazz.index_preference_columns
|
341
|
+
@data_sets.each do |data_set|
|
342
|
+
data_set.row_specifications.sort! do |spec1, spec2|
|
343
|
+
if spec1.respond_to?(:column) && spec2.respond_to?(:column)
|
344
|
+
pref1 = preference.index(spec1.column)
|
345
|
+
pref2 = preference.index(spec2.column)
|
346
|
+
if pref1 && pref2 then pref1 - pref2
|
347
|
+
elsif pref1 then -1
|
348
|
+
elsif pref2 then 1
|
349
|
+
else 0
|
350
|
+
end
|
351
|
+
else 0
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
@index_preference_applied = true
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|