ii_finder 1.1.2 → 2.1.0

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
  SHA256:
3
- metadata.gz: 1e833c71166437a7e1f0881641850c3b2bc6f36f9c7827a2e18d0c63ba9d3e4b
4
- data.tar.gz: 6f653a027ee5947a6c7bbcd0ce3f76999513f334b79ebd5a8b12dc907961ea2f
3
+ metadata.gz: e9c42e9402b1850eb69b928c7ea83e978e3ccbe3b3356eb4e5b1d24edf9424ce
4
+ data.tar.gz: d22e81c3f0c1bd32b9637d8722b5c6007ae5bf2c27c9b880c2c3b90e88f23363
5
5
  SHA512:
6
- metadata.gz: 1b861518b33b016ccc5795cd04c6c1677a159efd43eeb023bb00ae068b00cb2ca2820c0bf35c58b136cf6ee8392995b87b4a4d5e6e8bbfc5e09f1ab7e16a422c
7
- data.tar.gz: 443dc16e940192156b4af6af1747d9f81166455870ed50215a361c61b9e0535b6ae60e14a0824ae989be6cb141e844235cec5cfbb2b210e4923694b9e792cd73
6
+ metadata.gz: d5d8a31523c9cfa994a683c51723bbb62a1ca09891f1653ccc7f4af8a0b4a28a6927983886c504b86bfbb22e331f50e103f5378b5931e60e4111c49219e8670e
7
+ data.tar.gz: 1c0770784cc663a8bcba3c99b1d0af1439d29c23b8b5a885f7cc34a5c06136972879a9ce8d48d619ca4b6de80639f76c4eb4d51749597a619032f4d1324b54ed
@@ -4,21 +4,29 @@ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  test:
7
- runs-on: ubuntu-18.04
7
+ runs-on: ubuntu-20.04
8
8
  strategy:
9
9
  fail-fast: false
10
10
  matrix:
11
- ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
12
- gemfile: ['rails50', 'rails51', 'rails52', 'rails60', 'rails61']
11
+ ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0']
12
+ gemfile: ['rails50', 'rails51', 'rails52', 'rails60', 'rails61', 'rails70']
13
13
  exclude:
14
14
  - ruby: 2.3
15
15
  gemfile: rails60
16
16
  - ruby: 2.3
17
17
  gemfile: rails61
18
+ - ruby: 2.3
19
+ gemfile: rails70
18
20
  - ruby: 2.4
19
21
  gemfile: rails60
20
22
  - ruby: 2.4
21
23
  gemfile: rails61
24
+ - ruby: 2.4
25
+ gemfile: rails70
26
+ - ruby: 2.5
27
+ gemfile: rails70
28
+ - ruby: 2.6
29
+ gemfile: rails70
22
30
  - ruby: 3.0
23
31
  gemfile: rails50
24
32
  - ruby: 3.0
data/CHANGELOG.md CHANGED
@@ -1,8 +1,28 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 2.1.0
4
+
5
+ * Add traversal config.
6
+ * Add callbacks for `call_all`.
7
+ * Add calling instrumentation.
8
+ * Clear class cache when reloaded.
9
+ * Bump coactive version to 0.2.
10
+
11
+ ## 2.0.1
12
+
13
+ * Return relation after callbacks called.
14
+
15
+ ## 2.0.0
16
+
17
+ * Replace chain feature with coactive.
18
+
19
+ ## 1.2.0
20
+
21
+ * Add chain feature.
22
+
3
23
  ## 1.1.2
4
24
 
5
- Try fetch prior to calling instance method.
25
+ * Try fetch prior to calling instance method.
6
26
 
7
27
  ## 1.1.1
8
28
 
data/README.md CHANGED
@@ -24,14 +24,14 @@ Then execute:
24
24
  Prepare model:
25
25
 
26
26
  ```ruby
27
- class User < ActiveRecord::Base
27
+ class Item < ActiveRecord::Base
28
28
  end
29
29
  ```
30
30
 
31
31
  Prepare finder:
32
32
 
33
33
  ```ruby
34
- class UsersFinder < IIFinder::Base
34
+ class ItemsFinder < IIFinder::Base
35
35
  parameters :name
36
36
 
37
37
  def name(value)
@@ -43,15 +43,15 @@ end
43
43
  Use finder as follows:
44
44
 
45
45
  ```ruby
46
- UsersFinder.call(name: 'NAME').to_sql
47
- #=> SELECT "users".* FROM "users" WHERE "users"."name" = 'NAME'
46
+ ItemsFinder.call(name: 'NAME').to_sql
47
+ #=> SELECT "items".* FROM "items" WHERE "items"."name" = 'NAME'
48
48
  ```
49
49
 
50
50
  You can also specify relation as first argument:
51
51
 
52
52
  ```ruby
53
- UsersFinder.call(User.where(id: [1, 2, 3]), name: 'NAME').to_sql
54
- #=> SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2, 3) AND "users"."name" = 'NAME'
53
+ ItemsFinder.call(Item.where(id: [1, 2, 3]), name: 'NAME').to_sql
54
+ #=> SELECT "items".* FROM "items" WHERE "items"."id" IN (1, 2, 3) AND "items"."name" = 'NAME'
55
55
  ```
56
56
 
57
57
  ### Finder
@@ -61,7 +61,7 @@ Finder method will not be called when the value of parameter is blank.
61
61
  If you want to receive such value, set `allow_blank` as follows:
62
62
 
63
63
  ```ruby
64
- class UsersFinder < IIFinder::Base
64
+ class ItemsFinder < IIFinder::Base
65
65
  parameters :name, allow_blank: true
66
66
 
67
67
  def name(value)
@@ -69,14 +69,14 @@ class UsersFinder < IIFinder::Base
69
69
  end
70
70
  end
71
71
 
72
- UsersFinder.call(name: '').to_sql
73
- #=> SELECT "users".* FROM "users" WHERE "users"."name" = ''
72
+ ItemsFinder.call(name: '').to_sql
73
+ #=> SELECT "items".* FROM "items" WHERE "items"."name" = ''
74
74
  ```
75
75
 
76
76
  Finder has following attributes:
77
77
 
78
78
  ```ruby
79
- class UsersFinder < IIFinder::Base
79
+ class ItemsFinder < IIFinder::Base
80
80
  parameters :name
81
81
 
82
82
  def name(value)
@@ -87,10 +87,10 @@ class UsersFinder < IIFinder::Base
87
87
  end
88
88
  end
89
89
 
90
- UsersFinder.call(name: 'NAME')
91
- #=> relation: #<User::ActiveRecord_Relation:
90
+ ItemsFinder.call(name: 'NAME')
91
+ #=> relation: #<Item::ActiveRecord_Relation:
92
92
  # criteria: {:name=>'NAME'}
93
- # model: User
93
+ # model: Item
94
94
  # table: #<Arel::Table ...>
95
95
  ```
96
96
 
@@ -102,7 +102,7 @@ IIFinder.configure do |config|
102
102
  config.merge_relation = false
103
103
  end
104
104
 
105
- class UsersFinder < IIFinder::Base
105
+ class ItemsFinder < IIFinder::Base
106
106
  parameters :name
107
107
 
108
108
  def name(value)
@@ -110,8 +110,8 @@ class UsersFinder < IIFinder::Base
110
110
  end
111
111
  end
112
112
 
113
- UsersFinder.call(name: 'NAME').to_sql
114
- #=> SELECT "users".* FROM "users" WHERE "users"."name" = 'NAME'
113
+ ItemsFinder.call(name: 'NAME').to_sql
114
+ #=> SELECT "items".* FROM "items" WHERE "items"."name" = 'NAME'
115
115
  ```
116
116
 
117
117
  #### Callbacks
@@ -125,7 +125,7 @@ Following callbacks are available.
125
125
  For example:
126
126
 
127
127
  ```ruby
128
- class UsersFinder < IIFinder::Base
128
+ class ItemsFinder < IIFinder::Base
129
129
  after_call :default_order
130
130
 
131
131
  def default_order
@@ -133,14 +133,45 @@ class UsersFinder < IIFinder::Base
133
133
  end
134
134
  end
135
135
 
136
- UsersFinder.call.to_sql
137
- #=> SELECT "users".* FROM "users" ORDER BY "users"."id" DESC
136
+ ItemsFinder.call.to_sql
137
+ #=> SELECT "items".* FROM "items" ORDER BY "items"."id" DESC
138
138
  ```
139
139
 
140
140
  Note that finder does not handle the return value of callback.
141
141
  When you want to update `@relation` in the callback,
142
142
  reassign `@relation` or use methods like `where!` or `order!`.
143
143
 
144
+ #### Coactors
145
+
146
+ You can chain multiple finders by using `coact`. For example:
147
+
148
+ ```ruby
149
+ class NameFinder < IIFinder::Base
150
+ parameters :name
151
+
152
+ def name(value)
153
+ @relation.where(name: value)
154
+ end
155
+ end
156
+
157
+ class AgeFinder < IIFinder::Base
158
+ parameters :age
159
+
160
+ def age(value)
161
+ @relation.where(age: value)
162
+ end
163
+ end
164
+
165
+ class ItemsFinder < IIFinder::Base
166
+ coact NameFinder, AgeFinder
167
+ end
168
+
169
+ ItemsFinder.call(Item.all, name: 'name', age: 10).to_sql
170
+ #=> SELECT "items".* FROM "items" WHERE "items"."name" = 'name' AND "items"."age" = 10
171
+ ```
172
+
173
+ See [coactive](https://github.com/kanety/coactive) for more `coact` examples:
174
+
144
175
  ### Lookup for model
145
176
 
146
177
  Finder lookups related model by its class name when the first argument of `call` is not relation.
@@ -148,30 +179,30 @@ So the name of finder class should be composed of the name of model class.
148
179
  For example:
149
180
 
150
181
  ```ruby
151
- class User < ActiveRecord::Base
182
+ class Item < ActiveRecord::Base
152
183
  end
153
184
 
154
- class UsersFinder < IIFinder::Base
185
+ class ItemsFinder < IIFinder::Base
155
186
  end
156
187
 
157
- IIFinder::Base.lookup(UsersFinder)
158
- #=> User
188
+ IIFinder::Base.lookup(ItemsFinder)
189
+ #=> Item
159
190
  ```
160
191
 
161
192
  Note that superclass of finder is also looked up until related model is found.
162
193
 
163
194
  ```ruby
164
- class User < ActiveRecord::Base
195
+ class Item < ActiveRecord::Base
165
196
  end
166
197
 
167
- class UsersFinder < IIFinder::Base
198
+ class ItemsFinder < IIFinder::Base
168
199
  end
169
200
 
170
- class InheritedUsersFinder < UsersFinder
201
+ class InheritedItemsFinder < ItemsFinder
171
202
  end
172
203
 
173
- IIFinder::Base.lookup(InheritedUsersFinder)
174
- #=> User
204
+ IIFinder::Base.lookup(InheritedItemsFinder)
205
+ #=> Item
175
206
  ```
176
207
 
177
208
  ### Scope for model
@@ -179,12 +210,12 @@ IIFinder::Base.lookup(InheritedUsersFinder)
179
210
  In case you want to call finder from model, include `IIFinder::Scope` into model as follows:
180
211
 
181
212
  ```ruby
182
- class User < ActiveRecord::Base
213
+ class Item < ActiveRecord::Base
183
214
  include IIFinder::Scope
184
215
  end
185
216
 
186
- User.finder_scope(name: 'NAME').to_sql
187
- #=> SELECT "users".* FROM "users" WHERE "users"."name" = 'NAME'
217
+ Item.finder_scope(name: 'NAME').to_sql
218
+ #=> SELECT "items".* FROM "items" WHERE "items"."name" = 'NAME'
188
219
  ```
189
220
 
190
221
  ### Logging
@@ -199,7 +230,9 @@ IIFinder::LogSubscriber.attach_to :ii_finder
199
230
  This subscriber will write logs in debug mode as the following example:
200
231
 
201
232
  ```
202
- Called UsersFinder with {:id=>1} (Duration: 9.9ms, Allocations: 915)
233
+ Calling ItemsFinder with {:id=>1}
234
+ ...
235
+ Called ItemsFinder (Duration: 9.9ms, Allocations: 915)
203
236
  ```
204
237
 
205
238
  ## Contributing
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 7.0.0"
4
+
5
+ gemspec path: "../"
data/ii_finder.gemspec CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_dependency "activesupport", ">= 5.0"
21
+ spec.add_dependency "coactive", ">= 0.2"
21
22
 
22
23
  spec.add_development_dependency "rails", ">= 5.0"
23
24
  spec.add_development_dependency "sqlite3"
@@ -5,6 +5,9 @@ require_relative 'parameters'
5
5
  require_relative 'callbacks'
6
6
  require_relative 'instrumentation'
7
7
  require_relative 'lookup'
8
+ require_relative 'context'
9
+ require_relative 'contextualizer'
10
+ require_relative 'coactors'
8
11
 
9
12
  module IIFinder
10
13
  class Base
@@ -13,5 +16,7 @@ module IIFinder
13
16
  include Callbacks
14
17
  include Instrumentation
15
18
  include Lookup
19
+ include Contextualizer
20
+ include Coactors
16
21
  end
17
22
  end
@@ -6,9 +6,16 @@ module IIFinder
6
6
  include ActiveSupport::Callbacks
7
7
 
8
8
  included do
9
+ define_callbacks :all
9
10
  define_callbacks :call
10
11
  end
11
12
 
13
+ def call_all
14
+ run_callbacks :all do
15
+ super
16
+ end
17
+ end
18
+
12
19
  def call
13
20
  run_callbacks :call do
14
21
  super
@@ -16,6 +23,18 @@ module IIFinder
16
23
  end
17
24
 
18
25
  class_methods do
26
+ def before_all(*args, &block)
27
+ set_callback(:all, :before, *args, &block)
28
+ end
29
+
30
+ def after_all(*args, &block)
31
+ set_callback(:all, :after, *args, &block)
32
+ end
33
+
34
+ def around_all(*args, &block)
35
+ set_callback(:all, :around, *args, &block)
36
+ end
37
+
19
38
  def before_call(*args, &block)
20
39
  set_callback(:call, :before, *args, &block)
21
40
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIFinder
4
+ module Coactors
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include Coactive::Base
9
+
10
+ configure_coactive do |config|
11
+ config.load_paths = ['app/finders']
12
+ config.class_suffix = 'Finder'
13
+ config.use_cache = true
14
+ config.lookup_superclass_until = ['ActiveRecord::Base', 'ActiveModel::Base']
15
+ end
16
+
17
+ class << self
18
+ alias_method :chain, :coact
19
+ end
20
+ end
21
+ end
22
+ end
@@ -6,7 +6,8 @@ module IIFinder
6
6
 
7
7
  self.data = {
8
8
  lookup_cache: true,
9
- merge_relation: true
9
+ merge_relation: true,
10
+ traversal: :postorder,
10
11
  }
11
12
 
12
13
  data.keys.each do |key|
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIFinder
4
+ class Context < Coactive::Context
5
+ def to_s
6
+ "#<#{self.class} model=#{@_data[:model]} criteria=#{@_data[:criteria].to_s.truncate(300)}>"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIFinder
4
+ module Contextualizer
5
+ extend ActiveSupport::Concern
6
+ include Coactive::Contextualizer
7
+
8
+ def call
9
+ contextualize do
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
@@ -3,34 +3,49 @@
3
3
  module IIFinder
4
4
  module Core
5
5
  extend ActiveSupport::Concern
6
+ include Coactive::Initializer
6
7
 
7
8
  included do
8
- attr_reader :relation, :criteria, :model, :table
9
+ self.context_class = IIFinder::Context
10
+ context :relation, output: true
11
+ context :criteria, :model, :table
9
12
  end
10
13
 
11
14
  def initialize(*args)
12
- if args.size == 0 || args.size == 1
13
- @model = self.class.lookup
14
- raise IIFinder::Error.new("could not find model for #{self.class}") unless @model
15
- @relation = @model.all
16
- @criteria = args[0] || {}
15
+ if args[0].is_a?(self.class.context_class)
16
+ super(args[0])
17
17
  else
18
- @relation = args[0]
19
- @criteria = args[1]
20
- @model = @relation.klass
18
+ relation, criteria = Core.resolve_args(self, *args)
19
+ model = relation.klass
20
+ table = model.arel_table if model.respond_to?(:arel_table)
21
+ super(relation: relation, criteria: criteria, model: model, table: table)
22
+ end
23
+ end
24
+
25
+ def call_all
26
+ planned = case IIFinder.config.traversal
27
+ when :preorder
28
+ [self] + coactors
29
+ when :postorder
30
+ coactors + [self]
31
+ when :inorder
32
+ planned = coactors.in_groups(2, false)
33
+ planned[0] + [self] + planned[1]
34
+ end
35
+
36
+ planned.each do |finder|
37
+ relation = finder == self ? call : finder.call(@context)
38
+ @context.relation = @context.relation.merge(relation) if relation.respond_to?(:merge)
21
39
  end
22
- @table = @model.arel_table if @model.respond_to?(:arel_table)
23
40
  end
24
41
 
25
42
  def call
26
43
  self.class._parameters.each do |param|
27
44
  value = fetch_criteria(param.name)
28
45
  if value.present? || param.allow_blank?
29
- call_method(param.name, value)
46
+ merge_relation!(send(param.name, value))
30
47
  end
31
48
  end
32
-
33
- @relation
34
49
  end
35
50
 
36
51
  def fetch_criteria(name)
@@ -41,17 +56,28 @@ module IIFinder
41
56
  end
42
57
  end
43
58
 
44
- def call_method(name, value)
45
- result = send(name, value)
46
-
47
- if result.respond_to?(:merge) && Config.merge_relation
48
- @relation = @relation.merge(result)
59
+ def merge_relation!(relation)
60
+ if relation.respond_to?(:merge) && Config.merge_relation
61
+ @relation = @relation.merge(relation)
49
62
  end
50
63
  end
51
64
 
52
65
  class_methods do
53
66
  def call(*args)
54
- new(*args).call
67
+ finder = new(*args).tap(&:call_all)
68
+ finder.context.relation
69
+ end
70
+ end
71
+
72
+ class << self
73
+ def resolve_args(finder, *args)
74
+ if args.size == 0 || args.size == 1
75
+ model = finder.class.lookup
76
+ raise IIFinder::Error.new("could not find model for #{finder.class}") unless model
77
+ return model.all, args[0] || {}
78
+ else
79
+ return args[0], args[1]
80
+ end
55
81
  end
56
82
  end
57
83
  end
@@ -4,6 +4,11 @@ module IIFinder
4
4
  module Instrumentation
5
5
  extend ActiveSupport::Concern
6
6
 
7
+ def call_all
8
+ ActiveSupport::Notifications.instrument 'calling.ii_finder', finder: self
9
+ super
10
+ end
11
+
7
12
  def call
8
13
  ActiveSupport::Notifications.instrument 'call.ii_finder', finder: self do
9
14
  super
@@ -2,13 +2,22 @@
2
2
 
3
3
  module IIFinder
4
4
  class LogSubscriber < ActiveSupport::LogSubscriber
5
+ def calling(event)
6
+ debug do
7
+ finder = event.payload[:finder]
8
+ "Calling #{finder.class} with #{finder.context}"
9
+ end
10
+ end
11
+
5
12
  def call(event)
6
13
  debug do
7
14
  finder = event.payload[:finder]
8
- "Called #{finder.class} with #{finder.criteria} (#{additional_log(event)})"
15
+ "Called #{finder.class} (#{additional_log(event)})"
9
16
  end
10
17
  end
11
18
 
19
+ private
20
+
12
21
  def additional_log(event)
13
22
  additions = ["Duration: %.1fms" % event.duration]
14
23
  additions << "Allocations: %d" % event.allocations if event.respond_to?(:allocations)
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIFinder
4
+ class Railtie < Rails::Railtie
5
+ ActiveSupport::Reloader.to_prepare do
6
+ IIFinder::Lookup.cache.clear
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IIFinder
4
- VERSION = '1.1.2'
4
+ VERSION = '2.1.0'
5
5
  end
data/lib/ii_finder.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'active_support'
2
+ require 'coactive'
2
3
 
3
4
  require 'ii_finder/version'
4
5
  require 'ii_finder/config'
@@ -6,6 +7,7 @@ require 'ii_finder/errors'
6
7
  require 'ii_finder/base'
7
8
  require 'ii_finder/scope'
8
9
  require 'ii_finder/log_subscriber'
10
+ require 'ii_finder/railtie' if defined?(Rails)
9
11
 
10
12
  module IIFinder
11
13
  class << self
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ii_finder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoshikazu Kaneta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-25 00:00:00.000000000 Z
11
+ date: 2022-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: coactive
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rails
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -116,11 +130,15 @@ files:
116
130
  - gemfiles/rails52.gemfile
117
131
  - gemfiles/rails60.gemfile
118
132
  - gemfiles/rails61.gemfile
133
+ - gemfiles/rails70.gemfile
119
134
  - ii_finder.gemspec
120
135
  - lib/ii_finder.rb
121
136
  - lib/ii_finder/base.rb
122
137
  - lib/ii_finder/callbacks.rb
138
+ - lib/ii_finder/coactors.rb
123
139
  - lib/ii_finder/config.rb
140
+ - lib/ii_finder/context.rb
141
+ - lib/ii_finder/contextualizer.rb
124
142
  - lib/ii_finder/core.rb
125
143
  - lib/ii_finder/errors.rb
126
144
  - lib/ii_finder/instrumentation.rb
@@ -128,6 +146,7 @@ files:
128
146
  - lib/ii_finder/lookup.rb
129
147
  - lib/ii_finder/parameter.rb
130
148
  - lib/ii_finder/parameters.rb
149
+ - lib/ii_finder/railtie.rb
131
150
  - lib/ii_finder/scope.rb
132
151
  - lib/ii_finder/version.rb
133
152
  homepage: https://github.com/kanety/ii_finder