tool 0.1.1 → 0.2.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 +7 -0
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.travis.yml +2 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +7 -62
- data/examples/frank.rb +21 -0
- data/lib/tool/decoration.rb +103 -0
- data/lib/tool/equality_map.rb +56 -0
- data/lib/tool/thread_local.rb +45 -0
- data/lib/tool/version.rb +2 -2
- data/lib/tool/warning_filter.rb +21 -0
- data/spec/decoration_spec.rb +57 -0
- data/spec/equality_map_spec.rb +21 -0
- data/spec/support/coverage.rb +17 -0
- data/spec/thread_local_spec.rb +26 -0
- data/tool.gemspec +19 -13
- metadata +70 -39
- data/Rakefile +0 -21
- data/lib/tool.rb +0 -17
- data/lib/tool/autoloader.rb +0 -60
- data/lib/tool/lock.rb +0 -33
- data/spec/autoloader/foo.rb +0 -6
- data/spec/autoloader/foo/answer.rb +0 -3
- data/spec/autoloader/foo/bar.rb +0 -2
- data/spec/autoloader/foo/baz/bar.rb +0 -2
- data/spec/autoloader/foo/baz/foo.rb +0 -2
- data/spec/autoloader/foo/blah.rb +0 -2
- data/spec/autoloader/foo/blah/boom.rb +0 -2
- data/spec/autoloader/foo/cli.rb +0 -2
- data/spec/autoloader/foo/foo_bar.rb +0 -2
- data/spec/autoloader/foo/version.rb +0 -3
- data/spec/autoloader_spec.rb +0 -47
- data/spec/lock_spec.rb +0 -45
- data/spec/tool_spec.rb +0 -91
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 725ca5923dffd3a0ae1191c35fd80011f341750b
|
4
|
+
data.tar.gz: a09be4b173b7bd8aa8a8d2281ef2b2e274714ca8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c28c6074f6bbb5827825f4376ad9c4866661988fbc9247a74488122cb71faf30ce46e26a6e84cdea20259c000acf9d49bdbe70aef7c375d30e112fec1bf1e2fd
|
7
|
+
data.tar.gz: bfc3a87def2d13a293ded338fc9e4c3f3a2e6ebe3cc3faaf71ed922c8c69e4499a58eda3d08f59e1d22cffae6c7e842bf2a9e34332bd11d3a31bd670fe8c1efd
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013, 2014 Konstantin Haase
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,65 +1,10 @@
|
|
1
|
-
|
2
|
-
copy from one project to another.
|
1
|
+
*Make sure you view the correct docs: [latest release](http://rubydoc.info/gems/tool/frames), [master](http://rubydoc.info/github/rkh/tool/master/frames).*
|
3
2
|
|
4
|
-
|
5
|
-
to feel guilty for using it. Mixins are lazy-loaded.
|
3
|
+
General purpose Ruby library used by Sinatra 2.0, Mustermann and related projects.
|
6
4
|
|
7
|
-
|
5
|
+
Included:
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
ActiveSupport does. The means it is fast, transparent, and does not alter
|
14
|
-
constant lookup in any way.
|
15
|
-
|
16
|
-
``` ruby
|
17
|
-
module Foo
|
18
|
-
include Tool::Autoloader
|
19
|
-
end
|
20
|
-
```
|
21
|
-
|
22
|
-
If you don't want to include the module, use `setup`:
|
23
|
-
|
24
|
-
``` ruby
|
25
|
-
Tool::Autoloader.setup Foo
|
26
|
-
```
|
27
|
-
|
28
|
-
## Tool::Lock
|
29
|
-
|
30
|
-
Adds a `synchronize` method that behaves like `Rubinius.synchronize(self)`,
|
31
|
-
i.e. recursively going through the lock will not result in a deadlock:
|
32
|
-
|
33
|
-
``` ruby
|
34
|
-
class Foo
|
35
|
-
include Tool::Lock
|
36
|
-
|
37
|
-
def recursive_fun(i = 0)
|
38
|
-
return i if i == 5
|
39
|
-
# THIS NEEDS TO BE THREAD-SAFE!!!
|
40
|
-
synchronize { recursive_fun(i + 1) }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
```
|
44
|
-
|
45
|
-
It will use `Rubinius.synchronize` when on Rubinius.
|
46
|
-
|
47
|
-
## Tool.set
|
48
|
-
|
49
|
-
Simplified version of Sinatra's set:
|
50
|
-
|
51
|
-
``` ruby
|
52
|
-
class Foo
|
53
|
-
Tool.set(self, :foo, :foo)
|
54
|
-
end
|
55
|
-
|
56
|
-
class Bar < Foo
|
57
|
-
end
|
58
|
-
|
59
|
-
Bar.foo # => :foo
|
60
|
-
|
61
|
-
Bar.foo = :bar
|
62
|
-
Bar.foo # => :bar
|
63
|
-
|
64
|
-
Foo.foo # => :foo
|
65
|
-
```
|
7
|
+
* **Decoration** - Mixin for easy method decorations.
|
8
|
+
* **EqualityMap** - Weak reference caching based on key equality.
|
9
|
+
* **ThreadLocal** - True thread locals that are properly garbage collected.
|
10
|
+
* **WarningFilter** - Enable Ruby's warnings, but filter out caused by third party gems.
|
data/examples/frank.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'tool/decoration'
|
2
|
+
|
3
|
+
class Frank
|
4
|
+
extend Tool::Decoration
|
5
|
+
def self.get(path, &block)
|
6
|
+
decorate(block) do |method|
|
7
|
+
puts "mapping GET #{path} to #{method}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class MyApp < Frank
|
13
|
+
get '/hi' do
|
14
|
+
"Hello World"
|
15
|
+
end
|
16
|
+
|
17
|
+
get '/'; get '/index.php'
|
18
|
+
def index
|
19
|
+
"This is the index page."
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'tool/thread_local'
|
2
|
+
|
3
|
+
module Tool
|
4
|
+
# Mixin for easy method decorations.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class Frank
|
8
|
+
# extend Tool::Decoration
|
9
|
+
# def self.get(path, &block)
|
10
|
+
# decorate(block) do |method|
|
11
|
+
# puts "mapping GET #{path} to #{method}"
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # Output:
|
17
|
+
# # mapping GET /hi to __generated1
|
18
|
+
# # mapping GET / to index
|
19
|
+
# # mapping GET /index.php to index
|
20
|
+
# class MyApp < Frank
|
21
|
+
# get '/hi' do
|
22
|
+
# "Hello World"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# get '/'; get '/index.php'
|
26
|
+
# def index
|
27
|
+
# "This is the index page."
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
module Decoration
|
31
|
+
module Initializer
|
32
|
+
# Make sure decorations list is initializsed upon instantiation.
|
33
|
+
# @!visibility private
|
34
|
+
def initialize(*)
|
35
|
+
setup_decorations
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module Setup
|
41
|
+
# Make sure decorations list is initializsed if Decoration is included.
|
42
|
+
# @!visibility private
|
43
|
+
def included(object)
|
44
|
+
case object
|
45
|
+
when Class then object.send(:include, Initializer)
|
46
|
+
when Module then object.extend(Setup)
|
47
|
+
end
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
# Make sure decorations list is initializsed if Decoration extends an object.
|
52
|
+
# @!visibility private
|
53
|
+
def extended(object)
|
54
|
+
object.send(:setup_decorations)
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
extend Setup
|
60
|
+
|
61
|
+
# Set up a decoration.
|
62
|
+
#
|
63
|
+
# @param [Proc, UnboundMethod, nil] block used for defining a method right away
|
64
|
+
# @param [String, Symbol] name given to the generated method if block is given
|
65
|
+
# @yield callback called with method name once method is defined
|
66
|
+
# @yieldparam [Symbol] method name of the method that is to be decorated
|
67
|
+
def decorate(block = nil, name: "generated", &callback)
|
68
|
+
@decorations << callback
|
69
|
+
|
70
|
+
if block
|
71
|
+
alias_name = "__" << name.to_s.downcase.gsub(/[^a-z]+/, ?_) << ?1
|
72
|
+
alias_name = alias_name.succ while respond_to? alias_name, true
|
73
|
+
without_decorations { define_method(name, &block) }
|
74
|
+
alias_method(alias_name, name)
|
75
|
+
remove_method(name)
|
76
|
+
private(alias_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Runs a given block without applying decorations defined outside of the block.
|
81
|
+
# Decorations defined before the block will still be registered after the block.
|
82
|
+
#
|
83
|
+
# @yield block to run without decorations
|
84
|
+
def without_decorations
|
85
|
+
@decorations.clear if was = @decorations.to_a.dup
|
86
|
+
yield
|
87
|
+
ensure
|
88
|
+
@decorations.replace(was) if was
|
89
|
+
end
|
90
|
+
|
91
|
+
def method_added(name)
|
92
|
+
@decorations.each { |d| d.call(name) }.clear
|
93
|
+
super
|
94
|
+
end
|
95
|
+
|
96
|
+
def setup_decorations
|
97
|
+
@decorations = Tool::ThreadLocal.new([])
|
98
|
+
end
|
99
|
+
|
100
|
+
private :method_added, :setup_decorations
|
101
|
+
private_constant :Initializer, :Setup
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Tool
|
2
|
+
# A simple wrapper around ObjectSpace::WeakMap that allows matching keys by equality rather than identity.
|
3
|
+
# Used for caching. Note that `fetch` is not guaranteed to return the object, even if it has not been
|
4
|
+
# garbage collected yet, especially when used concurrently. Therefore, the block passed to `fetch` has to
|
5
|
+
# be idempotent.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# class ExpensiveComputation
|
9
|
+
# @map = Tool::EqualityMap.new
|
10
|
+
#
|
11
|
+
# def self.new(*args)
|
12
|
+
# @map.fetch(*args) { super }
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @see #fetch
|
17
|
+
class EqualityMap
|
18
|
+
attr_reader :map
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@keys = {}
|
22
|
+
@map = ObjectSpace::WeakMap.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [Array<#hash>] key for caching
|
26
|
+
# @yield block that will be called to populate entry if missing (has to be idempotent)
|
27
|
+
# @return value stored in map or result of block
|
28
|
+
def fetch(*key)
|
29
|
+
identity = @keys[key.hash]
|
30
|
+
key = identity == key ? identity : key
|
31
|
+
|
32
|
+
# it is ok that this is not thread-safe, worst case it has double cost in
|
33
|
+
# generating, object equality is not guaranteed anyways
|
34
|
+
@map[key] ||= track(key, yield)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [#hash] key for identifying the object
|
38
|
+
# @param [Object] object to be stored
|
39
|
+
# @return [Object] same as the second parameter
|
40
|
+
def track(key, object)
|
41
|
+
ObjectSpace.define_finalizer(object, finalizer(key.hash))
|
42
|
+
@keys[key.hash] = key
|
43
|
+
object
|
44
|
+
end
|
45
|
+
|
46
|
+
# Finalizer proc needs to be generated in different scope so it doesn't keep a reference to the object.
|
47
|
+
#
|
48
|
+
# @param [Fixnum] hash for key
|
49
|
+
# @return [Proc] finalizer callback
|
50
|
+
def finalizer(hash)
|
51
|
+
proc { @keys.delete(hash) }
|
52
|
+
end
|
53
|
+
|
54
|
+
private :track, :finalizer
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'weakref'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Tool
|
6
|
+
# Have thread local values without them actually being thread global.
|
7
|
+
#
|
8
|
+
# Advantages:
|
9
|
+
# * values for all threads are garbage collected when ThreadLocal instance is
|
10
|
+
# * values for specific thread are garbage collected when thread is
|
11
|
+
# * no hidden global state
|
12
|
+
# * supports other data types besides hashes.
|
13
|
+
#
|
14
|
+
# @example To replace Thread.current hash access
|
15
|
+
# local = Tool::ThreadLocal.new
|
16
|
+
# local[:key] = "value"
|
17
|
+
#
|
18
|
+
# Thread.new do
|
19
|
+
# local[:key] = "other value"
|
20
|
+
# puts local[:key] # other value
|
21
|
+
# end.join
|
22
|
+
#
|
23
|
+
# puts local[:key] # value
|
24
|
+
#
|
25
|
+
# @example Usage with Array
|
26
|
+
# local = Tool::ThreadLocal.new([:foo])
|
27
|
+
# local << :bar
|
28
|
+
#
|
29
|
+
# Thread.new { p local }.join # [:foo]
|
30
|
+
# p local # [:foo, :bar]
|
31
|
+
class ThreadLocal < Delegator
|
32
|
+
def initialize(default = {})
|
33
|
+
@mutex = Mutex.new
|
34
|
+
@default = default.dup
|
35
|
+
@map = {}
|
36
|
+
end
|
37
|
+
|
38
|
+
# @!visibility private
|
39
|
+
def __getobj__
|
40
|
+
ref = Thread.current[:weakref] ||= WeakRef.new(Thread.current)
|
41
|
+
@map.delete_if { |key, value| !key.weakref_alive? }
|
42
|
+
@mutex.synchronize { @map[ref] ||= @default.dup }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/tool/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Tool
|
2
|
-
VERSION = '0.
|
3
|
-
end
|
2
|
+
VERSION = '0.2.0'
|
3
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Tool
|
4
|
+
# Enables Ruby's built-in warnings (-w) but filters out those caused by third-party gems.
|
5
|
+
# Does not invlove any manual set up.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# require 'tool/warning_filter'
|
9
|
+
# Foo = 10
|
10
|
+
# Foo = 20
|
11
|
+
class WarningFilter < DelegateClass(IO)
|
12
|
+
$stderr = new($stderr)
|
13
|
+
$VERBOSE = true
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
def write(line)
|
17
|
+
super if line !~ /^\S+gems\/ruby\-\S+:\d+: warning:/
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'tool/decoration'
|
2
|
+
|
3
|
+
describe Tool::Decoration do
|
4
|
+
shared_examples :decorate do
|
5
|
+
specify "with block" do
|
6
|
+
method = nil
|
7
|
+
subject.decorate(-> { 42 }) { |m| method = m }
|
8
|
+
expect(method).not_to be_nil
|
9
|
+
expect(subject.new.send(method)).to be == 42
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "without block" do
|
13
|
+
method = nil
|
14
|
+
subject.decorate { |m| method = m }
|
15
|
+
expect(method).to be_nil
|
16
|
+
subject.send(:define_method, :foo) { }
|
17
|
+
expect(method).to be == :foo
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "multiple decorations" do
|
21
|
+
calls = []
|
22
|
+
subject.decorate { |m| calls << :a }
|
23
|
+
subject.decorate { |m| calls << :b }
|
24
|
+
expect(calls).to be_empty
|
25
|
+
subject.send(:define_method, :foo) { }
|
26
|
+
expect(calls).to be == [:a, :b]
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "multiple methods" do
|
30
|
+
calls = []
|
31
|
+
subject.decorate { |m| calls << :a }
|
32
|
+
subject.send(:define_method, :foo) { }
|
33
|
+
subject.send(:define_method, :bar) { }
|
34
|
+
expect(calls).to be == [:a]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "extending a class" do
|
39
|
+
subject { Class.new.extend(Tool::Decoration) }
|
40
|
+
include_examples(:decorate)
|
41
|
+
end
|
42
|
+
|
43
|
+
context "including in a module" do
|
44
|
+
subject { Class.new.extend(Module.new { include Tool::Decoration }) }
|
45
|
+
include_examples(:decorate)
|
46
|
+
end
|
47
|
+
|
48
|
+
context "including in a module" do
|
49
|
+
subject do
|
50
|
+
Class.new(Module) do
|
51
|
+
def new(*) Object.new.extend(self) end
|
52
|
+
include Tool::Decoration
|
53
|
+
end.new
|
54
|
+
end
|
55
|
+
include_examples(:decorate)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'tool/equality_map'
|
2
|
+
|
3
|
+
describe Tool::EqualityMap do
|
4
|
+
before { GC.disable }
|
5
|
+
after { GC.enable }
|
6
|
+
|
7
|
+
describe :fetch do
|
8
|
+
specify 'with existing entry' do
|
9
|
+
subject.fetch("foo") { "foo" }
|
10
|
+
result = subject.fetch("foo") { "bar" }
|
11
|
+
expect(result).to be == "foo"
|
12
|
+
end
|
13
|
+
|
14
|
+
specify 'with GC-removed entry' do
|
15
|
+
subject.fetch("foo") { "foo" }
|
16
|
+
expect(subject.map).to receive(:[]).and_return(nil)
|
17
|
+
result = subject.fetch("foo") { "bar" }
|
18
|
+
expect(result).to be == "bar"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'tool/warning_filter'
|
2
|
+
require 'simplecov'
|
3
|
+
require 'coveralls'
|
4
|
+
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
6
|
+
SimpleCov::Formatter::HTMLFormatter,
|
7
|
+
Coveralls::SimpleCov::Formatter
|
8
|
+
]
|
9
|
+
|
10
|
+
SimpleCov.start do
|
11
|
+
project_name 'tool'
|
12
|
+
minimum_coverage 100
|
13
|
+
coverage_dir '.coverage'
|
14
|
+
|
15
|
+
add_filter '/spec/'
|
16
|
+
add_group 'Library', 'lib'
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'tool/thread_local'
|
2
|
+
|
3
|
+
describe Tool::ThreadLocal do
|
4
|
+
specify 'normal access' do
|
5
|
+
subject[:foo] = 'bar'
|
6
|
+
expect(subject[:foo]).to be == 'bar'
|
7
|
+
end
|
8
|
+
|
9
|
+
specify 'concurrent access' do
|
10
|
+
subject[:foo] = 'bar'
|
11
|
+
value = Thread.new { subject[:foo] = 'baz' }.value
|
12
|
+
expect(value).to be == 'baz'
|
13
|
+
expect(subject[:foo]).to be == 'bar'
|
14
|
+
end
|
15
|
+
|
16
|
+
specify 'with an array as value' do
|
17
|
+
list = Tool::ThreadLocal.new([])
|
18
|
+
foo = Thread.new { 10.times { list << :foo; sleep(0.01) }; list.to_a }
|
19
|
+
bar = Thread.new { 10.times { list << :bar; sleep(0.01) }; list.to_a }
|
20
|
+
expect(list).to be_empty
|
21
|
+
list << :list
|
22
|
+
expect(list) .to be == [ :list ]
|
23
|
+
expect(foo.value) .to be == [ :foo ] * 10
|
24
|
+
expect(bar.value) .to be == [ :bar ] * 10
|
25
|
+
end
|
26
|
+
end
|
data/tool.gemspec
CHANGED
@@ -1,16 +1,22 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
require "tool/version"
|
3
3
|
|
4
|
-
Gem::Specification.new
|
5
|
-
s.
|
6
|
-
s.
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "tool"
|
6
|
+
s.version = Tool::VERSION
|
7
|
+
s.author = "Konstantin Haase"
|
8
|
+
s.email = "konstantin.mailinglists@googlemail.com"
|
9
|
+
s.homepage = "https://github.com/rkh/tool"
|
10
|
+
s.summary = %q{general purpose library}
|
11
|
+
s.description = %q{general purpose Ruby library used by Sinatra 2.0, Mustermann and related projects}
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.extra_rdoc_files = `git ls-files -- *.md`.split("\n")
|
17
|
+
s.required_ruby_version = '>= 2.0.0'
|
7
18
|
|
8
|
-
s.
|
9
|
-
s.
|
10
|
-
s.
|
11
|
-
|
12
|
-
s.files = `git ls-files`.split("\n") - %w[Gemfile .gitignore .travis.yml]
|
13
|
-
s.test_files = s.files.select { |p| p =~ /^spec\/.*_spec.rb/ }
|
14
|
-
|
15
|
-
s.add_development_dependency 'rspec', '~> 2.7'
|
19
|
+
s.add_development_dependency 'rspec', '~> 3.0.0.beta'
|
20
|
+
s.add_development_dependency 'simplecov'
|
21
|
+
s.add_development_dependency 'coveralls'
|
16
22
|
end
|
metadata
CHANGED
@@ -1,78 +1,109 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Konstantin Haase
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-02-22 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
19
|
+
version: 3.0.0.beta
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.0.beta
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: simplecov
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: coveralls
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: general purpose Ruby library used by Sinatra 2.0, Mustermann and related
|
56
|
+
projects
|
26
57
|
email: konstantin.mailinglists@googlemail.com
|
27
58
|
executables: []
|
28
59
|
extensions: []
|
29
|
-
extra_rdoc_files:
|
60
|
+
extra_rdoc_files:
|
61
|
+
- README.md
|
30
62
|
files:
|
63
|
+
- .gitignore
|
64
|
+
- .rspec
|
65
|
+
- .travis.yml
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE
|
31
68
|
- README.md
|
32
|
-
-
|
33
|
-
- lib/tool.rb
|
34
|
-
- lib/tool/
|
35
|
-
- lib/tool/
|
69
|
+
- examples/frank.rb
|
70
|
+
- lib/tool/decoration.rb
|
71
|
+
- lib/tool/equality_map.rb
|
72
|
+
- lib/tool/thread_local.rb
|
36
73
|
- lib/tool/version.rb
|
37
|
-
-
|
38
|
-
- spec/
|
39
|
-
- spec/
|
40
|
-
- spec/
|
41
|
-
- spec/
|
42
|
-
- spec/autoloader/foo/blah.rb
|
43
|
-
- spec/autoloader/foo/blah/boom.rb
|
44
|
-
- spec/autoloader/foo/cli.rb
|
45
|
-
- spec/autoloader/foo/foo_bar.rb
|
46
|
-
- spec/autoloader/foo/version.rb
|
47
|
-
- spec/autoloader_spec.rb
|
48
|
-
- spec/lock_spec.rb
|
49
|
-
- spec/tool_spec.rb
|
74
|
+
- lib/tool/warning_filter.rb
|
75
|
+
- spec/decoration_spec.rb
|
76
|
+
- spec/equality_map_spec.rb
|
77
|
+
- spec/support/coverage.rb
|
78
|
+
- spec/thread_local_spec.rb
|
50
79
|
- tool.gemspec
|
51
|
-
homepage:
|
52
|
-
licenses:
|
80
|
+
homepage: https://github.com/rkh/tool
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata: {}
|
53
84
|
post_install_message:
|
54
85
|
rdoc_options: []
|
55
86
|
require_paths:
|
56
87
|
- lib
|
57
88
|
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
89
|
requirements:
|
60
|
-
- -
|
90
|
+
- - '>='
|
61
91
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
92
|
+
version: 2.0.0
|
63
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
-
none: false
|
65
94
|
requirements:
|
66
|
-
- -
|
95
|
+
- - '>='
|
67
96
|
- !ruby/object:Gem::Version
|
68
97
|
version: '0'
|
69
98
|
requirements: []
|
70
99
|
rubyforge_project:
|
71
|
-
rubygems_version:
|
100
|
+
rubygems_version: 2.2.0
|
72
101
|
signing_key:
|
73
|
-
specification_version:
|
74
|
-
summary:
|
102
|
+
specification_version: 4
|
103
|
+
summary: general purpose library
|
75
104
|
test_files:
|
76
|
-
- spec/
|
77
|
-
- spec/
|
78
|
-
- spec/
|
105
|
+
- spec/decoration_spec.rb
|
106
|
+
- spec/equality_map_spec.rb
|
107
|
+
- spec/support/coverage.rb
|
108
|
+
- spec/thread_local_spec.rb
|
109
|
+
has_rdoc:
|
data/Rakefile
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
-
require 'tool/version'
|
3
|
-
|
4
|
-
def gem(*args) sh("gem", *args.map(&:to_s)) end
|
5
|
-
def git(*args) sh("git", *args.map(&:to_s)) end
|
6
|
-
|
7
|
-
gem_file = "tool-#{Tool::VERSION}.gem"
|
8
|
-
version = "v#{Tool::VERSION}"
|
9
|
-
message = "Release #{version}"
|
10
|
-
|
11
|
-
task(:spec) { ruby "-S rspec spec" }
|
12
|
-
task(:build) { gem :build, 'tool.gemspec' }
|
13
|
-
task(:install => :build) { gem :install, gem_file }
|
14
|
-
task(:publish => :install) { gem :push, gem_file }
|
15
|
-
task(:commit) { git :commit, '--allow-empty', '-m', message }
|
16
|
-
task(:tag) { git :tag, '-s', '-m', message, version }
|
17
|
-
task(:push) { git(:push) and git(:push, '--tags') }
|
18
|
-
|
19
|
-
task :release => [:spec, :commit, :publish, :tag, :push]
|
20
|
-
task :default => :spec
|
21
|
-
task :test => :spec
|
data/lib/tool.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
module Tool
|
2
|
-
autoload :Autoloader, 'tool/autoloader'
|
3
|
-
autoload :Lock, 'tool/lock'
|
4
|
-
autoload :VERSION, 'tool/version'
|
5
|
-
|
6
|
-
def self.set(object, key, value = (no_value = true), &block)
|
7
|
-
return key.each_pair { |k,v| set(object, k, v) } if no_value and not block
|
8
|
-
|
9
|
-
block = proc { value } unless no_value
|
10
|
-
sclass = (class << object; self; end)
|
11
|
-
setter = self
|
12
|
-
|
13
|
-
sclass.send(:define_method, key, &block)
|
14
|
-
sclass.send(:define_method, "#{key}=") { |v| setter.set(self, key, v) }
|
15
|
-
sclass.send(:define_method, "#{key}?") { !!__send__(key) }
|
16
|
-
end
|
17
|
-
end
|
data/lib/tool/autoloader.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
module Tool
|
2
|
-
module Autoloader
|
3
|
-
CAPITALIZE = %w[version cli] unless defined? CAPITALIZE
|
4
|
-
|
5
|
-
def self.setup(container, path = caller_dir)
|
6
|
-
prefix = path_for(container)
|
7
|
-
full_prefix = path_for(container, true)
|
8
|
-
path = File.expand_path(prefix, path)
|
9
|
-
directories = []
|
10
|
-
|
11
|
-
Dir.glob("#{path}/*") do |file|
|
12
|
-
base = File.basename(file, '.rb')
|
13
|
-
const = constant_for(base)
|
14
|
-
lib = "#{full_prefix}/#{base}"
|
15
|
-
|
16
|
-
if File.directory? file
|
17
|
-
directories << const
|
18
|
-
elsif file.end_with? '.rb'
|
19
|
-
container.autoload const, lib
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
directories.each do |const|
|
24
|
-
next if container.const_defined? const
|
25
|
-
nested = container.const_set(const, Module.new)
|
26
|
-
setup nested, path
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.path_for(constant, full = false)
|
31
|
-
name = constant.name.dup
|
32
|
-
name = name[/[^:]+$/] unless full
|
33
|
-
name.gsub! /([A-Z]+)([A-Z][a-z])/,'\1_\2'
|
34
|
-
name.gsub! /([a-z\d])([A-Z])/,'\1_\2'
|
35
|
-
name.gsub! '::', '/'
|
36
|
-
name.tr("-", "_").downcase
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.constant_for(file)
|
40
|
-
return file.upcase if CAPITALIZE.include? file
|
41
|
-
file.split('.', 2).first.split(/[_-]/).map(&:capitalize).join
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.capitalize(*args)
|
45
|
-
CAPITALIZE.concat(args)
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.append_features(base)
|
49
|
-
setup(base)
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.caller_dir
|
53
|
-
caller.each do |line|
|
54
|
-
file = File.expand_path(line.split(':', 2).first)
|
55
|
-
return File.dirname(file) if file != File.expand_path(__FILE__)
|
56
|
-
end
|
57
|
-
File.dirname($0)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
data/lib/tool/lock.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module Tool
|
2
|
-
module Lock
|
3
|
-
if defined? Rubinius
|
4
|
-
def synchronize
|
5
|
-
Rubinius.synchronize(self) { yield }
|
6
|
-
end
|
7
|
-
else
|
8
|
-
require 'thread'
|
9
|
-
@lock = Mutex.new
|
10
|
-
|
11
|
-
def self.synchronize(&block)
|
12
|
-
@lock.synchronize(&block)
|
13
|
-
end
|
14
|
-
|
15
|
-
def synchronize(&block)
|
16
|
-
Lock.synchronize { @lock, @locked_by = Mutex.new, nil unless lock? } unless lock?
|
17
|
-
return yield if @locked_by == Thread.current
|
18
|
-
@lock.synchronize do
|
19
|
-
@locked_by = Thread.current
|
20
|
-
result = yield
|
21
|
-
@locked_by = nil
|
22
|
-
result
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def lock?
|
29
|
-
instance_variable_defined? :@lock
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/spec/autoloader/foo.rb
DELETED
data/spec/autoloader/foo/bar.rb
DELETED
data/spec/autoloader/foo/blah.rb
DELETED
data/spec/autoloader/foo/cli.rb
DELETED
data/spec/autoloader_spec.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'tool'
|
2
|
-
require 'autoloader/foo'
|
3
|
-
|
4
|
-
describe Tool::Autoloader do
|
5
|
-
# poor man's matchers
|
6
|
-
def autoload(const) be_autoload(const) end
|
7
|
-
def start_with(str) be_start_with(str) end
|
8
|
-
|
9
|
-
it 'sets up autoloading' do
|
10
|
-
Autoloader::Foo.should autoload(:Bar)
|
11
|
-
Autoloader::Foo::Bar.name.should start_with("Autoloader::Foo::")
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'creates modules for subdirectories' do
|
15
|
-
Autoloader::Foo.should_not autoload(:Baz)
|
16
|
-
Autoloader::Foo::Baz.should autoload(:Bar)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'handles nested constants with same name' do
|
20
|
-
Autoloader::Foo::Baz::Foo.should_not be == Autoloader::Foo
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'does not automatically set up autoloading for autoloaded constants' do
|
24
|
-
Autoloader::Foo::Blah.should_not autoload(:Boom)
|
25
|
-
expect { Autoloader::Foo::Blah::Boom }.to raise_error(NameError)
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'does not override existing constants' do
|
29
|
-
Autoloader::Foo::Answer.should be == 42
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'loads VERSION' do
|
33
|
-
Autoloader::Foo.should autoload(:VERSION)
|
34
|
-
Autoloader::Foo.should_not autoload(:Version)
|
35
|
-
Autoloader::Foo::VERSION.should be == 1337
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'loads CLI' do
|
39
|
-
Autoloader::Foo.should autoload(:CLI)
|
40
|
-
Autoloader::Foo.should_not autoload(:Cli)
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'loads camel-cased constants' do
|
44
|
-
Autoloader::Foo.should autoload(:FooBar)
|
45
|
-
Autoloader::Foo.should_not autoload(:Foo_bar)
|
46
|
-
end
|
47
|
-
end
|
data/spec/lock_spec.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'tool'
|
2
|
-
|
3
|
-
describe Tool::Lock do
|
4
|
-
before do
|
5
|
-
Thread.abort_on_exception = true
|
6
|
-
end
|
7
|
-
|
8
|
-
let(:object) { Object.new.extend(Tool::Lock) }
|
9
|
-
let(:track) { [] }
|
10
|
-
|
11
|
-
def synchronize(&block)
|
12
|
-
object.synchronize(&block)
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'runs the given block' do
|
16
|
-
synchronize { track << :ran }
|
17
|
-
track.should be == [:ran]
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'locks for other threads' do
|
21
|
-
a = Thread.new { synchronize { sleep(0.2) and track << :first } }
|
22
|
-
|
23
|
-
sleep 0.1
|
24
|
-
b = Thread.new { synchronize { track << :second } }
|
25
|
-
|
26
|
-
a.join
|
27
|
-
b.join
|
28
|
-
|
29
|
-
track.should be == [:first, :second]
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'is no global lock' do
|
33
|
-
a = Thread.new { Object.new.extend(Tool::Lock).synchronize { sleep(0.1) and track << :first } }
|
34
|
-
b = Thread.new { Object.new.extend(Tool::Lock).synchronize { track << :second } }
|
35
|
-
|
36
|
-
a.join
|
37
|
-
b.join
|
38
|
-
|
39
|
-
track.should be == [:second, :first]
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'has no issue with recursion' do
|
43
|
-
synchronize { synchronize { } }
|
44
|
-
end
|
45
|
-
end
|
data/spec/tool_spec.rb
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
require 'tool'
|
2
|
-
|
3
|
-
describe Tool do
|
4
|
-
describe :set do
|
5
|
-
let(:object) { Object.new }
|
6
|
-
|
7
|
-
it 'defines a getter' do
|
8
|
-
Tool.set(object, :foo, :bar)
|
9
|
-
object.foo.should be == :bar
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'defines a setter' do
|
13
|
-
Tool.set(object, :foo, :bar)
|
14
|
-
object.foo = :foo
|
15
|
-
object.foo.should be == :foo
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'defines a flag' do
|
19
|
-
Tool.set(object, :foo, :bar)
|
20
|
-
object.should be_foo
|
21
|
-
object.foo = false
|
22
|
-
object.should_not be_foo
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'takes a block' do
|
26
|
-
Tool.set(object, :foo) { :bar }
|
27
|
-
object.foo.should be == :bar
|
28
|
-
object.should be_foo
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'adds a setter, even with a block' do
|
32
|
-
Tool.set(object, :foo) { :bar }
|
33
|
-
object.foo = false
|
34
|
-
object.should_not be_foo
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'uses the blocks value as flag' do
|
38
|
-
Tool.set(object, :foo) { false }
|
39
|
-
object.should_not be_foo
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'runs the block more than once' do
|
43
|
-
counter = 0
|
44
|
-
Tool.set(object, :foo) { counter += 1 }
|
45
|
-
object.foo.should be == 1
|
46
|
-
object.foo.should be == 2
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'wraps a block passed to the setter' do
|
50
|
-
Tool.set(object, :foo) { :bar }
|
51
|
-
object.foo = proc { false }
|
52
|
-
object.should be_foo
|
53
|
-
object.foo.should be_a(Proc)
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'allows passing in a hash' do
|
57
|
-
Tool.set(object, :foo => :bar)
|
58
|
-
object.foo.should be == :bar
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'allows passing in nil' do
|
62
|
-
Tool.set(object, :foo, nil)
|
63
|
-
object.foo.should be_nil
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'does not use the passed block if nil is given' do
|
67
|
-
Tool.set(object, :foo, nil) { :bar }
|
68
|
-
object.foo.should be_nil
|
69
|
-
object.should_not be_foo
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'inherits class settings' do
|
73
|
-
a = Class.new
|
74
|
-
b = Class.new(a)
|
75
|
-
|
76
|
-
Tool.set(a, :foo, :bar)
|
77
|
-
b.foo.should be == :bar
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'allows overriding class settings in subclasses' do
|
81
|
-
a = Class.new
|
82
|
-
b = Class.new(a)
|
83
|
-
|
84
|
-
Tool.set(a, :foo, :bar)
|
85
|
-
b.foo = :foo
|
86
|
-
|
87
|
-
a.foo.should be == :bar
|
88
|
-
b.foo.should be == :foo
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|