smart_preloads 0.0.1 → 0.1.0

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: 5203b8b2b5dbd92bc5643f6688a3e73571150d85
4
- data.tar.gz: 504790d10fa6b9e43733d5e8ae012fcf2c8e5c03
3
+ metadata.gz: 18f4e3190dc51950fb7a67eb68be6e2f9d8b86a4
4
+ data.tar.gz: f47c16e997076ce9041616ae243d50974b4a57be
5
5
  SHA512:
6
- metadata.gz: 76ca0adc8537ffcf01e1459185ecd2c0143f65d6b74d060d478c0289a310e35a4befaf9b793758d1ff4ae486c11266eba430bfe38edfdc4d96d378915a646fe1
7
- data.tar.gz: d5d259616f5b62597f3eeeeedfa2c7191b04ccee0238817ca2a8b44897472a985e5f586469a57199859dbc1793110adf4766db857ca36c1900895d8df7c781e7
6
+ metadata.gz: 60e9e6f7c3fbc8da1e845ab31ef6e74ca7fca4bc4ca48dd40d43fb303374abea9426bd43ed9b825fce77243709071c1f151cc44f626b94d89884f579368dad39
7
+ data.tar.gz: b3ca7004e8f80ba53a1cea14985767cd210e93d61df45e9ce4b8c525a4252c6d2057c66e0665663610b4c1780ac4be3b7c14082b2e1d76ad773e16c55c4aa662
data/README.md CHANGED
@@ -5,7 +5,15 @@ when you do not have control over which associations are going to be used by
5
5
  the view, like when you provide users customizable views with
6
6
  [Liquid](https://github.com/chamnap/liquid-rails).
7
7
 
8
- ## How it Works
8
+ ## Installation
9
+
10
+ You can use `gem install smart_preloads` to install it manually or use Bundler:
11
+
12
+ ```ruby
13
+ gem 'smart_preloads'
14
+ ```
15
+
16
+ ## Usage
9
17
 
10
18
  You have to call `smart_preloads` at the end of your association. This will
11
19
  generate a smart list of items that will load associations **if and when**
@@ -39,13 +47,24 @@ end
39
47
  #=> SELECT "categories".* FROM "categories" WHERE "categories"."id" IN (1, 2)
40
48
  ```
41
49
 
42
- ## Installation
50
+ ## How it Works
43
51
 
44
- You can use `gem install smart_preloads` to install it manually or use Bundler:
52
+ In order for it to work, `smart_preloads` has a custom list class
53
+ (`SmartPreloads::List`) and a custom item class for each item in a list
54
+ (`SmartPreloads::Item`).
45
55
 
46
- ```ruby
47
- gem 'smart_preloads'
48
- ```
56
+ The List class allows detecting when the collection
57
+ is really used (iterated) so only then the associations will be detected and
58
+ mokey patched in place. This is needed so whenever a call to an association
59
+ is made it will be loaded, even from calls from within the object itself.
60
+
61
+ The Item class monkey patches all association methods in the objects loaded
62
+ to intercept the calls. The monkey patch is **in place**, not global. Only
63
+ the objects in the `smart_preloads` collection will be monkey patched.
64
+
65
+ Note that when you call `Author.all.smart_preloads.first` you will **not**
66
+ have an instance of `Author`. Instead, you will have an instance of
67
+ `SmartPreloads::Item` that delegates calls to the original `Author` object.
49
68
 
50
69
  ## Contributing
51
70
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.1.0
@@ -3,23 +3,24 @@ module SmartPreloads
3
3
  def initialize(record, loader: nil)
4
4
  super(record)
5
5
  @loader = loader || Loader.new([record])
6
+ _override_associations
7
+ end
6
8
 
7
- record.class.reflections.each do |key, association|
8
- original = method(key)
9
- singleton_class.class_exec do
10
- body = nil
11
- if %i(has_many has_and_belongs_to_many).include?(association.macro)
12
- body = lambda do
13
- List.new(original.call, loader: @loader.nested(key.to_sym))
14
- end
15
- else
16
- body = lambda do
17
- @loader.load(key.to_sym)
18
- original.call
19
- end
9
+ def _override_associations
10
+ __getobj__.class.reflections.each do |key, association|
11
+ body = nil
12
+ loader = @loader
13
+ if %i(has_many has_and_belongs_to_many).include?(association.macro)
14
+ body = lambda do
15
+ List.new(super(), loader: loader.nested(key.to_sym))
16
+ end
17
+ else
18
+ body = lambda do
19
+ loader.load(key.to_sym)
20
+ super()
20
21
  end
21
- define_method(key, &body)
22
22
  end
23
+ __getobj__.define_singleton_method(key, &body)
23
24
  end
24
25
  end
25
26
  end
@@ -9,7 +9,11 @@ module SmartPreloads
9
9
 
10
10
  def each
11
11
  loaded_collection.each do |resource|
12
- yield Item.new(resource, loader: @loader)
12
+ if resource.is_a?(Item)
13
+ yield resource
14
+ else
15
+ yield Item.new(resource, loader: @loader)
16
+ end
13
17
  end
14
18
  end
15
19
 
@@ -21,6 +25,14 @@ module SmartPreloads
21
25
  to_a.size
22
26
  end
23
27
 
28
+ def +(other)
29
+ to_a + other.to_a
30
+ end
31
+
32
+ def [](idx)
33
+ to_a[idx]
34
+ end
35
+
24
36
  protected
25
37
 
26
38
  def loaded_collection
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: smart_preloads 0.0.1 ruby lib
5
+ # stub: smart_preloads 0.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "smart_preloads".freeze
9
- s.version = "0.0.1"
9
+ s.version = "0.1.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Diego Aguir Selzlein".freeze]
14
- s.date = "2017-05-02"
14
+ s.date = "2017-05-26"
15
15
  s.description = "Avoid N + 1 queries without having to worry about it at all!".freeze
16
16
  s.email = "diegoselzlein@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
@@ -12,6 +12,10 @@ class Book < ActiveRecord::Base
12
12
  belongs_to :author
13
13
  belongs_to :category
14
14
  has_many :tags, as: :taggable
15
+
16
+ def author_name
17
+ author.try!(:name)
18
+ end
15
19
  end
16
20
 
17
21
  class Tag < ActiveRecord::Base
@@ -78,4 +82,31 @@ describe 'SmartPreloads' do
78
82
  expect(list.first.books.first.association(:category)).to be_loaded
79
83
  expect(list.last.books.first.association(:category)).to be_loaded
80
84
  end
85
+
86
+ it 'supports concatenation' do
87
+ Book.create!(name: 'Rework')
88
+ Book.create!(name: 'Lean Startup')
89
+ Author.create!(books: [Book.first])
90
+ Author.create!(books: [Book.last])
91
+
92
+ list = Book.all.smart_preloads + Author.all.smart_preloads
93
+
94
+ list[2].books.to_a
95
+ expect(list[3].association(:books)).to be_loaded
96
+
97
+ list.first.author
98
+ expect(list[1].association(:author)).to be_loaded
99
+ end
100
+
101
+ it 'works with internal methods' do
102
+ a1 = Author.create!(name: 'John')
103
+ a2 = Author.create!(name: 'Doe')
104
+ Book.create!(name: 'Rework', author: a1)
105
+ Book.create!(name: 'Lean Startup', author: a2)
106
+
107
+ list = Book.all.smart_preloads
108
+
109
+ list.first.author_name
110
+ expect(list[1].association(:author)).to be_loaded
111
+ end
81
112
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_preloads
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diego Aguir Selzlein
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-02 00:00:00.000000000 Z
11
+ date: 2017-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec