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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 187bb48a7b917cc0b0f8e068fb48f32a559d9ffb
4
- data.tar.gz: fb246fd8dd0ca7c3cd1a8ed01d57a699dfa9433b
3
+ metadata.gz: e48926b0db15bfc7e6c786278522b7f07df46900
4
+ data.tar.gz: 6a1daefd7675a7bd97068d4e1298e5737f0273a0
5
5
  SHA512:
6
- metadata.gz: de0fdf1c0a01d34314a4e6d6665ef945dbb195ef728519ef8a874f7b4961c9c4547679245ce7eae2b0ca999bccf939ee715404c9c05a8b385ec9b74944a4b3f7
7
- data.tar.gz: 85aa4af37c448c96684b30a9764b677fd62f9de0de6d152cc81501ce40cf2aa7920695e7e29e5407236e2a5e87ad72ff5e02c88293c649b506293124433e07d1
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
- association = owner.association(@config.items_name)
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) #{association.uncached_scope.select(owner.id, :id).to_sql}"
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
@@ -1,3 +1,3 @@
1
1
  module SmartCollection
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2'
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'active_record'
1
+ require 'associationist'
2
2
 
3
3
  Dir.glob("#{File.dirname(__FILE__)}/**/*.rb").each do |file|
4
4
  require file
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.1.1
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-23 00:00:00.000000000 Z
11
+ date: 2019-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
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: '6.0'
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: '6.0'
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/reflection/smart_collection_reflection.rb
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