tool 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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