computed_model 0.1.0 → 0.1.1
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/.yardopts +1 -0
- data/CHANGELOG.md +5 -0
- data/README.md +3 -2
- data/lib/computed_model.rb +110 -12
- data/lib/computed_model/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fbb65b996358c1bad2d9f03cde8c2fa0e326341d5ef6964a273370d111bc90f
|
4
|
+
data.tar.gz: 26869abf1e1ddf74b3c94e836d192379fdc1159328b9c260c14a8968b95ec9fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcc03d655e79ef7941b1fc765f9dc56c698aa1afa832c9dac43b409b7a104332a77d9d87ff288146bcb348597af225cc666320eac51d543805cd8d0c39f92a43
|
7
|
+
data.tar.gz: e0677afa15a0c1e0b33ce94e2543b9101a985ef2a9ce852c62e1bf69de54321f3fa825c38332a7e3f22810ac90f0beb8c4900ca8b4d509ed6607872028b5bb98
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -12,7 +12,7 @@ ActiveRecord and remote server (such as ActiveResource).
|
|
12
12
|
Add this line to your application's Gemfile:
|
13
13
|
|
14
14
|
```ruby
|
15
|
-
gem 'computed_model'
|
15
|
+
gem 'computed_model', '~> 0.1.1'
|
16
16
|
```
|
17
17
|
|
18
18
|
And then execute:
|
@@ -115,6 +115,7 @@ users = User.list([1, 2, 3], with: [:name, :name_with_playing_music, :premium_us
|
|
115
115
|
This library is distributed under MIT license.
|
116
116
|
|
117
117
|
Copyright (c) 2020 Masaki Hara
|
118
|
+
|
118
119
|
Copyright (c) 2020 Wantedly, Inc.
|
119
120
|
|
120
121
|
## Development
|
@@ -125,4 +126,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
125
126
|
|
126
127
|
## Contributing
|
127
128
|
|
128
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
129
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/wantedly/computed_model.
|
data/lib/computed_model.rb
CHANGED
@@ -3,19 +3,71 @@
|
|
3
3
|
require "computed_model/version"
|
4
4
|
require 'set'
|
5
5
|
|
6
|
+
# A mixin for batch-loadable compound models.
|
7
|
+
#
|
8
|
+
# @example typical structure of a computed model
|
9
|
+
# class User
|
10
|
+
# include ComputedModel
|
11
|
+
#
|
12
|
+
# attr_reader :id
|
13
|
+
# def initialize(id)
|
14
|
+
# @id = id
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# define_loader do ... end
|
18
|
+
#
|
19
|
+
# dependency :foo, :bar
|
20
|
+
# computed def something ... end
|
21
|
+
# end
|
6
22
|
module ComputedModel
|
23
|
+
# An error raised when you tried to read from a loaded/computed attribute,
|
24
|
+
# but that attribute isn't loaded by the batch loader.
|
7
25
|
class NotLoaded < StandardError; end
|
8
26
|
|
27
|
+
# A return value from {ComputedModel::ClassMethods#computing_plan}.
|
9
28
|
Plan = Struct.new(:load_order, :subdeps_hash)
|
10
29
|
|
30
|
+
# A set of class methods for {ComputedModel}. Automatically included to the
|
31
|
+
# singleton class when you include {ComputedModel}.
|
11
32
|
module ClassMethods
|
12
|
-
#
|
33
|
+
# Declares the dependency of a computed attribute. See {#computed} too.
|
34
|
+
#
|
35
|
+
# @param deps [Array<Symbol, Hash{Symbol=>Array}>]
|
36
|
+
# Dependency description. If a symbol `:foo` is given,
|
37
|
+
# it's interpreted as `{ foo: [] }`.
|
38
|
+
# When the same symbol occurs multiple times, the array is concatenated.
|
39
|
+
# The contents of the array (called "sub-dependency") is treated opaquely
|
40
|
+
# by the `computed_model` gem. It is up to the user to design the format
|
41
|
+
# of sub-dependencies.
|
42
|
+
# @return [void]
|
43
|
+
#
|
44
|
+
# @example declaring dependencies
|
45
|
+
# dependency :user, :user_external_resource
|
46
|
+
# computed def something
|
47
|
+
# # Use user and user_external_resource ...
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# @example declaring dependencies with sub-dependencies
|
51
|
+
# dependency user: [:user_names, :premium], user_external_resource: [:received_stars]
|
52
|
+
# computed def something
|
53
|
+
# # Use user and user_external_resource ...
|
54
|
+
# end
|
13
55
|
def dependency(*deps)
|
14
56
|
@__computed_model_next_dependency ||= []
|
15
57
|
@__computed_model_next_dependency.push(*deps)
|
16
58
|
end
|
17
59
|
|
18
|
-
#
|
60
|
+
# Declares a computed attribute. See {#dependency} too.
|
61
|
+
#
|
62
|
+
# @param meth_name [Symbol] a method name to promote to a computed attribute.
|
63
|
+
# Typically used in the form of `computed def ...`.
|
64
|
+
# @return [Symbol] passes through the argument.
|
65
|
+
#
|
66
|
+
# @example define a field which is calculated from loaded models
|
67
|
+
# dependency :user, :user_external_resource
|
68
|
+
# computed def something
|
69
|
+
# # Use user and user_external_resource ...
|
70
|
+
# end
|
19
71
|
def computed(meth_name)
|
20
72
|
var_name = :"@#{meth_name}"
|
21
73
|
meth_name_orig = :"#{meth_name}_orig"
|
@@ -43,12 +95,25 @@ module ComputedModel
|
|
43
95
|
meth_name
|
44
96
|
end
|
45
97
|
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# @param
|
98
|
+
# A shorthand for simple computed attributes.
|
99
|
+
#
|
100
|
+
# Use {#computed} for more complex definition.
|
101
|
+
#
|
102
|
+
# @param methods [Array<Symbol>] method names to delegate
|
103
|
+
# @param to [Symbol] which attribute to delegate the methods to.
|
104
|
+
# This parameter is used for the dependency declaration too.
|
105
|
+
# @param allow_nil [nil, Boolean] If `true`,
|
106
|
+
# nil receivers are is ignored, and nil is returned instead.
|
107
|
+
# @param prefix [nil, Symbol] A prefix for the delegating method name.
|
108
|
+
# @param include_subdeps [nil, Boolean] If `true`,
|
109
|
+
# sub-dependencies are also included.
|
51
110
|
# @return [void]
|
111
|
+
#
|
112
|
+
# @example delegate name from raw_user
|
113
|
+
# delegate_dependency :name, to: :raw_user
|
114
|
+
#
|
115
|
+
# @example delegate name from raw_user, but expose as user_name
|
116
|
+
# delegate_dependency :name, to: :raw_user, prefix: :user
|
52
117
|
def delegate_dependency(*methods, to:, allow_nil: nil, prefix: nil, include_subdeps: nil)
|
53
118
|
method_prefix = prefix ? "#{prefix_}" : ""
|
54
119
|
methods.each do |meth_name|
|
@@ -71,10 +136,30 @@ module ComputedModel
|
|
71
136
|
end
|
72
137
|
end
|
73
138
|
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
139
|
+
# Declares a loaded attribute. See {#dependency} too.
|
140
|
+
#
|
141
|
+
# `define_loader :foo do ... end` generates a reader `foo` and a writer `foo=`.
|
142
|
+
# The writer is only meant to be used in the loader.
|
143
|
+
#
|
144
|
+
# The responsibility of loader is to call `foo=` for all the given objects,
|
145
|
+
# or set `computed_model_error` otherwise.
|
146
|
+
#
|
147
|
+
# @param meth_name [Symbol] the name of the loaded attribute.
|
148
|
+
# @return [void]
|
149
|
+
# @yield [objects, **options]
|
150
|
+
# @yieldparam objects [Array] The objects to preload the attribute into.
|
151
|
+
# @yieldparam options [Hash] A verbatim copy of what is passed to {#bulk_load_and_compute}.
|
77
152
|
# @yieldreturn [void]
|
153
|
+
#
|
154
|
+
# @example define a loader for ActiveRecord-based models
|
155
|
+
# define_loader :raw_user do |users, subdeps, **options|
|
156
|
+
# user_ids = users.map(&:id)
|
157
|
+
# raw_users = RawUser.where(id: user_ids).preload(subdeps).index_by(&:id)
|
158
|
+
# users.each do |user|
|
159
|
+
# # Even if it doesn't exist, you must explicitly assign nil to the field.
|
160
|
+
# user.raw_user = raw_users[user.id]
|
161
|
+
# end
|
162
|
+
# end
|
78
163
|
def define_loader(meth_name, &block)
|
79
164
|
raise ArgumentError, "No block given" unless block
|
80
165
|
|
@@ -89,9 +174,15 @@ module ComputedModel
|
|
89
174
|
attr_writer meth_name
|
90
175
|
end
|
91
176
|
|
92
|
-
#
|
93
|
-
#
|
177
|
+
# The core routine for batch-loading.
|
178
|
+
#
|
179
|
+
# @param objs [Array] The objects to preload attributes into.
|
180
|
+
# @param deps [Array<Symbol, Hash{Symbol=>Array}>] A set of dependencies.
|
181
|
+
# @param options [Hash] An arbitrary hash to pass to loaders
|
182
|
+
# defined by {#define_loader}.
|
183
|
+
# @return [void]
|
94
184
|
def bulk_load_and_compute(objs, deps, **options)
|
185
|
+
objs = objs.dup
|
95
186
|
plan = computing_plan(deps)
|
96
187
|
plan.load_order.each do |dep_name|
|
97
188
|
if @__computed_model_dependencies.key?(dep_name)
|
@@ -103,6 +194,7 @@ module ComputedModel
|
|
103
194
|
else
|
104
195
|
raise "No dependency info for #{self}##{dep_name}"
|
105
196
|
end
|
197
|
+
objs.reject! { |obj| !obj.computed_model_error.nil? }
|
106
198
|
end
|
107
199
|
end
|
108
200
|
|
@@ -168,6 +260,12 @@ module ComputedModel
|
|
168
260
|
normalized
|
169
261
|
end
|
170
262
|
|
263
|
+
# An error field to prevent {ComputedModel::ClassMethods#bulk_load_and_compute}
|
264
|
+
# from loading remaining attributes.
|
265
|
+
#
|
266
|
+
# @return [StandardError]
|
267
|
+
attr_accessor :computed_model_error
|
268
|
+
|
171
269
|
def self.included(klass)
|
172
270
|
super
|
173
271
|
klass.extend ClassMethods
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: computed_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masaki Hara
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- ".gitignore"
|
71
71
|
- ".rspec"
|
72
72
|
- ".travis.yml"
|
73
|
+
- ".yardopts"
|
73
74
|
- CHANGELOG.md
|
74
75
|
- Gemfile
|
75
76
|
- README.md
|