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