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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 192790cbc51085d35ee324b7c2e61936c56285ed
4
- data.tar.gz: 500efab14683ba051367ea4bcfa72757e8ba4761
3
+ metadata.gz: f6a1d8087898abadc4007581ab714424b15e797d
4
+ data.tar.gz: 4bd38a8e29b7e491e37b9337b7ee88d6c61042a0
5
5
  SHA512:
6
- metadata.gz: 8cc2ca473a2f4d6ccee65824c69940ace08445dfdb4ff36b174f3fbbd591f153d5d3eebe72ec6bf3cb532aa51fcaac2522034342b313d00ea1a5c30d6a615076
7
- data.tar.gz: 1f71fdfa21ecc5ab76419307eb2a23301d26cef231fbe08618c5d29e36f8bb09035eb27877b8b8ab637b120f7142d7e1611814c3cd5bfe99a6fdf32c64009b06
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
- begin
28
- klass = self.name.demodulize.singularize.constantize
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
- ActiveRecord::Collection::COLLECTABLES[name]
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 { |wh| collection.where!(wh) }
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
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Collections
3
- VERSION = '0.0.23'
3
+ VERSION = '0.0.24'
4
4
  end
5
5
  end
@@ -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 'should return the same records as a standard relation' do
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 'should default to 500' do
48
+ it 'defaults to 500' do
19
49
  expect(ActiveRecord::Collection.default_batch_size).to eql(500)
20
50
  end
21
51
 
22
- it 'should be overridable by extending classes' do
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 'should default to 0' do
58
+ it 'defaults to 0' do
29
59
  expect(ActiveRecord::Collection.batching_threshold).to eql(0)
30
60
  end
31
61
 
32
- it 'should be overridable by extending classes' do
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
@@ -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.23
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-22 00:00:00.000000000 Z
11
+ date: 2015-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord