tool 0.1.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.
- data/README.md +65 -0
- data/Rakefile +21 -0
- data/lib/tool.rb +17 -0
- data/lib/tool/autoloader.rb +60 -0
- data/lib/tool/lock.rb +33 -0
- data/lib/tool/version.rb +3 -0
- data/spec/autoloader/foo.rb +6 -0
- data/spec/autoloader/foo/answer.rb +3 -0
- data/spec/autoloader/foo/bar.rb +2 -0
- data/spec/autoloader/foo/baz/bar.rb +2 -0
- data/spec/autoloader/foo/baz/foo.rb +2 -0
- data/spec/autoloader/foo/blah.rb +2 -0
- data/spec/autoloader/foo/blah/boom.rb +2 -0
- data/spec/autoloader/foo/cli.rb +2 -0
- data/spec/autoloader/foo/foo_bar.rb +2 -0
- data/spec/autoloader/foo/version.rb +3 -0
- data/spec/autoloader_spec.rb +47 -0
- data/spec/lock_spec.rb +43 -0
- data/spec/tool_spec.rb +91 -0
- data/tool.gemspec +16 -0
- metadata +78 -0
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
Sane tools for Ruby without monkey-patching. This is basically code usually
|
2
|
+
copy from one project to another.
|
3
|
+
|
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.
|
6
|
+
|
7
|
+
# Included Tools
|
8
|
+
|
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
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
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 }
|
18
|
+
|
19
|
+
task :release => [:spec, :commit, :publish, :tag, :push]
|
20
|
+
task :default => :spec
|
21
|
+
task :test => :spec
|
data/lib/tool.rb
ADDED
@@ -0,0 +1,17 @@
|
|
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
|
@@ -0,0 +1,60 @@
|
|
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
ADDED
@@ -0,0 +1,33 @@
|
|
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/lib/tool/version.rb
ADDED
@@ -0,0 +1,47 @@
|
|
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
ADDED
@@ -0,0 +1,43 @@
|
|
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.1) and track << :first } }
|
22
|
+
b = Thread.new { synchronize { track << :second } }
|
23
|
+
|
24
|
+
a.join
|
25
|
+
b.join
|
26
|
+
|
27
|
+
track.should be == [:first, :second]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'is no global lock' do
|
31
|
+
a = Thread.new { Object.new.extend(Tool::Lock).synchronize { sleep(0.1) and track << :first } }
|
32
|
+
b = Thread.new { Object.new.extend(Tool::Lock).synchronize { track << :second } }
|
33
|
+
|
34
|
+
a.join
|
35
|
+
b.join
|
36
|
+
|
37
|
+
track.should be == [:second, :first]
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'has no issue with recursion' do
|
41
|
+
synchronize { synchronize { } }
|
42
|
+
end
|
43
|
+
end
|
data/spec/tool_spec.rb
ADDED
@@ -0,0 +1,91 @@
|
|
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
|
data/tool.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'tool/version'
|
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"
|
7
|
+
|
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'
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tool
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Konstantin Haase
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-27 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &2152929520 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.7'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2152929520
|
25
|
+
description: This is basically code usually copy from one project to another
|
26
|
+
email: konstantin.mailinglists@googlemail.com
|
27
|
+
executables: []
|
28
|
+
extensions: []
|
29
|
+
extra_rdoc_files: []
|
30
|
+
files:
|
31
|
+
- README.md
|
32
|
+
- Rakefile
|
33
|
+
- lib/tool.rb
|
34
|
+
- lib/tool/autoloader.rb
|
35
|
+
- lib/tool/lock.rb
|
36
|
+
- 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
|
50
|
+
- tool.gemspec
|
51
|
+
homepage: http://github.com/rkh/tool
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.8.10
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Sane tools for Ruby without monkey-patching
|
75
|
+
test_files:
|
76
|
+
- spec/autoloader_spec.rb
|
77
|
+
- spec/lock_spec.rb
|
78
|
+
- spec/tool_spec.rb
|