Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,2 @@ | |||
Gemfile.lock | |||
.coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,3 @@ | |||
--color | |||
--tty | |||
-r support/coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,2 @@ | |||
rvm: [2.0.0, 2.1.0] | |||
script: bundle exec rspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,2 @@ | |||
source 'https://rubygems.org' | |||
gemspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,22 @@ | |||
Copyright (c) 2013, 2014 Konstantin Haase | |||
|
|||
Permission is hereby granted, free of charge, to any person | |||
obtaining a copy of this software and associated documentation | |||
files (the "Software"), to deal in the Software without | |||
restriction, including without limitation the rights to use, | |||
copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the | |||
Software is furnished to do so, subject to the following | |||
conditions: | |||
|
|||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |||
OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1 @@ | |||
General purpose Ruby library used by Sinatra 2.0, Mustermann and related projects. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,21 @@ | |||
require 'tool/decoration' | |||
|
|||
class Frank | |||
extend Tool::Decoration | |||
def self.get(path, &block) | |||
decorate(block) do |method| | |||
puts "mapping GET #{path} to #{method}" | |||
end | |||
end | |||
end | |||
|
|||
class MyApp < Frank | |||
get '/hi' do | |||
"Hello World" | |||
end | |||
|
|||
get '/'; get '/index.php' | |||
def index | |||
"This is the index page." | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,86 @@ | |||
require 'tool/thread_local' | |||
|
|||
module Tool | |||
# Mixin for easy method decorations. | |||
# | |||
# @example | |||
# class Frank | |||
# extend Tool::Decoration | |||
# def self.get(path, &block) | |||
# decorate(block) do |method| | |||
# puts "mapping GET #{path} to #{method}" | |||
# end | |||
# end | |||
# end | |||
# | |||
# # Output: | |||
# # mapping GET /hi to __generated1 | |||
# # mapping GET / to index | |||
# # mapping GET /index.php to index | |||
# class MyApp < Frank | |||
# get '/hi' do | |||
# "Hello World" | |||
# end | |||
# | |||
# get '/'; get '/index.php' | |||
# def index | |||
# "This is the index page." | |||
# end | |||
# end | |||
module Decoration | |||
module Initializer | |||
def initialize(*) | |||
setup_decorations | |||
super | |||
end | |||
end | |||
|
|||
module Setup | |||
def included(object) | |||
super | |||
case object | |||
when Class then object.send(:include, Initializer) | |||
when Module then object.extend(Setup) | |||
end | |||
end | |||
|
|||
def extended(object) | |||
object.send(:setup_decorations) | |||
end | |||
end | |||
|
|||
extend Setup | |||
|
|||
def decorate(block = nil, name: "generated", &callback) | |||
@decorations << callback | |||
|
|||
if block | |||
alias_name = "__" << name.to_s.downcase.gsub(/[^a-z]+/, ?_) << ?1 | |||
alias_name = alias_name.succ while respond_to? alias_name, true | |||
without_decorations { define_method(name, &block) } | |||
alias_method(alias_name, name) | |||
remove_method(name) | |||
private(alias_name) | |||
end | |||
end | |||
|
|||
def without_decorations | |||
@decorations.clear if was = @decorations.to_a.dup | |||
yield | |||
ensure | |||
@decorations.replace(was) if was | |||
end | |||
|
|||
def method_added(name) | |||
@decorations.each { |d| d.call(name) }.clear | |||
super | |||
end | |||
|
|||
def setup_decorations | |||
@decorations = Tool::ThreadLocal.new([]) | |||
end | |||
|
|||
private :method_added, :setup_decorations | |||
private_constant :Initializer, :Setup | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,54 @@ | |||
module Tool | |||
# A simple wrapper around ObjectSpace::WeakMap that allows matching keys by equality rather than identity. | |||
# Used for caching. | |||
# | |||
# @example | |||
# class ExpensiveComputation | |||
# @map = Tool::EqualityMap.new | |||
# | |||
# def self.new(*args) | |||
# @map.fetch(*args) { super } | |||
# end | |||
# end | |||
# | |||
# @see #fetch | |||
class EqualityMap | |||
attr_reader :map | |||
|
|||
def initialize | |||
@keys = {} | |||
@map = ObjectSpace::WeakMap.new | |||
end | |||
|
|||
# @param [Array<#hash>] key for caching | |||
# @yield block that will be called to populate entry if missing | |||
# @return value stored in map or result of block | |||
def fetch(*key) | |||
identity = @keys[key.hash] | |||
key = identity == key ? identity : key | |||
|
|||
# it is ok that this is not thread-safe, worst case it has double cost in | |||
# generating, object equality is not guaranteed anyways | |||
@map[key] ||= track(key, yield) | |||
end | |||
|
|||
# @param [#hash] key for identifying the object | |||
# @param [Object] object to be stored | |||
# @return [Object] same as the second parameter | |||
def track(key, object) | |||
ObjectSpace.define_finalizer(object, finalizer(key.hash)) | |||
@keys[key.hash] = key | |||
object | |||
end | |||
|
|||
# Finalizer proc needs to be generated in different scope so it doesn't keep a reference to the object. | |||
# | |||
# @param [Fixnum] hash for key | |||
# @return [Proc] finalizer callback | |||
def finalizer(hash) | |||
proc { @keys.delete(hash) } | |||
end | |||
|
|||
private :track, :finalizer | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,19 @@ | |||
require 'delegate' | |||
require 'weakref' | |||
require 'thread' | |||
|
|||
module Tool | |||
class ThreadLocal < Delegator | |||
def initialize(default = {}) | |||
@mutex = Mutex.new | |||
@default = default.dup | |||
@map = {} | |||
end | |||
|
|||
def __getobj__ | |||
ref = Thread.current[:weakref] ||= WeakRef.new(Thread.current) | |||
@map.delete_if { |key, value| !key.weakref_alive? } | |||
@mutex.synchronize { @map[ref] ||= @default.dup } | |||
end | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,3 @@ | |||
module Tool | |||
VERSION = '0.2.0' | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,13 @@ | |||
require 'delegate' | |||
|
|||
module Tool | |||
class WarningFilter < DelegateClass(IO) | |||
$stderr = new($stderr) | |||
$VERBOSE = true | |||
|
|||
def write(line) | |||
super if line !~ /^\S+gems\/ruby\-\S+:\d+: warning:/ | |||
end | |||
end | |||
end | |||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,57 @@ | |||
require 'tool/decoration' | |||
|
|||
describe Tool::Decoration do | |||
shared_examples :decorate do | |||
specify "with block" do | |||
method = nil | |||
subject.decorate(-> { 42 }) { |m| method = m } | |||
expect(method).not_to be_nil | |||
expect(subject.new.send(method)).to be == 42 | |||
end | |||
|
|||
specify "without block" do | |||
method = nil | |||
subject.decorate { |m| method = m } | |||
expect(method).to be_nil | |||
subject.send(:define_method, :foo) { } | |||
expect(method).to be == :foo | |||
end | |||
|
|||
specify "multiple decorations" do | |||
calls = [] | |||
subject.decorate { |m| calls << :a } | |||
subject.decorate { |m| calls << :b } | |||
expect(calls).to be_empty | |||
subject.send(:define_method, :foo) { } | |||
expect(calls).to be == [:a, :b] | |||
end | |||
|
|||
specify "multiple methods" do | |||
calls = [] | |||
subject.decorate { |m| calls << :a } | |||
subject.send(:define_method, :foo) { } | |||
subject.send(:define_method, :bar) { } | |||
expect(calls).to be == [:a] | |||
end | |||
end | |||
|
|||
context "extending a class" do | |||
subject { Class.new.extend(Tool::Decoration) } | |||
include_examples(:decorate) | |||
end | |||
|
|||
context "including in a module" do | |||
subject { Class.new.extend(Module.new { include Tool::Decoration }) } | |||
include_examples(:decorate) | |||
end | |||
|
|||
context "including in a module" do | |||
subject do | |||
Class.new(Module) do | |||
def new(*) Object.new.extend(self) end | |||
include Tool::Decoration | |||
end.new | |||
end | |||
include_examples(:decorate) | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,21 @@ | |||
require 'tool/equality_map' | |||
|
|||
describe Tool::EqualityMap do | |||
before { GC.disable } | |||
after { GC.enable } | |||
|
|||
describe :fetch do | |||
specify 'with existing entry' do | |||
subject.fetch("foo") { "foo" } | |||
result = subject.fetch("foo") { "bar" } | |||
expect(result).to be == "foo" | |||
end | |||
|
|||
specify 'with GC-removed entry' do | |||
subject.fetch("foo") { "foo" } | |||
expect(subject.map).to receive(:[]).and_return(nil) | |||
result = subject.fetch("foo") { "bar" } | |||
expect(result).to be == "bar" | |||
end | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,17 @@ | |||
require 'tool/warning_filter' | |||
require 'simplecov' | |||
require 'coveralls' | |||
|
|||
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ | |||
SimpleCov::Formatter::HTMLFormatter, | |||
Coveralls::SimpleCov::Formatter | |||
] | |||
|
|||
SimpleCov.start do | |||
project_name 'tool' | |||
minimum_coverage 100 | |||
coverage_dir '.coverage' | |||
|
|||
add_filter '/spec/' | |||
add_group 'Library', 'lib' | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,26 @@ | |||
require 'tool/thread_local' | |||
|
|||
describe Tool::ThreadLocal do | |||
specify 'normal access' do | |||
subject[:foo] = 'bar' | |||
expect(subject[:foo]).to be == 'bar' | |||
end | |||
|
|||
specify 'concurrent access' do | |||
subject[:foo] = 'bar' | |||
value = Thread.new { subject[:foo] = 'baz' }.value | |||
expect(value).to be == 'baz' | |||
expect(subject[:foo]).to be == 'bar' | |||
end | |||
|
|||
specify 'with an array as value' do | |||
list = Tool::ThreadLocal.new([]) | |||
foo = Thread.new { 10.times { list << :foo; sleep(0.01) }; list.to_a } | |||
bar = Thread.new { 10.times { list << :bar; sleep(0.01) }; list.to_a } | |||
expect(list).to be_empty | |||
list << :list | |||
expect(list) .to be == [ :list ] | |||
expect(foo.value) .to be == [ :foo ] * 10 | |||
expect(bar.value) .to be == [ :bar ] * 10 | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,22 @@ | |||
$:.unshift File.expand_path("../lib", __FILE__) | |||
require "tool/version" | |||
|
|||
Gem::Specification.new do |s| | |||
s.name = "tool" | |||
s.version = Tool::VERSION | |||
s.author = "Konstantin Haase" | |||
s.email = "konstantin.mailinglists@googlemail.com" | |||
s.homepage = "https://github.com/rkh/tool" | |||
s.summary = %q{general purpose library} | |||
s.description = %q{general purpose Ruby library used by Sinatra 2.0, Mustermann and related projects} | |||
s.license = 'MIT' | |||
s.files = `git ls-files`.split("\n") | |||
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") | |||
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } | |||
s.extra_rdoc_files = `git ls-files -- *.md`.split("\n") | |||
s.required_ruby_version = '>= 2.0.0' | |||
|
|||
s.add_development_dependency 'rspec', '~> 3.0.0.beta' | |||
s.add_development_dependency 'simplecov' | |||
s.add_development_dependency 'coveralls' | |||
end |