lock_and_cache 0.1.2 → 1.0.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/.travis.yml +2 -0
- data/.yardopts +2 -0
- data/CHANGELOG +17 -0
- data/README.md +59 -13
- data/Rakefile +2 -0
- data/lib/lock_and_cache/version.rb +1 -1
- data/lib/lock_and_cache.rb +69 -16
- data/lock_and_cache.gemspec +6 -6
- data/spec/lock_and_cache_spec.rb +35 -10
- data/spec/spec_helper.rb +2 -2
- metadata +22 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9845e5a7f52feea98d5f059df539cd79258ba24d
|
4
|
+
data.tar.gz: 58865bd7f3a4a94516bc16e7eed6b4d26b53fba3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57db24888a5da3addfc922b470a5cd7e1da892f5c227326413669165277d3f6aa017ab1812ca3514439fa4965c81c6627b7ecd324edb752ff0cd6b0949e78b46
|
7
|
+
data.tar.gz: 8cafb78639eb731bf7c439bfb101d65a8ec5633a2cecf68af33445e1a1224428302d1d64b2f304c13f74accc4c42e48c88e1f29975f38fb429597cfe3a3c9bbc
|
data/.travis.yml
CHANGED
data/.yardopts
ADDED
data/CHANGELOG
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
1.0.1 / 2015-08-06
|
2
|
+
|
3
|
+
* Bug fixes
|
4
|
+
|
5
|
+
* Return value properly if lock was acquired but cached value immediately found
|
6
|
+
|
7
|
+
* Enhancements
|
8
|
+
|
9
|
+
* Documentation
|
10
|
+
|
11
|
+
1.0.0 / 2015-08-05
|
12
|
+
|
13
|
+
* Enhancements
|
14
|
+
|
15
|
+
* Use Redis redlock http://redis.io/topics/distlock instead of Postgres advisory locks
|
16
|
+
* No more dependency on ActiveRecord or Postgres!
|
17
|
+
|
1
18
|
0.1.2 / 2015-06-24
|
2
19
|
|
3
20
|
* Enhancements
|
data/README.md
CHANGED
@@ -1,31 +1,73 @@
|
|
1
1
|
# LockAndCache
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/seamusabshere/lock_and_cache)
|
4
|
+
[](https://codeclimate.com/github/seamusabshere/lock_and_cache)
|
5
|
+
[](https://gemnasium.com/seamusabshere/lock_and_cache)
|
6
|
+
[](http://badge.fury.io/rb/redlock)
|
7
|
+
[](https://hakiri.io/github/seamusabshere/lock_and_cache/master)
|
8
|
+
[](http://inch-ci.org/github/seamusabshere/lock_and_cache)
|
4
9
|
|
5
|
-
|
10
|
+
Lock and cache using redis!
|
6
11
|
|
7
|
-
|
8
|
-
* no dep on redis
|
12
|
+
## Redlock locking
|
9
13
|
|
10
|
-
|
14
|
+
Based on [antirez's Redlock algorithm](http://redis.io/topics/distlock).
|
11
15
|
|
12
|
-
|
16
|
+
```ruby
|
17
|
+
LockAndCache.storage = Redis.new
|
18
|
+
```
|
19
|
+
|
20
|
+
It will use this redis for both locking and storing cached values.
|
21
|
+
|
22
|
+
## Convenient caching
|
23
|
+
|
24
|
+
(be sure to set up storage as above)
|
25
|
+
|
26
|
+
You put a block inside of a method:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class Blog
|
30
|
+
def click(arg1, arg2)
|
31
|
+
lock_and_cache(arg1, arg2, expires: 5) do
|
32
|
+
# do the work
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
The key will be `{ Blog, :click, $id, $arg1, $arg2 }`. In other words, it auto-detects the class, method, object id ... and you add other args if you want.
|
39
|
+
|
40
|
+
You can change the object id easily:
|
13
41
|
|
14
42
|
```ruby
|
15
|
-
|
43
|
+
class Blog
|
44
|
+
# [...]
|
45
|
+
# if you don't define this, it will try to call #id
|
46
|
+
def lock_and_cache_key
|
47
|
+
[author, title]
|
48
|
+
end
|
49
|
+
end
|
16
50
|
```
|
17
51
|
|
18
|
-
|
52
|
+
## Tunables
|
19
53
|
|
20
|
-
|
54
|
+
* `LockAndCache.storage=[redis]`
|
55
|
+
* `LockAndCache.lock_expires=[seconds]` default is 3 days
|
56
|
+
* `LockAndCache.lock_spin=[seconds]` (how long to wait before retrying lock) default is 0.1 seconds
|
57
|
+
* `ENV['LOCK_AND_CACHE_DEBUG']='true'` if you want some debugging output on `$stderr`
|
21
58
|
|
22
|
-
|
59
|
+
## Few dependencies
|
23
60
|
|
24
|
-
|
61
|
+
* [activesupport](https://rubygems.org/gems/activesupport) (come on, it's the bomb)
|
62
|
+
* [redis](https://github.com/redis/redis-rb)
|
63
|
+
* [redlock](https://github.com/leandromoreira/redlock-rb)
|
64
|
+
* [hash_digest](https://github.com/seamusabshere/hash_digest) (which requires [murmurhash3](https://github.com/funny-falcon/murmurhash3-ruby))
|
25
65
|
|
26
|
-
##
|
66
|
+
## Real-world usage
|
27
67
|
|
28
|
-
|
68
|
+
<p><a href="http://faraday.io"><img src="https://s3.amazonaws.com/photos.angel.co/startups/i/175701-a63ebd1b56a401e905963c64958204d4-medium_jpg.jpg" alt="Faraday logo"/></a></p>
|
69
|
+
|
70
|
+
We use [`lock_and_cache`](https://rubygems.org/gems/lock_and_cache) for [big data-driven marketing at Faraday](http://angel.co/faraday).
|
29
71
|
|
30
72
|
## Contributing
|
31
73
|
|
@@ -34,3 +76,7 @@ TODO: Write usage instructions here
|
|
34
76
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
35
77
|
4. Push to the branch (`git push origin my-new-feature`)
|
36
78
|
5. Create a new Pull Request
|
79
|
+
|
80
|
+
# Copyright
|
81
|
+
|
82
|
+
Copyright 2015 Seamus Abshere
|
data/Rakefile
CHANGED
data/lib/lock_and_cache.rb
CHANGED
@@ -1,23 +1,57 @@
|
|
1
1
|
require 'lock_and_cache/version'
|
2
|
-
|
2
|
+
require 'redis'
|
3
|
+
require 'redlock'
|
3
4
|
require 'hash_digest'
|
4
|
-
require '
|
5
|
-
require '
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext'
|
6
7
|
|
7
8
|
module LockAndCache
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
DEFAULT_LOCK_EXPIRES = 60 * 60 * 24 * 3 * 1000 # 3 days in milliseconds
|
10
|
+
DEFAULT_LOCK_SPIN = 0.1
|
11
|
+
|
12
|
+
# @param redis_connection [Redis] A redis connection to be used for lock and cached value storage
|
13
|
+
def LockAndCache.storage=(redis_connection)
|
14
|
+
raise "only redis for now" unless redis_connection.class.to_s == 'Redis'
|
15
|
+
@storage = redis_connection
|
16
|
+
@lock_manager = Redlock::Client.new [redis_connection]
|
11
17
|
end
|
12
18
|
|
19
|
+
# @return [Redis] The redis connection used for lock and cached value storage
|
13
20
|
def LockAndCache.storage
|
14
21
|
@storage
|
15
22
|
end
|
16
23
|
|
17
|
-
|
18
|
-
|
24
|
+
# @param seconds [Numeric] Lock expiry in seconds.
|
25
|
+
#
|
26
|
+
# @note Can be overridden by putting `expires:` in your call to `#lock_and_cache`
|
27
|
+
def LockAndCache.lock_expires=(seconds)
|
28
|
+
@lock_expires = seconds.to_f * 1000
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Numeric] Lock expiry in milliseconds.
|
32
|
+
# @private
|
33
|
+
def LockAndCache.lock_expires
|
34
|
+
@lock_expires || DEFAULT_LOCK_EXPIRES
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param seconds [Numeric] How long to wait before trying a lock again, in seconds
|
38
|
+
#
|
39
|
+
# @note Can be overridden by putting `lock_spin:` in your call to `#lock_and_cache`
|
40
|
+
def LockAndCache.lock_spin=(seconds)
|
41
|
+
@lock_spin = seconds.to_f
|
19
42
|
end
|
20
43
|
|
44
|
+
# @private
|
45
|
+
def LockAndCache.lock_spin
|
46
|
+
@lock_spin || DEFAULT_LOCK_SPIN
|
47
|
+
end
|
48
|
+
|
49
|
+
# @private
|
50
|
+
def LockAndCache.lock_manager
|
51
|
+
@lock_manager
|
52
|
+
end
|
53
|
+
|
54
|
+
# @private
|
21
55
|
class Key
|
22
56
|
attr_reader :obj
|
23
57
|
attr_reader :method_id
|
@@ -53,6 +87,9 @@ module LockAndCache
|
|
53
87
|
|
54
88
|
end
|
55
89
|
|
90
|
+
# Clear a cache given exactly the method and exactly the same arguments
|
91
|
+
#
|
92
|
+
# @note Does not unlock.
|
56
93
|
def lock_and_cache_clear(method_id, *key_parts)
|
57
94
|
debug = (ENV['LOCK_AND_CACHE_DEBUG'] == 'true')
|
58
95
|
key = LockAndCache::Key.new self, method_id, key_parts
|
@@ -61,13 +98,20 @@ module LockAndCache
|
|
61
98
|
LockAndCache.storage.del digest
|
62
99
|
end
|
63
100
|
|
101
|
+
# Lock and cache a method given key parts.
|
102
|
+
#
|
103
|
+
# @param key_parts [*] Parts that you want to include in the lock and cache key
|
104
|
+
#
|
105
|
+
# @return The cached value (possibly newly calculated).
|
64
106
|
def lock_and_cache(*key_parts)
|
65
107
|
raise "need a block" unless block_given?
|
66
108
|
debug = (ENV['LOCK_AND_CACHE_DEBUG'] == 'true')
|
67
109
|
caller[0] =~ /in `(\w+)'/
|
68
110
|
method_id = $1 or raise "couldn't get method_id from #{kaller[0]}"
|
69
|
-
options = key_parts.
|
70
|
-
expires = options['expires']
|
111
|
+
options = key_parts.last.is_a?(Hash) ? key_parts.pop.stringify_keys : {}
|
112
|
+
expires = options['expires']
|
113
|
+
lock_expires = options.fetch 'lock_expires', LockAndCache.lock_expires
|
114
|
+
lock_spin = options.fetch 'lock_spin', LockAndCache.lock_spin
|
71
115
|
key = LockAndCache::Key.new self, method_id, key_parts
|
72
116
|
digest = key.digest
|
73
117
|
storage = LockAndCache.storage
|
@@ -76,20 +120,29 @@ module LockAndCache
|
|
76
120
|
return ::Marshal.load(storage.get(digest))
|
77
121
|
end
|
78
122
|
Thread.exclusive { $stderr.puts "[lock_and_cache] B #{key.debug}" } if debug
|
79
|
-
|
123
|
+
retval = nil
|
124
|
+
lock_manager = LockAndCache.lock_manager
|
125
|
+
lock_digest = 'lock/' + digest
|
126
|
+
lock_info = nil
|
127
|
+
begin
|
128
|
+
until lock_info = lock_manager.lock(lock_digest, lock_expires)
|
129
|
+
sleep lock_spin
|
130
|
+
end
|
80
131
|
Thread.exclusive { $stderr.puts "[lock_and_cache] C #{key.debug}" } if debug
|
81
132
|
if storage.exists digest
|
82
|
-
::Marshal.load storage.get(digest)
|
133
|
+
retval = ::Marshal.load storage.get(digest)
|
83
134
|
else
|
84
135
|
Thread.exclusive { $stderr.puts "[lock_and_cache] D #{key.debug}" } if debug
|
85
|
-
|
136
|
+
retval = yield
|
86
137
|
if expires
|
87
|
-
storage.setex digest, expires, ::Marshal.dump(
|
138
|
+
storage.setex digest, expires, ::Marshal.dump(retval)
|
88
139
|
else
|
89
|
-
storage.set digest, ::Marshal.dump(
|
140
|
+
storage.set digest, ::Marshal.dump(retval)
|
90
141
|
end
|
91
|
-
memo
|
92
142
|
end
|
143
|
+
ensure
|
144
|
+
lock_manager.unlock lock_info
|
93
145
|
end
|
146
|
+
retval
|
94
147
|
end
|
95
148
|
end
|
data/lock_and_cache.gemspec
CHANGED
@@ -18,15 +18,15 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency '
|
21
|
+
spec.add_runtime_dependency 'activesupport'
|
22
22
|
spec.add_runtime_dependency 'hash_digest'
|
23
|
-
spec.add_runtime_dependency '
|
23
|
+
spec.add_runtime_dependency 'redis'
|
24
|
+
spec.add_runtime_dependency 'redlock'
|
24
25
|
|
25
26
|
spec.add_development_dependency 'pry'
|
26
|
-
spec.add_development_dependency '
|
27
|
-
spec.add_development_dependency 'bundler', '~> 1.7'
|
28
|
-
spec.add_development_dependency 'pg'
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
29
28
|
spec.add_development_dependency 'rake', '~> 10.0'
|
30
29
|
spec.add_development_dependency 'rspec'
|
31
|
-
spec.add_development_dependency '
|
30
|
+
spec.add_development_dependency 'thread'
|
31
|
+
spec.add_development_dependency 'yard'
|
32
32
|
end
|
data/spec/lock_and_cache_spec.rb
CHANGED
@@ -10,13 +10,13 @@ class Foo
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def click
|
13
|
-
lock_and_cache
|
13
|
+
lock_and_cache do
|
14
14
|
@count += 1
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def click_exp
|
19
|
-
lock_and_cache(
|
19
|
+
lock_and_cache(expires: 1, foo: :bar) do
|
20
20
|
@count_exp += 1
|
21
21
|
end
|
22
22
|
end
|
@@ -44,14 +44,12 @@ class Bar
|
|
44
44
|
end
|
45
45
|
sleep 1
|
46
46
|
@count += 1
|
47
|
-
|
48
|
-
$clicking.delete @id
|
49
|
-
end
|
47
|
+
$clicking.delete @id
|
50
48
|
@count
|
51
49
|
end
|
52
50
|
|
53
51
|
def click
|
54
|
-
lock_and_cache
|
52
|
+
lock_and_cache do
|
55
53
|
unsafe_click
|
56
54
|
end
|
57
55
|
end
|
@@ -63,7 +61,7 @@ end
|
|
63
61
|
|
64
62
|
describe LockAndCache do
|
65
63
|
before do
|
66
|
-
LockAndCache.
|
64
|
+
LockAndCache.storage.flushdb
|
67
65
|
end
|
68
66
|
|
69
67
|
it 'has a version number' do
|
@@ -79,7 +77,7 @@ describe LockAndCache do
|
|
79
77
|
|
80
78
|
it "can be cleared" do
|
81
79
|
expect(foo.click).to eq(1)
|
82
|
-
foo.lock_and_cache_clear :click
|
80
|
+
foo.lock_and_cache_clear :click
|
83
81
|
expect(foo.click).to eq(2)
|
84
82
|
end
|
85
83
|
|
@@ -93,7 +91,8 @@ describe LockAndCache do
|
|
93
91
|
|
94
92
|
describe "locking" do
|
95
93
|
let(:bar) { Bar.new(rand.to_s) }
|
96
|
-
|
94
|
+
|
95
|
+
it "it blows up normally (simple thread)" do
|
97
96
|
a = Thread.new do
|
98
97
|
bar.unsafe_click
|
99
98
|
end
|
@@ -106,7 +105,21 @@ describe LockAndCache do
|
|
106
105
|
end.to raise_error(/somebody/)
|
107
106
|
end
|
108
107
|
|
109
|
-
it "
|
108
|
+
it "it blows up (pre-existing thread pool, more reliable)" do
|
109
|
+
pool = Thread.pool 2
|
110
|
+
Thread::Pool.abort_on_exception = true
|
111
|
+
expect do
|
112
|
+
pool.process do
|
113
|
+
bar.unsafe_click
|
114
|
+
end
|
115
|
+
pool.process do
|
116
|
+
bar.unsafe_click
|
117
|
+
end
|
118
|
+
pool.shutdown
|
119
|
+
end.to raise_error(/somebody/)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "doesn't blow up if you lock it (simple thread)" do
|
110
123
|
a = Thread.new do
|
111
124
|
bar.click
|
112
125
|
end
|
@@ -117,6 +130,18 @@ describe LockAndCache do
|
|
117
130
|
b.join
|
118
131
|
end
|
119
132
|
|
133
|
+
it "doesn't blow up if you lock it (pre-existing thread pool, more reliable)" do
|
134
|
+
pool = Thread.pool 2
|
135
|
+
Thread::Pool.abort_on_exception = true
|
136
|
+
pool.process do
|
137
|
+
bar.click
|
138
|
+
end
|
139
|
+
pool.process do
|
140
|
+
bar.click
|
141
|
+
end
|
142
|
+
pool.shutdown
|
143
|
+
end
|
144
|
+
|
120
145
|
end
|
121
146
|
|
122
147
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
2
|
require 'lock_and_cache'
|
3
3
|
|
4
|
-
ActiveRecord::Base.establish_connection adapter: 'postgresql', database: 'lock_and_cache_test'
|
5
|
-
|
6
4
|
require 'redis'
|
7
5
|
LockAndCache.storage = Redis.new
|
8
6
|
|
7
|
+
require 'thread/pool'
|
8
|
+
|
9
9
|
require 'pry'
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lock_and_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Seamus Abshere
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: redis
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,13 +53,13 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: redlock
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
-
type: :
|
62
|
+
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: pry
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -86,44 +86,44 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '1.
|
89
|
+
version: '1.6'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '1.
|
96
|
+
version: '1.6'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
103
|
+
version: '10.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
110
|
+
version: '10.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: rspec
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: thread
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: yard
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -161,6 +161,7 @@ files:
|
|
161
161
|
- ".gitignore"
|
162
162
|
- ".rspec"
|
163
163
|
- ".travis.yml"
|
164
|
+
- ".yardopts"
|
164
165
|
- CHANGELOG
|
165
166
|
- Gemfile
|
166
167
|
- LICENSE.txt
|