memoized 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 30eefb403ce7300b9b943f9a6a946c56ac21fe619323ace0c06e63ca11198eab
4
+ data.tar.gz: 5e0e371e3bb72db04dec7fd23789a4c88f1acfffcf49c898529c5edcd60594a4
5
+ SHA512:
6
+ metadata.gz: 7402174132f312c8e7f45389fd18fcae350aeffc006587c0d20921b4ead346ef794c90634e4c6c367891050edbc6d5c0982a4203e822dc9c3cda9b91dfd9ee76
7
+ data.tar.gz: 6a0676fcac1c743821fde6b3010d9d53a0792097775593fb41a7c912ca45ddb2ef68276cae9996aa96cc7ee92274c1c62e0e0af2442b02d4d69858442f90e99c
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.1
data/.travis.yml ADDED
@@ -0,0 +1,26 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.1.8
5
+ - 2.3.8
6
+ - 2.4.5
7
+ - 2.5.3
8
+ - 2.6.1
9
+
10
+ gemfile:
11
+ - Gemfile
12
+
13
+ script: bundle exec rspec spec
14
+
15
+ sudo: false
16
+
17
+ cache: bundler
18
+
19
+ notifications:
20
+ email:
21
+ - fail@makandra.de
22
+
23
+ install:
24
+ # Replace default Travis CI bundler script with a version that doesn't
25
+ # explode when lockfile doesn't match recently bumped version
26
+ - bundle install --no-deployment --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
data/CHANGELOG.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.1
2
+ * Bump version to avoid conflict with old memoizer gem
3
+ == 1.0.0
4
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ memoized (1.0.3)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rake (10.4.2)
11
+ rspec (3.5.0)
12
+ rspec-core (~> 3.5.0)
13
+ rspec-expectations (~> 3.5.0)
14
+ rspec-mocks (~> 3.5.0)
15
+ rspec-core (3.5.4)
16
+ rspec-support (~> 3.5.0)
17
+ rspec-expectations (3.5.0)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.5.0)
20
+ rspec-mocks (3.5.0)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.5.0)
23
+ rspec-support (3.5.0)
24
+ timecop (0.8.1)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ memoized!
31
+ rake (~> 10.4.2)
32
+ rspec (~> 3.5.0)
33
+ timecop (~> 0.8.0)
34
+
35
+ BUNDLED WITH
36
+ 1.17.2
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ The MIT LICENSE
2
+
3
+ Original gem "Memoizer" copyright (c) 2011 Wegowise Inc.
4
+ Changes in this fork "Memoized" Copyright (c) 2019 makandra GmbH.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ [![Build Status](https://travis-ci.org/makandra/memoized.svg?branch=master)](https://travis-ci.org/makandra/memoized)
2
+
3
+ # Memoized
4
+
5
+ Memoized will memoize the results of your methods. It acts much like
6
+ `ActiveSupport::Memoizable` without all of that freezing business. The API for
7
+ unmemoizing is also a bit more explicit.
8
+
9
+ ## Install
10
+
11
+ ```
12
+ $ gem install memoized
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ To define a memoized instance method, use `memoized def``:
18
+
19
+ ```ruby
20
+ class A
21
+ include Memoized
22
+
23
+ memoize def hello
24
+ 'hello!'
25
+ end
26
+ end
27
+ ```
28
+
29
+ You may also `memoize` one or more methods after they have been defined:
30
+
31
+ ```ruby
32
+ class B
33
+ include Memoized
34
+
35
+ def hello
36
+ 'hello!'
37
+ end
38
+
39
+ def goodbye
40
+ 'goodbye :('
41
+ end
42
+
43
+ memoize :hello, :goodbye
44
+ end
45
+ ```
46
+
47
+ Memoizing class methods works the same way:
48
+
49
+ ```ruby
50
+ class C
51
+ class << self
52
+ include Memoized
53
+
54
+ memoize def hello
55
+ 'hello!'
56
+ end
57
+ end
58
+ end
59
+ ```
60
+
61
+
62
+ To unmemoize a specific method:
63
+
64
+ ```ruby
65
+ instance = A.new
66
+ instance.hello # the hello method is now memoized
67
+ instance.unmemoize(:hello) # the hello method is no longer memoized
68
+ instance.hello # the hello method is run again and re-memoized
69
+ ```
70
+
71
+
72
+ To unmemoize all methods for an instance:
73
+
74
+ ```ruby
75
+ instance = B.new
76
+ instance.hello # the hello method is now memoized
77
+ instance.goodbye # the goodbye method is now memoized
78
+ instance.unmemoize_all # neither hello nor goodbye are memoized anymore
79
+ ```
80
+
81
+
82
+ ## License
83
+
84
+ See [LICENSE.txt](https://github.com/makandra/memoized/blob/master/LICENSE.txt)
85
+
86
+
87
+ ## Credits
88
+
89
+ - This gem is a fork of [Memoizer](https://github.com/wegowise/memoizer) by [Wegowise](https://www.wegowise.com/).
90
+ - Changes in this fork by [makandra](https://makandra.com).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'rake'
2
+ require 'bundler/gem_tasks'
data/lib/memoized.rb ADDED
@@ -0,0 +1,85 @@
1
+ module Memoized
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ base.send :include, InstanceMethods
5
+ end
6
+
7
+ def self.safe_name(method_name)
8
+ method_name.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang').to_sym
9
+ end
10
+
11
+ def self.ivar_name(method_name)
12
+ :"@_memoized_#{self.safe_name(method_name)}"
13
+ end
14
+
15
+ module ClassMethods
16
+ def memoize(*method_names)
17
+ method_names.each do |method_name|
18
+ memoized_ivar_name = Memoized.ivar_name(method_name)
19
+ unmemoized_method = "_unmemoized_#{method_name}"
20
+
21
+ alias_method unmemoized_method, method_name
22
+
23
+ arity = instance_method(unmemoized_method).arity
24
+
25
+ case arity
26
+ when 0
27
+ module_eval(<<-RUBY)
28
+ def #{method_name}()
29
+ #{memoized_ivar_name} ||= [#{unmemoized_method}()]
30
+ #{memoized_ivar_name}.first
31
+ end
32
+ RUBY
33
+
34
+ when -1
35
+ module_eval(<<-RUBY)
36
+ def #{method_name}(*args)
37
+ #{memoized_ivar_name} ||= {}
38
+ if #{memoized_ivar_name}.has_key?(args)
39
+ #{memoized_ivar_name}[args]
40
+ else
41
+ #{memoized_ivar_name}[args] = #{unmemoized_method}(*args)
42
+ end
43
+ end
44
+ RUBY
45
+
46
+ else
47
+ arg_names = (0..(arity - 1)).map { |i| "arg#{i}" }
48
+ args_ruby = arg_names.join(', ')
49
+
50
+ module_eval(<<-RUBY)
51
+ def #{method_name}(#{args_ruby})
52
+ args = [#{args_ruby}]
53
+ #{memoized_ivar_name} ||= {}
54
+ if #{memoized_ivar_name}.has_key?(args)
55
+ #{memoized_ivar_name}[args]
56
+ else
57
+ #{memoized_ivar_name}[args] = #{unmemoized_method}(#{args_ruby})
58
+ end
59
+ end
60
+ RUBY
61
+ end
62
+
63
+ if self.private_method_defined?(unmemoized_method)
64
+ private method_name
65
+ elsif self.protected_method_defined?(unmemoized_method)
66
+ protected method_name
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ module InstanceMethods
73
+ def unmemoize(method_name)
74
+ self.instance_variable_set(Memoized.ivar_name(method_name), nil)
75
+ end
76
+
77
+ def unmemoize_all
78
+ (methods + private_methods + protected_methods).each do |method|
79
+ if method.to_s =~ /^_unmemoized_(.*)/
80
+ unmemoize($1)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module Memoized
2
+ VERSION = '1.0.0'
3
+ end
data/memoized.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "memoized/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "memoized"
6
+ s.version = Memoized::VERSION
7
+ s.authors = ["Barun Singh", "Henning Koch"]
8
+ s.homepage = "https://github.com/makandra/memoizer"
9
+ s.summary = "Memoized caches the results of your method calls"
10
+ s.description = s.summary
11
+
12
+ s.files = `git ls-files`.split("\n").reject { |path| File.lstat(path).symlink? }
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").reject { |path| File.lstat(path).symlink? }
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ s.require_paths = ["lib"]
16
+
17
+ s.license = 'MIT'
18
+
19
+ s.add_development_dependency('rake', '~> 10.4.2')
20
+ s.add_development_dependency('rspec', '~> 3.5.0')
21
+ s.add_development_dependency('timecop', '~> 0.8.0')
22
+ end
@@ -0,0 +1,248 @@
1
+ class MemoizedSpecClass
2
+ include Memoized
3
+ def no_params() Date.today; end
4
+ def with_params?(ndays, an_array) Date.today + ndays + an_array.length; end
5
+ def returning_nil!() Date.today; nil; end
6
+ memoize :no_params, :with_params?, :returning_nil!
7
+ end
8
+ class Beepbop < MemoizedSpecClass; end
9
+
10
+
11
+ describe Memoized do
12
+ let(:today) { Date.today }
13
+
14
+ describe '.memoize' do
15
+ let(:object) { MemoizedSpecClass.new }
16
+ let(:tomorrow) { Date.today + 1 }
17
+
18
+ context "for a method with no params" do
19
+ it "stores memoized value" do
20
+ Timecop.freeze(today)
21
+ expect(object.no_params).to eq(today)
22
+ Timecop.freeze(tomorrow)
23
+ expect(object.no_params).to eq(today)
24
+ end
25
+ end
26
+
27
+ context "for a method with params (and ending in ?)" do
28
+ it "stores memoized value" do
29
+ Timecop.freeze(today)
30
+ expect(object.with_params?(1, [1,2])).to eq(today + 3)
31
+ Timecop.freeze(tomorrow)
32
+ expect(object.with_params?(1, [1,2])).to eq(today + 3)
33
+ end
34
+ it "does not confuse one set of inputs for another" do
35
+ Timecop.freeze(today)
36
+ expect(object.with_params?(1, [1,2])).to eq(today + 3)
37
+ expect(object.with_params?(2, [1,2])).to eq(today + 4)
38
+ Timecop.freeze(tomorrow)
39
+ expect(object.with_params?(1, [1,2])).to eq(today + 3)
40
+ expect(object.with_params?(1, [2,2])).to eq(today + 4)
41
+ end
42
+ end
43
+
44
+ context "for a method that returns nil (and ends in !)" do
45
+ it "stores the memoized value" do
46
+ object.returning_nil!
47
+ allow(Date).to receive(:today).and_raise(ArgumentError)
48
+ expect(object.returning_nil!).to be_nil
49
+ end
50
+ end
51
+
52
+ context "for subclasses" do
53
+ let(:object) { Beepbop.new }
54
+ it "still memoizes things" do
55
+ Timecop.freeze(today)
56
+ expect(object.no_params).to eq(today)
57
+ Timecop.freeze(tomorrow)
58
+ expect(object.no_params).to eq(today)
59
+ end
60
+ end
61
+
62
+ context 'for private methods' do
63
+ class Beirut < MemoizedSpecClass
64
+ def foo() bar; end
65
+ private
66
+ def bar() Date.today; end
67
+ memoize :bar
68
+ end
69
+ let(:object) { Beirut.new }
70
+
71
+ it "respects the privacy of the memoized method" do
72
+ expect(Beirut.private_method_defined?(:bar)).to be_truthy
73
+ expect(Beirut.private_method_defined?(:_unmemoized_bar)).to be_truthy
74
+ end
75
+
76
+ it "memoizes things" do
77
+ Timecop.freeze(today)
78
+ expect(object.foo).to eq(today)
79
+ Timecop.freeze(today + 1)
80
+ expect(object.foo).to eq(today)
81
+ end
82
+ end
83
+
84
+ context 'for protected methods' do
85
+ class Wonka < MemoizedSpecClass
86
+ def foo() bar; end
87
+ protected
88
+ def bar() Date.today; end
89
+ memoize :bar
90
+ end
91
+ let(:object) { Wonka.new }
92
+
93
+ it "respects the privacy of the memoized method" do
94
+ expect(Wonka.protected_method_defined?(:bar)).to be_truthy
95
+ expect(Wonka.protected_method_defined?(:_unmemoized_bar)).to be_truthy
96
+ end
97
+
98
+ it "memoizes things" do
99
+ Timecop.freeze(today)
100
+ expect(object.foo).to eq(today)
101
+ Timecop.freeze(today + 1)
102
+ expect(object.foo).to eq(today)
103
+ end
104
+ end
105
+
106
+ context 'for methods with an arity of 0' do
107
+ class Arity0 < MemoizedSpecClass
108
+ def foo()
109
+ end
110
+
111
+ memoize :foo
112
+ end
113
+
114
+ it 'creates a memoized method with an arity of 0' do
115
+ expect(Arity0.instance_method(:foo).arity).to eq(0)
116
+ end
117
+
118
+ end
119
+
120
+ context 'for methods with an arity of 2' do
121
+ class Arity2 < MemoizedSpecClass
122
+ def foo(a, b)
123
+ end
124
+
125
+ memoize :foo
126
+ end
127
+
128
+ it 'creates a memoized method with an arity of 2' do
129
+ expect(Arity2.instance_method(:foo).arity).to eq(2)
130
+ end
131
+
132
+ end
133
+
134
+ context 'for methods with splat args' do
135
+ class AritySplat < MemoizedSpecClass
136
+ def foo(*args)
137
+ end
138
+
139
+ memoize :foo
140
+ end
141
+
142
+ it 'creates a memoized method with an arity of -1' do
143
+ expect(AritySplat.instance_method(:foo).arity).to eq(-1)
144
+ end
145
+
146
+ end
147
+
148
+ end
149
+
150
+
151
+ describe 'instance methods' do
152
+ class MemoizedSpecClass
153
+ def today() Date.today; end
154
+ def plus_ndays(ndays) Date.today + ndays; end
155
+ memoize :today, :plus_ndays
156
+ end
157
+
158
+ let(:object) { MemoizedSpecClass.new }
159
+ before do
160
+ Timecop.freeze(today)
161
+ expect(object.today).to eq(today)
162
+ expect(object.plus_ndays(1)).to eq(today + 1)
163
+ expect(object.plus_ndays(3)).to eq(today + 3)
164
+ end
165
+
166
+ describe '#unmemoize' do
167
+ context "for a method with no arguments" do
168
+ it "clears the memoized value so it can be rememoized" do
169
+ Timecop.freeze(today + 1)
170
+ expect(object.today).to eq(today)
171
+
172
+ object.unmemoize(:today)
173
+ expect(object.today).to eq(today + 1)
174
+
175
+ Timecop.freeze(today + 2)
176
+ expect(object.today).to eq(today + 1)
177
+ end
178
+ end
179
+
180
+ context "for a method with arguments" do
181
+ it "unmemoizes for all inupts" do
182
+ Timecop.freeze(today + 1)
183
+ expect(object.plus_ndays(1)).to eq(today + 1)
184
+ expect(object.plus_ndays(3)).to eq(today + 3)
185
+
186
+ object.unmemoize(:plus_ndays)
187
+ expect(object.plus_ndays(1)).to eq(today + 2)
188
+ expect(object.plus_ndays(3)).to eq(today + 4)
189
+
190
+ Timecop.freeze(today + 2)
191
+ expect(object.plus_ndays(1)).to eq(today + 2)
192
+ expect(object.plus_ndays(3)).to eq(today + 4)
193
+ end
194
+ end
195
+
196
+ it "only affects the method specified" do
197
+ Timecop.freeze(today + 1)
198
+ expect(object.today).to eq(today)
199
+
200
+ object.unmemoize(:plus_ndays)
201
+ expect(object.today).to eq(today)
202
+
203
+ object.unmemoize(:today)
204
+ expect(object.today).to eq(today + 1)
205
+ end
206
+
207
+ context "for subclasses" do
208
+ let(:object) { Beepbop.new }
209
+ it "clears the memoized value" do
210
+ Timecop.freeze(today + 1)
211
+ expect(object.today).to eq(today)
212
+
213
+ object.unmemoize(:today)
214
+ expect(object.today).to eq(today + 1)
215
+
216
+ Timecop.freeze(today + 2)
217
+ expect(object.today).to eq(today + 1)
218
+ end
219
+ end
220
+ end
221
+
222
+ describe '#unmemoize_all' do
223
+ shared_examples_for "unmemoizing methods" do
224
+ it "clears all memoized values" do
225
+ Timecop.freeze(today + 1)
226
+ expect(object.today).to eq(today)
227
+ expect(object.plus_ndays(1)).to eq(today + 1)
228
+ expect(object.plus_ndays(3)).to eq(today + 3)
229
+
230
+ object.unmemoize_all
231
+
232
+ expect(object.today).to eq(today + 1)
233
+ expect(object.plus_ndays(1)).to eq(today + 2)
234
+ expect(object.plus_ndays(3)).to eq(today + 4)
235
+ end
236
+ end
237
+
238
+ it_should_behave_like "unmemoizing methods"
239
+
240
+ context "for subclasses" do
241
+ let(:object) { Beepbop.new }
242
+ it_should_behave_like "unmemoizing methods"
243
+ end
244
+ end
245
+
246
+ end
247
+
248
+ end
@@ -0,0 +1,9 @@
1
+ require 'date'
2
+ require 'timecop'
3
+ require 'memoized'
4
+
5
+ RSpec.configure do |config|
6
+ config.warnings = true
7
+ config.order = :random
8
+ Kernel.srand config.seed
9
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memoized
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Barun Singh
8
+ - Henning Koch
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-02-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 10.4.2
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 10.4.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 3.5.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 3.5.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: timecop
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 0.8.0
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 0.8.0
56
+ description: Memoized caches the results of your method calls
57
+ email:
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".ruby-version"
65
+ - ".travis.yml"
66
+ - CHANGELOG.txt
67
+ - Gemfile
68
+ - Gemfile.lock
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - lib/memoized.rb
73
+ - lib/memoized/version.rb
74
+ - memoized.gemspec
75
+ - spec/memoized_spec.rb
76
+ - spec/spec_helper.rb
77
+ homepage: https://github.com/makandra/memoizer
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.0.1
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Memoized caches the results of your method calls
100
+ test_files: []