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 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
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ .coverage
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --tty
3
+ -r support/coverage
data/.travis.yml ADDED
@@ -0,0 +1,2 @@
1
+ rvm: [2.0.0, 2.1.0]
2
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
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
- Sane tools for Ruby without monkey-patching. This is basically code usually
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
- Goal of this library is to be lightweight and unobtrusive, so you don't have
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
- # Included Tools
5
+ Included:
8
6
 
9
- ## Tool::Autoloader
10
-
11
- Sets up `autoload` directives for nested constants. Has the advantage of
12
- setting these up when included instead of hooking into `const_missing`, like
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.1.1'
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
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
- require 'tool/version'
1
+ $:.unshift File.expand_path("../lib", __FILE__)
2
+ require "tool/version"
3
3
 
4
- Gem::Specification.new 'tool', Tool::VERSION do |s|
5
- s.description = "This is basically code usually copy from one project to another"
6
- s.summary = "Sane tools for Ruby without monkey-patching"
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.authors = ["Konstantin Haase"]
9
- s.email = "konstantin.mailinglists@googlemail.com"
10
- s.homepage = "http://github.com/rkh/tool"
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.1.1
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: 2011-10-27 00:00:00.000000000Z
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: &2152865200 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
- version: '2.7'
19
+ version: 3.0.0.beta
22
20
  type: :development
23
21
  prerelease: false
24
- version_requirements: *2152865200
25
- description: This is basically code usually copy from one project to another
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
- - Rakefile
33
- - lib/tool.rb
34
- - lib/tool/autoloader.rb
35
- - lib/tool/lock.rb
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
- - spec/autoloader/foo.rb
38
- - spec/autoloader/foo/answer.rb
39
- - spec/autoloader/foo/bar.rb
40
- - spec/autoloader/foo/baz/bar.rb
41
- - spec/autoloader/foo/baz/foo.rb
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: http://github.com/rkh/tool
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: '0'
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: 1.8.10
100
+ rubygems_version: 2.2.0
72
101
  signing_key:
73
- specification_version: 3
74
- summary: Sane tools for Ruby without monkey-patching
102
+ specification_version: 4
103
+ summary: general purpose library
75
104
  test_files:
76
- - spec/autoloader_spec.rb
77
- - spec/lock_spec.rb
78
- - spec/tool_spec.rb
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
@@ -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
@@ -1,6 +0,0 @@
1
- module Autoloader
2
- module Foo
3
- Answer = 42
4
- include Tool::Autoloader
5
- end
6
- end
@@ -1,3 +0,0 @@
1
- module Autoloader::Foo
2
- Answer = 23
3
- end
@@ -1,2 +0,0 @@
1
- class Autoloader::Foo::Bar
2
- end
@@ -1,2 +0,0 @@
1
- class Autoloader::Foo::Baz::Bar
2
- end
@@ -1,2 +0,0 @@
1
- module Autoloader::Foo::Baz::Foo
2
- end
@@ -1,2 +0,0 @@
1
- class Autoloader::Foo::Blah
2
- end
@@ -1,2 +0,0 @@
1
- class Autoloader::Foo::Blah::Boom
2
- end
@@ -1,2 +0,0 @@
1
- class Autoloader::Foo::CLI
2
- end
@@ -1,2 +0,0 @@
1
- class Autoloader::Foo::FooBar
2
- end
@@ -1,3 +0,0 @@
1
- module Autoloader::Foo
2
- VERSION = 1337
3
- end
@@ -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