smart_collection 0.1.1 → 0.2
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/smart_collection/cache_manager/cache_store.rb +1 -3
- data/lib/smart_collection/cache_manager/table.rb +1 -3
- data/lib/smart_collection/mixin.rb +49 -3
- data/lib/smart_collection/scope_builder.rb +46 -0
- data/lib/smart_collection/version.rb +1 -1
- data/lib/smart_collection.rb +1 -1
- metadata +8 -17
- data/lib/smart_collection/associations/preloader/smart_collection.rb +0 -54
- data/lib/smart_collection/associations/smart_collection_association.rb +0 -75
- data/lib/smart_collection/builder/smart_collection_association.rb +0 -26
- data/lib/smart_collection/reflection/smart_collection_reflection.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e48926b0db15bfc7e6c786278522b7f07df46900
|
4
|
+
data.tar.gz: 6a1daefd7675a7bd97068d4e1298e5737f0273a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d9a7c2649df55b559a0f54517ffbca6a237cad2da28da6a254980fc41fd0eb6bb27db13b5eb7c59251fddfea404f9ad24e06595a364f962264fe147ec315528
|
7
|
+
data.tar.gz: 782412ba48ce5aa99508992857e94490a19602a8b28d546217725cb77b8232875e6b733b191ff09195f889d32919181f3ce3587c167f0b637eefcb770efdc752
|
@@ -17,9 +17,7 @@ module SmartCollection
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def update owner
|
20
|
-
|
21
|
-
|
22
|
-
cache_store.write(cache_key(owner), Marshal.dump(association.uncached_scope.pluck(:id)))
|
20
|
+
cache_store.write(cache_key(owner), Marshal.dump(owner.smart_collection_mixin.uncached_scope(owner).pluck(:id)))
|
23
21
|
owner.update(cache_expires_at: Time.now + expires_in)
|
24
22
|
end
|
25
23
|
|
@@ -25,10 +25,8 @@ module SmartCollection
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def update owner
|
28
|
-
association = owner.association(@config.items_name)
|
29
|
-
|
30
28
|
@cache_model.where(collection_id: owner.id).delete_all
|
31
|
-
@cache_model.connection.execute "INSERT INTO #{@cache_model.table_name} (collection_id, item_id) #{
|
29
|
+
@cache_model.connection.execute "INSERT INTO #{@cache_model.table_name} (collection_id, item_id) #{owner.smart_collection_mixin.uncached_scope(owner).select(owner.id, :id).to_sql}"
|
32
30
|
owner.update_column(:cache_expires_at, Time.now + expires_in)
|
33
31
|
end
|
34
32
|
|
@@ -32,6 +32,14 @@ module SmartCollection
|
|
32
32
|
@raw_config = raw_config
|
33
33
|
end
|
34
34
|
|
35
|
+
def uncached_scope owner
|
36
|
+
SmartCollection::ScopeBuilder.new(owner.rule, @config.item_class).build
|
37
|
+
end
|
38
|
+
|
39
|
+
def cached_scope owner
|
40
|
+
@config.cache_manager.read_scope owner
|
41
|
+
end
|
42
|
+
|
35
43
|
def included base
|
36
44
|
@config = config = SmartCollection::Config.new(@raw_config)
|
37
45
|
|
@@ -40,9 +48,6 @@ module SmartCollection
|
|
40
48
|
reflection_options[:class_name] = config.item_class_name
|
41
49
|
end
|
42
50
|
|
43
|
-
reflection = Builder::SmartCollectionAssociation.build(base, config.items_name, nil, reflection_options)
|
44
|
-
::ActiveRecord::Reflection.add_reflection base, config.items_name, reflection
|
45
|
-
|
46
51
|
base.include(InstanceMethods)
|
47
52
|
base.extend(ClassMethods)
|
48
53
|
|
@@ -50,6 +55,47 @@ module SmartCollection
|
|
50
55
|
config.cache_manager = cache_class.new(model: base, config: config)
|
51
56
|
end
|
52
57
|
|
58
|
+
mixin_options = {
|
59
|
+
name: config.items_name,
|
60
|
+
scope: -> owner {
|
61
|
+
if cache_manager = config.cache_manager
|
62
|
+
unless cache_manager.cache_exists? owner
|
63
|
+
owner.update_cache
|
64
|
+
end
|
65
|
+
cached_scope(owner)
|
66
|
+
else
|
67
|
+
uncached_scope(owner)
|
68
|
+
end
|
69
|
+
},
|
70
|
+
type: :collection
|
71
|
+
}
|
72
|
+
|
73
|
+
case
|
74
|
+
when cache_class == SmartCollection::CacheManager::CacheStore
|
75
|
+
mixin_options[:preloader] = -> owners {
|
76
|
+
owners.reject(&:cache_exists?).each(&:update_cache)
|
77
|
+
loaded = config.cache_manager.read_multi(owners)
|
78
|
+
records = config.item_class.where(id: loaded.values.flatten.uniq).map{|x| [x.id, x]}.to_h
|
79
|
+
loaded.map do |owner, ids|
|
80
|
+
[owner, ids.map{|x| records[x]}]
|
81
|
+
end.to_h
|
82
|
+
}
|
83
|
+
when cache_class == SmartCollection::CacheManager::Table
|
84
|
+
cached_name = "cached_#{config.items_name}".to_sym
|
85
|
+
mixin_options[:preloader] = -> owners {
|
86
|
+
owners.reject(&:cache_exists?).each(&:update_cache)
|
87
|
+
Associationist.preload(owners, cached_items: config.item_name)
|
88
|
+
owners.map do |owner|
|
89
|
+
[owner, owner.cached_items.map{|item| item.send(config.item_name)}]
|
90
|
+
end.to_h
|
91
|
+
}
|
92
|
+
else
|
93
|
+
mixin_options[:preloader] = -> _ {
|
94
|
+
raise RuntimeError, "Turn on cache to enable preloading."
|
95
|
+
}
|
96
|
+
end
|
97
|
+
base.include Associationist::Mixin.new(mixin_options)
|
98
|
+
|
53
99
|
base.validates_with SmartCollection::Validator
|
54
100
|
|
55
101
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SmartCollection
|
2
|
+
class ScopeBuilder
|
3
|
+
def initialize rule, klass
|
4
|
+
@rule = rule
|
5
|
+
@klass = klass
|
6
|
+
@klass_hash = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
rule_to_bulk_queries @rule
|
11
|
+
bulk_load
|
12
|
+
rule_to_scope @rule
|
13
|
+
end
|
14
|
+
|
15
|
+
def bulk_load
|
16
|
+
@klass_hash = @klass_hash.map do |klass_name, ids|
|
17
|
+
[klass_name, Object.const_get(klass_name).where(id: ids).map{|x| [x.id, x]}.to_h]
|
18
|
+
end.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def rule_to_bulk_queries rule
|
22
|
+
case
|
23
|
+
when arr = (rule['or'] || rule['and'])
|
24
|
+
arr.each{|x| rule_to_bulk_queries x}
|
25
|
+
when assoc = rule['association']
|
26
|
+
ids = @klass_hash[assoc['class_name']] ||= []
|
27
|
+
ids << assoc['id']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def rule_to_scope rule
|
32
|
+
case
|
33
|
+
when ors = rule['or']
|
34
|
+
ors.map{|x| rule_to_scope x}.inject(:or)
|
35
|
+
when ands = rule['and']
|
36
|
+
ands.map{|x| rule_to_scope x}.inject(:merge)
|
37
|
+
when assoc = rule['association']
|
38
|
+
@klass_hash[assoc['class_name']][assoc['id']].association(assoc['source']).scope
|
39
|
+
when cond = rule['condition']
|
40
|
+
scope = @klass
|
41
|
+
scope = scope.joins(cond['joins'].to_sym) if cond['joins']
|
42
|
+
scope.where(cond['where'])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/smart_collection.rb
CHANGED
metadata
CHANGED
@@ -1,35 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_collection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CicholGricenchos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: associationist
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '5.0'
|
20
|
-
- - "<"
|
17
|
+
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
19
|
+
version: 0.1.0
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- - "
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '5.0'
|
30
|
-
- - "<"
|
24
|
+
- - "~>"
|
31
25
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
26
|
+
version: 0.1.0
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: pry
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,15 +88,12 @@ extensions: []
|
|
94
88
|
extra_rdoc_files: []
|
95
89
|
files:
|
96
90
|
- lib/smart_collection.rb
|
97
|
-
- lib/smart_collection/associations/preloader/smart_collection.rb
|
98
|
-
- lib/smart_collection/associations/smart_collection_association.rb
|
99
|
-
- lib/smart_collection/builder/smart_collection_association.rb
|
100
91
|
- lib/smart_collection/cache_manager.rb
|
101
92
|
- lib/smart_collection/cache_manager/cache_store.rb
|
102
93
|
- lib/smart_collection/cache_manager/table.rb
|
103
94
|
- lib/smart_collection/config.rb
|
104
95
|
- lib/smart_collection/mixin.rb
|
105
|
-
- lib/smart_collection/
|
96
|
+
- lib/smart_collection/scope_builder.rb
|
106
97
|
- lib/smart_collection/validator.rb
|
107
98
|
- lib/smart_collection/version.rb
|
108
99
|
homepage: https://github.com/CicholGricenchos/smart_collection
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module SmartCollection
|
2
|
-
module Associations
|
3
|
-
module Preloader
|
4
|
-
class SmartCollectionCachedByTable < ActiveRecord::Associations::Preloader::HasManyThrough
|
5
|
-
def through_reflection
|
6
|
-
reflection.active_record.reflect_on_association(:cached_items)
|
7
|
-
end
|
8
|
-
|
9
|
-
def source_reflection
|
10
|
-
association_name = reflection.options[:smart_collection].item_name
|
11
|
-
through_reflection.klass.reflect_on_association(association_name)
|
12
|
-
end
|
13
|
-
|
14
|
-
def associated_records_by_owner preloader
|
15
|
-
owners.reject(&:cache_exists?).each(&:update_cache)
|
16
|
-
super
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class SmartCollectionCachedByCacheStore < ActiveRecord::Associations::Preloader::CollectionAssociation
|
21
|
-
def associated_records_by_owner preloader
|
22
|
-
owners.reject(&:cache_exists?).each(&:update_cache)
|
23
|
-
config = reflection.options[:smart_collection]
|
24
|
-
loaded = config.cache_manager.read_multi(owners)
|
25
|
-
records = config.item_class.where(id: loaded.values.flatten.uniq).map{|x| [x.id, x]}.to_h
|
26
|
-
loaded.map do |owner, ids|
|
27
|
-
[owner, ids.map{|x| records[x]}]
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
module ActiveRecordPreloaderPatch
|
36
|
-
def preloader_for(reflection, owners, rhs_klass)
|
37
|
-
config = reflection.options[:smart_collection]
|
38
|
-
if config
|
39
|
-
case config.cache_manager
|
40
|
-
when SmartCollection::CacheManager::CacheStore
|
41
|
-
SmartCollection::Associations::Preloader::SmartCollectionCachedByCacheStore
|
42
|
-
when SmartCollection::CacheManager::Table
|
43
|
-
SmartCollection::Associations::Preloader::SmartCollectionCachedByTable
|
44
|
-
else
|
45
|
-
raise RuntimeError, "Turn on cache to enable preloading."
|
46
|
-
end
|
47
|
-
else
|
48
|
-
super
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
ActiveRecord::Associations::Preloader.prepend ActiveRecordPreloaderPatch
|
54
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module SmartCollection
|
2
|
-
module Associations
|
3
|
-
class SmartCollectionAssociation < ::ActiveRecord::Associations::HasManyAssociation
|
4
|
-
|
5
|
-
class ScopeBuilder
|
6
|
-
def initialize rule, klass
|
7
|
-
@rule = rule
|
8
|
-
@klass = klass
|
9
|
-
@klass_hash = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def build
|
13
|
-
rule_to_bulk_queries @rule
|
14
|
-
bulk_load
|
15
|
-
rule_to_scope @rule
|
16
|
-
end
|
17
|
-
|
18
|
-
def bulk_load
|
19
|
-
@klass_hash = @klass_hash.map do |klass_name, ids|
|
20
|
-
[klass_name, Object.const_get(klass_name).where(id: ids).map{|x| [x.id, x]}.to_h]
|
21
|
-
end.to_h
|
22
|
-
end
|
23
|
-
|
24
|
-
def rule_to_bulk_queries rule
|
25
|
-
case
|
26
|
-
when arr = (rule['or'] || rule['and'])
|
27
|
-
arr.each{|x| rule_to_bulk_queries x}
|
28
|
-
when assoc = rule['association']
|
29
|
-
ids = @klass_hash[assoc['class_name']] ||= []
|
30
|
-
ids << assoc['id']
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def rule_to_scope rule
|
35
|
-
case
|
36
|
-
when ors = rule['or']
|
37
|
-
ors.map{|x| rule_to_scope x}.inject(:or)
|
38
|
-
when ands = rule['and']
|
39
|
-
ands.map{|x| rule_to_scope x}.inject(:merge)
|
40
|
-
when assoc = rule['association']
|
41
|
-
@klass_hash[assoc['class_name']][assoc['id']].association(assoc['source']).scope
|
42
|
-
when cond = rule['condition']
|
43
|
-
scope = @klass
|
44
|
-
scope = scope.joins(cond['joins'].to_sym) if cond['joins']
|
45
|
-
scope.where(cond['where'])
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def association_scope
|
51
|
-
if cache_manager = reflection.options[:smart_collection].cache_manager
|
52
|
-
unless cache_manager.cache_exists? owner
|
53
|
-
owner.update_cache
|
54
|
-
end
|
55
|
-
cached_scope
|
56
|
-
else
|
57
|
-
uncached_scope
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def skip_statement_cache?
|
62
|
-
true
|
63
|
-
end
|
64
|
-
|
65
|
-
def uncached_scope
|
66
|
-
ScopeBuilder.new(owner.rule, reflection.klass).build
|
67
|
-
end
|
68
|
-
|
69
|
-
def cached_scope
|
70
|
-
reflection.options[:smart_collection].cache_manager.read_scope owner
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module SmartCollection
|
2
|
-
module Builder
|
3
|
-
class SmartCollectionAssociation < ::ActiveRecord::Associations::Builder::CollectionAssociation
|
4
|
-
def self.macro
|
5
|
-
:has_many
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.valid_options(options)
|
9
|
-
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors, :smart_collection]
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.valid_dependent_options
|
13
|
-
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.create_reflection(model, name, scope, options, extension = nil)
|
17
|
-
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
|
18
|
-
|
19
|
-
validate_options(options)
|
20
|
-
|
21
|
-
scope = build_scope(scope, extension)
|
22
|
-
Reflection::SmartCollectionReflection.new(name, scope, options, model)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module SmartCollection
|
2
|
-
module Reflection
|
3
|
-
class SmartCollectionReflection < ::ActiveRecord::Reflection::HasManyReflection
|
4
|
-
def association_class
|
5
|
-
Associations::SmartCollectionAssociation
|
6
|
-
end
|
7
|
-
|
8
|
-
def check_eager_loadable!
|
9
|
-
unless options[:smart_collection].cache_manager.instance_of? SmartCollection::CacheManager::Table
|
10
|
-
raise RuntimeError, 'eager_load is only supported when using table for cache.'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def chain
|
15
|
-
items_name = options[:smart_collection].items_name
|
16
|
-
active_record.reflect_on_association("cached_#{items_name}".to_sym).chain
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|