smart_collection 0.1.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|