cequel 1.0.0.rc2 → 1.0.0.rc3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cequel/record.rb +2 -0
- data/lib/cequel/record/association_collection.rb +77 -2
- data/lib/cequel/record/conversion.rb +15 -0
- data/lib/cequel/record/lazy_record_collection.rb +5 -0
- data/lib/cequel/record/persistence.rb +1 -0
- data/lib/cequel/record/record_set.rb +51 -10
- data/lib/cequel/record/scoped.rb +5 -0
- data/lib/cequel/version.rb +1 -1
- data/spec/examples/record/associations_spec.rb +51 -0
- data/spec/examples/record/persistence_spec.rb +5 -0
- data/spec/examples/record/record_set_spec.rb +33 -3
- data/spec/support/helpers.rb +0 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ed6d7b1a7f6b34c7ebfb6421f5402b163f79ad2
|
4
|
+
data.tar.gz: 6e88f21a67e23480f71b2c391f20d21df851b490
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53f2888d6b7fe8be5b36cb647e558be6b2ea4a8cffde531125f398a03bc922b19a90e5c02c6a92434a0ff9f7f7b40bd0339280b0d7e21f9daf2e7695a5647a8a
|
7
|
+
data.tar.gz: c92544762c93bab662602603d9d32810fd19f2010dd8754c955ac9ec674207104753453de508a0bf8fff346c4b6cd9d147d6b72230c38396ff9995fd098cd9fa
|
data/lib/cequel/record.rb
CHANGED
@@ -21,6 +21,7 @@ require 'cequel/record/mass_assignment'
|
|
21
21
|
require 'cequel/record/callbacks'
|
22
22
|
require 'cequel/record/validations'
|
23
23
|
require 'cequel/record/dirty'
|
24
|
+
require 'cequel/record/conversion'
|
24
25
|
|
25
26
|
require 'cequel/record'
|
26
27
|
|
@@ -88,6 +89,7 @@ module Cequel
|
|
88
89
|
include Validations
|
89
90
|
include Dirty
|
90
91
|
extend ActiveModel::Naming
|
92
|
+
include Conversion
|
91
93
|
include ActiveModel::Serializers::JSON
|
92
94
|
include ActiveModel::Serializers::Xml
|
93
95
|
end
|
@@ -2,7 +2,7 @@ module Cequel
|
|
2
2
|
module Record
|
3
3
|
#
|
4
4
|
# Collection of records from a
|
5
|
-
# {Associations::ClassMethods#has_many has_many}
|
5
|
+
# {Associations::ClassMethods#has_many has_many} association. Encapsulates
|
6
6
|
# and behaves like a {RecordSet}, but unlike a normal RecordSet the loaded
|
7
7
|
# records are held in memory after they are loaded.
|
8
8
|
#
|
@@ -11,6 +11,7 @@ module Cequel
|
|
11
11
|
#
|
12
12
|
class AssociationCollection < DelegateClass(RecordSet)
|
13
13
|
include Enumerable
|
14
|
+
extend Forwardable
|
14
15
|
|
15
16
|
#
|
16
17
|
# @yield [Record]
|
@@ -20,10 +21,84 @@ module Cequel
|
|
20
21
|
target.each(&block)
|
21
22
|
end
|
22
23
|
|
24
|
+
#
|
25
|
+
# (see RecordSet#find)
|
26
|
+
#
|
27
|
+
def find(*keys)
|
28
|
+
if block_given? then super
|
29
|
+
else record_set.find(*keys)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# (see RecordSet#select)
|
35
|
+
#
|
36
|
+
def select(*columns)
|
37
|
+
if block_given? then super
|
38
|
+
else record_set.select(*columns)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# (see RecordSet#first)
|
44
|
+
#
|
45
|
+
def first(*args)
|
46
|
+
if loaded? then super
|
47
|
+
else record_set.first(*args)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# @!method count
|
53
|
+
# Get the count of child records stored in the database. This method
|
54
|
+
# will always query Cassandra, even if the records are loaded in
|
55
|
+
# memory.
|
56
|
+
#
|
57
|
+
# @return [Integer] number of child records in the database
|
58
|
+
# @see #size
|
59
|
+
# @see #length
|
60
|
+
#
|
61
|
+
def_delegator :record_set, :count
|
62
|
+
|
63
|
+
#
|
64
|
+
# @!method length
|
65
|
+
# The number of child instances in the in-memory collection. If the
|
66
|
+
# records are not loaded in memory, they will be loaded and then
|
67
|
+
# counted.
|
68
|
+
#
|
69
|
+
# @return [Integer] length of the loaded record collection in memory
|
70
|
+
# @see #size
|
71
|
+
# @see #count
|
72
|
+
#
|
73
|
+
def_delegator :entries, :length
|
74
|
+
|
75
|
+
#
|
76
|
+
# Get the size of the child collection. If the records are loaded in
|
77
|
+
# memory from a previous operation, count the length of the array in
|
78
|
+
# memory. If the collection is unloaded, perform a `COUNT` query.
|
79
|
+
#
|
80
|
+
# @return [Integer] size of the child collection
|
81
|
+
# @see #length
|
82
|
+
# @see #count
|
83
|
+
#
|
84
|
+
def size
|
85
|
+
loaded? ? length : count
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# @return [Boolean] true if this collection's records are loaded in
|
90
|
+
# memory
|
91
|
+
#
|
92
|
+
def loaded?
|
93
|
+
!!@target
|
94
|
+
end
|
95
|
+
|
23
96
|
private
|
24
97
|
|
98
|
+
alias_method :record_set, :__getobj__
|
99
|
+
|
25
100
|
def target
|
26
|
-
@target ||=
|
101
|
+
@target ||= record_set.entries
|
27
102
|
end
|
28
103
|
end
|
29
104
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Cequel
|
2
|
+
module Record
|
3
|
+
#
|
4
|
+
# Provides support for ActiveModel::Conversions
|
5
|
+
#
|
6
|
+
module Conversion
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
include ActiveModel::Conversion
|
11
|
+
alias_method :to_key, :key_values
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -224,8 +224,7 @@ module Cequel
|
|
224
224
|
#
|
225
225
|
def at(*scoped_key_values)
|
226
226
|
warn "`at` is deprecated. Use `[]` instead"
|
227
|
-
scoped_key_values
|
228
|
-
.reduce(self) { |record_set, key_value| record_set[key_value] }
|
227
|
+
traverse(*scoped_key_values)
|
229
228
|
end
|
230
229
|
|
231
230
|
#
|
@@ -314,6 +313,9 @@ module Cequel
|
|
314
313
|
# Return a loaded Record or collection of loaded Records with the
|
315
314
|
# specified primary key values
|
316
315
|
#
|
316
|
+
# Multiple arguments are mapped onto unscoped key columns. To specify
|
317
|
+
# multiple values for a given key column, use an array.
|
318
|
+
#
|
317
319
|
# @param scoped_key_values one or more values for the final primary key
|
318
320
|
# column
|
319
321
|
# @return [Record] if a single key is specified, return the loaded
|
@@ -322,15 +324,31 @@ module Cequel
|
|
322
324
|
# collection of loaded records at those keys
|
323
325
|
# @raise [RecordNotFound] if not all the keys correspond to records in
|
324
326
|
# the table
|
325
|
-
# @raise [ArgumentError] if not all primary key columns have been
|
326
|
-
# specified
|
327
327
|
#
|
328
|
-
# @
|
329
|
-
#
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
328
|
+
# @example One record with one-column primary key
|
329
|
+
# # find the blog with subdomain 'cassandra'
|
330
|
+
# Blog.find('cassandra')
|
331
|
+
#
|
332
|
+
# @example Multiple records with one-column primary key
|
333
|
+
# # find the blogs with subdomain 'cassandra' and 'postgres'
|
334
|
+
# Blog.find(['cassandra', 'postgres'])
|
335
|
+
#
|
336
|
+
# @example One record with two-column primary key
|
337
|
+
# # find the post instance with blog subdomain 'cassandra' and
|
338
|
+
# # permalink 'my-post'
|
339
|
+
# Post.find('cassandra', 'my-post')
|
340
|
+
#
|
341
|
+
# @example Multiple records with two-column primary key
|
342
|
+
# # find the post instances with blog subdomain cassandra and
|
343
|
+
# # permalinks 'my-post' and 'my-new-post'
|
344
|
+
# Post.find('cassandra', ['my-post', 'my-new-post']
|
345
|
+
#
|
346
|
+
def find(*keys)
|
347
|
+
return super if block_given?
|
348
|
+
keys = [keys] if almost_fully_specified? && keys.many?
|
349
|
+
records = traverse(*keys).assert_fully_specified!.load!
|
350
|
+
force_array = keys.any? { |value| value.is_a?(Array) }
|
351
|
+
force_array ? Array.wrap(records) : records
|
334
352
|
end
|
335
353
|
|
336
354
|
#
|
@@ -472,6 +490,8 @@ module Cequel
|
|
472
490
|
def count
|
473
491
|
data_set.count
|
474
492
|
end
|
493
|
+
alias_method :length, :count
|
494
|
+
alias_method :size, :count
|
475
495
|
|
476
496
|
#
|
477
497
|
# Enumerate over the records in this record set
|
@@ -594,6 +614,13 @@ module Cequel
|
|
594
614
|
end
|
595
615
|
end
|
596
616
|
|
617
|
+
# @private
|
618
|
+
def assert_fully_specified!
|
619
|
+
raise ArgumentError,
|
620
|
+
"Missing key component(s) " \
|
621
|
+
"#{unscoped_key_names.join(', ')}"
|
622
|
+
end
|
623
|
+
|
597
624
|
def_delegators :entries, :inspect
|
598
625
|
|
599
626
|
# @private
|
@@ -635,6 +662,16 @@ module Cequel
|
|
635
662
|
end
|
636
663
|
end
|
637
664
|
|
665
|
+
def traverse(*keys)
|
666
|
+
keys.reduce(self) do |record_set, key_value|
|
667
|
+
if key_value.is_a?(Array)
|
668
|
+
record_set.values_at(*key_value)
|
669
|
+
else
|
670
|
+
record_set[key_value]
|
671
|
+
end
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
638
675
|
def scoped_key_names
|
639
676
|
scoped_key_columns.map { |column| column.name }
|
640
677
|
end
|
@@ -671,6 +708,10 @@ module Cequel
|
|
671
708
|
scoped_key_values.length == target_class.key_columns.length
|
672
709
|
end
|
673
710
|
|
711
|
+
def almost_fully_specified?
|
712
|
+
scoped_key_values.length == target_class.key_columns.length - 1
|
713
|
+
end
|
714
|
+
|
674
715
|
def partition_specified?
|
675
716
|
scoped_key_values.length >= target_class.partition_key_columns.length
|
676
717
|
end
|
data/lib/cequel/record/scoped.rb
CHANGED
data/lib/cequel/version.rb
CHANGED
@@ -138,6 +138,57 @@ describe Cequel::Record::Associations do
|
|
138
138
|
blog.posts(true).map(&:title).should == ['Post 1', 'Post 2']
|
139
139
|
end
|
140
140
|
|
141
|
+
it 'should support #find with key' do
|
142
|
+
blog.posts.find(posts.first.id).should == posts.first
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should support #find with block' do
|
146
|
+
blog.posts.find { |post| post.title.include?('1') }.should == posts[1]
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should support #select with block' do
|
150
|
+
blog.posts.select { |post| !post.title.include?('2') }
|
151
|
+
.should == posts.first(2)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should support #select with arguments' do
|
155
|
+
expect { blog.posts.select(:title).first.id }
|
156
|
+
.to raise_error(Cequel::Record::MissingAttributeError)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should load #first directly from the database if unloaded' do
|
160
|
+
blog.posts.first.title
|
161
|
+
blog.posts.should_not be_loaded
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should read #first from loaded collection' do
|
165
|
+
blog.posts.entries
|
166
|
+
disallow_queries!
|
167
|
+
blog.posts.first.title.should == 'Post 0'
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should always query the database for #count' do
|
171
|
+
blog.posts.entries
|
172
|
+
posts.first.destroy
|
173
|
+
blog.posts.count.should == 2
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should always load the records for #length' do
|
177
|
+
blog.posts.length.should == 3
|
178
|
+
blog.posts.should be_loaded
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should count from database for #size if unloaded' do
|
182
|
+
blog.posts.size.should == 3
|
183
|
+
blog.posts.should_not be_loaded
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should count records in memory for #size if loaded' do
|
187
|
+
blog.posts.entries
|
188
|
+
disallow_queries!
|
189
|
+
blog.posts.size.should == 3
|
190
|
+
end
|
191
|
+
|
141
192
|
it "does not allow invalid :dependent options" do
|
142
193
|
expect {
|
143
194
|
Post.class_eval do
|
@@ -27,6 +27,11 @@ describe Cequel::Record::Persistence do
|
|
27
27
|
end.tap(&:save)
|
28
28
|
end
|
29
29
|
|
30
|
+
describe 'new record' do
|
31
|
+
specify { Blog.new.should_not be_persisted }
|
32
|
+
specify { Blog.new.should be_transient }
|
33
|
+
end
|
34
|
+
|
30
35
|
describe '#save' do
|
31
36
|
context 'on create' do
|
32
37
|
it 'should save row to database' do
|
@@ -129,11 +129,21 @@ describe Cequel::Record::RecordSet do
|
|
129
129
|
|
130
130
|
it { should be_persisted }
|
131
131
|
it { should_not be_transient }
|
132
|
-
specify { Blog.new.should_not be_persisted }
|
133
|
-
specify { Blog.new.should be_transient }
|
134
132
|
|
135
133
|
it 'should cast argument to correct type' do
|
136
|
-
Blog.find('blog-0'.force_encoding('ASCII-8BIT')).should
|
134
|
+
Blog.find('blog-0'.force_encoding('ASCII-8BIT')).should == blogs.first
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should return multiple results as an array from vararg keys' do
|
138
|
+
Blog.find('blog-0', 'blog-1').should == blogs.first(2)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should return multiple results as an array from array of keys' do
|
142
|
+
Blog.find(['blog-0', 'blog-1']).should == blogs.first(2)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should return result in an array from one-element array of keys' do
|
146
|
+
Blog.find(['blog-1']).should == [blogs[1]]
|
137
147
|
end
|
138
148
|
|
139
149
|
it 'should raise RecordNotFound if bad argument passed' do
|
@@ -164,6 +174,23 @@ describe Cequel::Record::RecordSet do
|
|
164
174
|
expect { Post['cequel'].find('bogus')}.
|
165
175
|
to raise_error(Cequel::Record::RecordNotFound)
|
166
176
|
end
|
177
|
+
|
178
|
+
it 'should take vararg of values for single key' do
|
179
|
+
Post.find('cassandra', 'cequel0').should == posts.first
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should take multiple values for key' do
|
183
|
+
Post.find('cassandra', ['cequel0', 'cequel1']).should == posts.first(2)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should use Enumerable#find if block given' do
|
187
|
+
Post['cassandra'].find { |post| post.title.include?('1') }
|
188
|
+
.should == posts[1]
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should raise error if not enough key values specified' do
|
192
|
+
expect { Post.find('cassandra') }.to raise_error(ArgumentError)
|
193
|
+
end
|
167
194
|
end
|
168
195
|
end
|
169
196
|
|
@@ -335,6 +362,9 @@ describe Cequel::Record::RecordSet do
|
|
335
362
|
end
|
336
363
|
end
|
337
364
|
|
365
|
+
describe '#find' do
|
366
|
+
end
|
367
|
+
|
338
368
|
describe '#[]' do
|
339
369
|
it 'should return partial collection' do
|
340
370
|
Post['cassandra'].find_each(:batch_size => 2).map(&:title).
|
data/spec/support/helpers.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mat Brown
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2014-02-
|
15
|
+
date: 2014-02-02 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: activesupport
|
@@ -176,6 +176,7 @@ files:
|
|
176
176
|
- lib/cequel/record/callbacks.rb
|
177
177
|
- lib/cequel/record/collection.rb
|
178
178
|
- lib/cequel/record/configuration_generator.rb
|
179
|
+
- lib/cequel/record/conversion.rb
|
179
180
|
- lib/cequel/record/data_set_builder.rb
|
180
181
|
- lib/cequel/record/dirty.rb
|
181
182
|
- lib/cequel/record/errors.rb
|