attr_memoized 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AttrMemoized
2
- VERSION = '0.2.2'
4
+ VERSION = '0.3.0'
3
5
  end
metadata CHANGED
@@ -1,59 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attr_memoized
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-05 00:00:00.000000000 Z
11
+ date: 2020-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: codeclimate-test-reporter
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '12'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '12'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: colored2
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec-its
56
+ name: irbtools
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: simplecov
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: irbtools
84
+ name: relaxed-rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: colored2
98
+ name: rspec
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,35 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: codeclimate-test-reporter
112
+ name: rspec-its
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
113
141
  requirement: !ruby/object:Gem::Requirement
114
142
  requirements:
115
143
  - - ">="
@@ -135,10 +163,12 @@ files:
135
163
  - ".gitignore"
136
164
  - ".rspec"
137
165
  - ".rubocop.yml"
166
+ - ".rubocop_todo.yml"
138
167
  - ".travis.yml"
168
+ - ".vscode/settings.json"
139
169
  - Gemfile
140
170
  - LICENSE.txt
141
- - README.md
171
+ - README.adoc
142
172
  - Rakefile
143
173
  - attr_memoized.gemspec
144
174
  - bin/console
@@ -164,8 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
194
  - !ruby/object:Gem::Version
165
195
  version: '0'
166
196
  requirements: []
167
- rubyforge_project:
168
- rubygems_version: 2.6.11
197
+ rubygems_version: 3.1.2
169
198
  signing_key:
170
199
  specification_version: 4
171
200
  summary: Memoize attributes in a thread-safe way. This ruby gem adds a `#attr_memoized`
data/README.md DELETED
@@ -1,170 +0,0 @@
1
- [![Build Status](https://travis-ci.org/kigster/attr_memoized.svg?branch=master)](https://travis-ci.org/kigster/attr_memoized)
2
- [![Code Climate](https://codeclimate.com/github/kigster/attr_memoized/badges/gpa.svg)](https://codeclimate.com/github/kigster/attr_memoized)
3
- [![Test Coverage](https://codeclimate.com/github/kigster/attr_memoized/badges/coverage.svg)](https://codeclimate.com/github/kigster/attr_memoized/coverage)
4
- [![Issue Count](https://codeclimate.com/github/kigster/attr_memoized/badges/issue_count.svg)](https://codeclimate.com/github/kigster/attr_memoized)
5
-
6
- # AttrMemoized
7
-
8
- This is a simple, and yet rather useful **memoization** library, with a specific goal of being **thread-safe** during lazy-loading of attributes. Class method `attr_memoized` automatically generates attribute reader and attribute writer methods. The reader performs a thread-safe lazy-initialization of each attribute. The writer performs a thread-safe assignment. You can disable writer method generation by passing `writer: false` option to `attr_memoized` method.
9
-
10
- Any `attr_memoized` attribute may depend on any number of regular attributes or other `attr_memoized` attributes.
11
-
12
- This gems provides a shorthand syntax for defining lazy-initialized variables as "one-liners", while additionally providing thread-safety guarantees around lazy-initilization of attributes, or attribute assignments.
13
-
14
- #### Caveat
15
-
16
- Note, that if the initialization or assignment returns a "falsey" result (ie, `false` or `nil`), then the attribute will attempt to be re-initialized every time its "reader" method is called. This is not a bug. We treat falsey value as uninitialized by design.
17
-
18
- ## Complete Example
19
-
20
- Below we have a `Configuration` class that has several attributes that are all lazy loaded.
21
-
22
- ```ruby
23
- require 'redis'
24
- # Save config to Redis
25
- r = Redis.new
26
- #=> #<Redis:0x007fbd8d3a4308>
27
- r.set('config_file', '{ "host": "127.0.0.1" }')
28
- #=> OK
29
- r.set('another_file', '{ "host": "google.com" }')
30
- #=> OK
31
- r.get('config_file') #=> { "host": "127.0.0.1" }
32
-
33
- require 'attr_memoized'
34
- module Concurrent
35
- class RedisConfig
36
- include AttrMemoized
37
- # Now there is an instance and a class methods +#mutex+ are defined.
38
- # We also have an instance method +with_lock+, and a class method
39
- # +attr_memoized+
40
- attr_memoized :contents, -> { redis.get(redis_key) }
41
- attr_memoized :redis, -> { Redis.new }
42
- attr_memoized :redis_key, -> { 'config_file' }
43
-
44
- def reload_config!(new_key)
45
- # +with_lock+ method if offered in place of +synchronize+
46
- # to avoid double-locking within the same thread.
47
- with_lock do
48
- self.redis_key = new_key
49
- contents(reload: true)
50
- end
51
- end
52
- end
53
- end
54
-
55
- @config = Concurrent::RedisConfig.new
56
- @config.contents
57
- #=> { "host": "127.0.0.1" }
58
- @config.reload_config!('another_file')
59
- #=> { "host": "google.com" }
60
- @config.contents
61
- #=> { "host": "google.com" }
62
- ```
63
-
64
- ### The Problem
65
-
66
- One of the issues with memoization in multi-threaded environment is that it may lead to unexpected or undefined behavior, due to the situation known as a [_race condition_](https://stackoverflow.com/questions/34510/what-is-a-race-condition).
67
-
68
- Consider the following example:
69
-
70
- ```ruby
71
- class Account
72
- def self.owner
73
- # Slow expensive query
74
- @owner ||= ActiveRecord::Base.execute('select ...').first
75
- end
76
- end
77
- # Let's be dangerous:
78
- [ Thread.new { Account.owner },
79
- Thread.new { Account.owner } ].map(&:join)
80
- ```
81
-
82
- Ruby evaluates `a||=b` as `a || a=b`, which means that the assignment above won't happen if `a` is "falsey", ie. `false` or `nil`. If the method `self.owner` is not synchronized, then both threads will execute the expensive query, and only the result of the query executed by the second thread will be saved in `@owner`, even though by that time it will already have a value assigned by the first thread, that by that time had already completed.
83
-
84
- Most memoization gems out there, among those that the author had reviewed, did not seem to be concerned with thread safety, which is actually OK under wide ranging situations, particularly if the objects are not meant to be shared across threads.
85
-
86
- But in multi-threaded applications it's important to protect initializers of expensive resources, which is exactly what this library attempts to accomplish.
87
-
88
-
89
- ## Usage
90
-
91
- `AttrMemoized` — the gem's primary module, when included, decorates the receiver with several useful
92
- methods:
93
-
94
- * Pre-initialized class method `#mutex`. Each class that includes `AttrMemoized` gets their own mutex.
95
-
96
- * Pre-initialized instance method `#mutex`. Each instance of the class gets it's own dedicated mutex.
97
-
98
- * Convenience method `#with_lock` is provided in place of `#mutex.synhronize` and should be used to wrap any state changes to the class in order to guard against concurrent modification by other threads. It will only use `mutex.synchronize` once per thread, to avoid self-deadlocking.
99
-
100
- * New class method `#attr_memoized` is added, with the following syntax:
101
-
102
- ```ruby
103
- attr_memoized :attribute, [ :attribute, ...], -> { block returning a value } # Proc
104
- attr_memoized :attribute, [ :attribute, ...], :instance_method # symbol
105
- attr_memoized :attribute, [ :attribute, ...], SomeClass.method(:method_name) # Method instance
106
- ```
107
-
108
- * In the above definitions:
109
- * If a `Proc` is provided as an initializer, it will be called via `#instance_exec` method on the instance and, therefore, can access any public or private method of the instance without the need for `self.` receiver.
110
-
111
- * If the initializer is a `Symbol`, it is expected to be an instance method name, of a method that takes no arguments.
112
-
113
- * Finally, any `Method` instance can also be used.
114
-
115
- * Note, that multiple attribute names can be passed to `#attr_memoized`, and they will be lazy-loaded in the order of access and independently of each other. If the block always returns the same exactly value, then the list may be viewed as aliases. But if the block returns a new value each time its called, then each attribute will be initialized with a different value, eg:
116
-
117
- ```ruby
118
- srand
119
- require 'attr_memoized'
120
- class RandomNumberGenerator
121
- include AttrMemoized
122
- attr_memoized :random1,
123
- :random2,
124
- :random3, -> { rand(2**64) }
125
- end
126
-
127
- rng = RandomNumberGenerator.new
128
- # each is initialized as it's called, and so they
129
- # are all different:
130
- rng.random1 #=> 1304594275874777789
131
- rng.random2 #=> 12671375021040220422
132
- rng.random3 #=> 16656281832060271071
133
-
134
- # second time, they are all already memoized:
135
- rng.random1 #=> 1304594275874777789
136
- rng.random2 #=> 12671375021040220422
137
- rng.random3 #=> 16656281832060271071
138
- ```
139
-
140
-
141
- ## Installation
142
-
143
- Add this line to your application's Gemfile:
144
-
145
- ```ruby
146
- gem 'attr_memoized'
147
- ```
148
-
149
- And then execute:
150
-
151
- $ bundle
152
-
153
- Or install it yourself as:
154
-
155
- $ gem install attr_memoized
156
-
157
-
158
- ## Development
159
-
160
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
161
-
162
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
163
-
164
- ## Contributing
165
-
166
- Bug reports and pull requests are welcome on GitHub at [https://github.com/kigster/attr_memoized](https://github.com/kigster/attr_memoized).
167
-
168
- ## License
169
-
170
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).