activerecord-collections 0.0.23 → 0.0.24
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/active_record/collection.rb +21 -10
- data/lib/active_record/collections/serialization.rb +7 -1
- data/lib/active_record/collections/serializer.rb +3 -0
- data/lib/active_record/collections/version.rb +1 -1
- data/spec/active_record/collection_spec.rb +65 -6
- data/spec/support/models.rb +17 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6a1d8087898abadc4007581ab714424b15e797d
|
4
|
+
data.tar.gz: 4bd38a8e29b7e491e37b9337b7ee88d6c61042a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a3a224370a7e042c1d1a184db1e38b70e0c93cd1614b1a5ad090d04b585218c9c2bd3a52e553f1432186a558cddcca062a2d119baa34e16e1df62c57090d921
|
7
|
+
data.tar.gz: 39574217740a6c075d46f413232d50020bdeb17a35c2ec88d0a03a0bd3710ce1ddfce0cf7d0bc03c241600e2bad99a0101b08f8c7347d583106165724c87cc72
|
@@ -12,30 +12,41 @@ module ActiveRecord
|
|
12
12
|
COLLECTIONS = []
|
13
13
|
|
14
14
|
class << self
|
15
|
-
attr_reader :collections
|
16
15
|
def inherited(subclass)
|
17
|
-
ActiveRecord::Collection::COLLECTIONS << subclass
|
16
|
+
ActiveRecord::Collection::COLLECTIONS << subclass.name unless ActiveRecord::Collection::COLLECTIONS.include?(subclass.name)
|
17
|
+
# if parent class is not Collection, register the collectable on the class as the closest parent's collectable
|
18
|
+
end
|
19
|
+
|
20
|
+
def collections
|
21
|
+
ActiveRecord::Collection::COLLECTIONS.map(&:constantize)
|
18
22
|
end
|
19
23
|
|
20
24
|
def collectable(klass=nil)
|
21
25
|
unless klass.nil?
|
22
26
|
raise ArgumentError, "The collection model must inherit from ActiveRecord::Base" unless klass.ancestors.include?(ActiveRecord::Base)
|
23
|
-
ActiveRecord::Collection::COLLECTABLES[name] ||= klass
|
27
|
+
ActiveRecord::Collection::COLLECTABLES[name] ||= klass.name
|
24
28
|
end
|
25
29
|
|
26
30
|
if ActiveRecord::Collection::COLLECTABLES[name].nil?
|
27
|
-
|
28
|
-
|
29
|
-
ActiveRecord::Collection::COLLECTABLES[name] = klass if !klass.nil? && klass.ancestors.include?(ActiveRecord::Base)
|
30
|
-
rescue
|
31
|
-
# singularized class doesn't exist
|
32
|
-
end
|
31
|
+
klass = infer_collectable
|
32
|
+
ActiveRecord::Collection::COLLECTABLES[name] = klass.name if !klass.nil? && klass.ancestors.include?(ActiveRecord::Base)
|
33
33
|
end
|
34
34
|
|
35
35
|
raise "Unable to determine a model to use for your collection, please set one with the `collectable` class method" if ActiveRecord::Collection::COLLECTABLES[name].nil? # TODO implement real exceptions
|
36
|
-
|
36
|
+
|
37
|
+
ActiveRecord::Collection::COLLECTABLES[name].constantize
|
37
38
|
end
|
38
39
|
alias_method :model, :collectable
|
40
|
+
|
41
|
+
def infer_collectable(klass=self)
|
42
|
+
singular = klass.name.demodulize.singularize
|
43
|
+
raise "Cannot infer collectable from singular collection" if singular == klass.name.demodulize
|
44
|
+
singular.constantize
|
45
|
+
rescue
|
46
|
+
parent = klass.ancestors[1]
|
47
|
+
return nil if parent.name == 'ActiveRecord::Collection'
|
48
|
+
parent.try(:collectable) || infer_collectable(parent)
|
49
|
+
end
|
39
50
|
end
|
40
51
|
|
41
52
|
def collectable
|
@@ -24,7 +24,13 @@ module ActiveRecord
|
|
24
24
|
collection.includes!(*hash[:includes]) unless hash[:includes].empty?
|
25
25
|
|
26
26
|
wheres = hash[:where].partition { |w| w.is_a?(Hash) }
|
27
|
-
wheres.first.each
|
27
|
+
wheres.first.each do |wh|
|
28
|
+
if wh.keys.first == :not
|
29
|
+
collection.not!(wh[:not])
|
30
|
+
else
|
31
|
+
collection.where!(wh)
|
32
|
+
end
|
33
|
+
end
|
28
34
|
collection.where!(*hash[:bind].map { |b| b[:value] }.unshift(wheres.last.join(" AND ").gsub(/\$\d/,'?'))) unless wheres.last.empty?
|
29
35
|
|
30
36
|
collection.group!(hash[:group]) unless hash[:group].empty?
|
@@ -123,6 +123,9 @@ module ActiveRecord
|
|
123
123
|
serialize_node(node.expr)
|
124
124
|
when Arel::Nodes::And
|
125
125
|
node.children.map { |child| serialize_node(child) }
|
126
|
+
when Arel::Nodes::NotEqual
|
127
|
+
bound = bind.delete_at(bind.find_index { |b| b[:name] == node.left.name.to_s })[:value]
|
128
|
+
{not: {node.left.name.to_sym => bound}}
|
126
129
|
when Arel::Nodes::Or
|
127
130
|
{or: [serialize_node(node.left), serialize_node(node.right)]}
|
128
131
|
else
|
@@ -2,36 +2,95 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe ActiveRecord::Collection do
|
4
4
|
context 'querying' do
|
5
|
-
before(:each) { create(:retailer) }
|
5
|
+
before(:each) { 5.times { create(:retailer) } }
|
6
6
|
|
7
|
-
it '
|
7
|
+
it 'returns the same records as a standard relation' do
|
8
8
|
retailer = Retailer.all.to_a.sample
|
9
9
|
collection = StockedProducts.where(retailer_id: retailer.id)
|
10
10
|
relation = StockedProduct.where(retailer_id: retailer.id)
|
11
11
|
expect(collection.count).to eql(relation.count)
|
12
12
|
expect(collection.pluck(:id).sort).to eql(relation.pluck(:id).sort)
|
13
13
|
end
|
14
|
+
|
15
|
+
describe 'not' do
|
16
|
+
it 'excludes records matching the criteria' do
|
17
|
+
retailer = Retailer.all.to_a.sample
|
18
|
+
collection = StockedProducts.not(retailer_id: retailer.id)
|
19
|
+
relation = StockedProduct.where.not(retailer_id: retailer.id)
|
20
|
+
expect(collection.count).to eql(relation.count)
|
21
|
+
expect(collection.pluck(:id).sort).to eql(relation.pluck(:id).sort)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'to_collection' do
|
26
|
+
it 'reproduces the same result set' do
|
27
|
+
retailer = Retailer.all.to_a.sample
|
28
|
+
product = retailer.stocked_products.sample.product
|
29
|
+
relation = StockedProduct.where.not(retailer_id: retailer.id)
|
30
|
+
collection = StockedProduct.where.not(retailer_id: retailer.id).to_collection
|
31
|
+
expect(collection.count).to eql(relation.count)
|
32
|
+
expect(collection.pluck(:id).sort).to eql(relation.pluck(:id).sort)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'supports multiple parameters' do
|
36
|
+
retailer = Retailer.all.to_a.sample
|
37
|
+
product = retailer.stocked_products.sample.product
|
38
|
+
relation = StockedProduct.where.not(retailer_id: retailer.id, product_id: product.id)
|
39
|
+
collection = StockedProduct.where.not(retailer_id: retailer.id, product_id: product.id).to_collection
|
40
|
+
expect(collection.count).to eql(relation.count)
|
41
|
+
expect(collection.pluck(:id).sort).to eql(relation.pluck(:id).sort)
|
42
|
+
end
|
43
|
+
end
|
14
44
|
end
|
15
45
|
|
16
46
|
context 'batching' do
|
17
47
|
describe 'default_batch_size' do
|
18
|
-
it '
|
48
|
+
it 'defaults to 500' do
|
19
49
|
expect(ActiveRecord::Collection.default_batch_size).to eql(500)
|
20
50
|
end
|
21
51
|
|
22
|
-
it '
|
52
|
+
it 'is overridable by extending classes' do
|
23
53
|
expect(StockedProducts.default_batch_size).to eql(200)
|
24
54
|
end
|
25
55
|
end
|
26
56
|
|
27
57
|
describe 'batching_threshold' do
|
28
|
-
it '
|
58
|
+
it 'defaults to 0' do
|
29
59
|
expect(ActiveRecord::Collection.batching_threshold).to eql(0)
|
30
60
|
end
|
31
61
|
|
32
|
-
it '
|
62
|
+
it 'is overridable by extending classes' do
|
33
63
|
expect(StockedProducts.batching_threshold).to eql(500)
|
34
64
|
end
|
35
65
|
end
|
36
66
|
end
|
67
|
+
|
68
|
+
context 'subclassing' do
|
69
|
+
it 'infers the collectable from the class name' do
|
70
|
+
expect(Retailers.collectable).to eql(Retailer)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'allows setting a collectable' do
|
74
|
+
expect(ProductCollection.collectable).to eql(Product)
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'multiple collections for the same model' do
|
78
|
+
it 'registers all of them as collections for the model' do
|
79
|
+
expect(ActiveRecord::Collection::COLLECTABLES[StockedProducts.name]).to eql('StockedProduct')
|
80
|
+
expect(ActiveRecord::Collection::COLLECTABLES[StockedProductCollection.name]).to eql('StockedProduct')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'a subclass' do
|
85
|
+
it 'registers itself as a collection' do
|
86
|
+
expect(ActiveRecord::Collection.collections).to include(ProductCollection)
|
87
|
+
expect(ActiveRecord::Collection.collections).to include(AnotherProductCollection)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'inherits the parent collectable' do
|
91
|
+
expect(AnotherProductCollection.collectable).to eql(Product)
|
92
|
+
expect(MoreRetailers.collectable).to eql(Retailer)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
37
96
|
end
|
data/spec/support/models.rb
CHANGED
@@ -21,3 +21,20 @@ class StockedProducts < ActiveRecord::Collection
|
|
21
21
|
default_batch_size 200
|
22
22
|
batching_threshold 500
|
23
23
|
end
|
24
|
+
|
25
|
+
class StockedProductCollection < ActiveRecord::Collection
|
26
|
+
collectable StockedProduct
|
27
|
+
end
|
28
|
+
|
29
|
+
class ProductCollection < ActiveRecord::Collection
|
30
|
+
collectable Product
|
31
|
+
end
|
32
|
+
|
33
|
+
class AnotherProductCollection < ProductCollection
|
34
|
+
end
|
35
|
+
|
36
|
+
class Retailers < ActiveRecord::Collection
|
37
|
+
end
|
38
|
+
|
39
|
+
class MoreRetailers < Retailers
|
40
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-collections
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.24
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Rebec
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|