cequel 1.0.0.rc2 → 1.0.0.rc3
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.
- 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
|