spruz 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 +0 -0
- data/Rakefile +85 -0
- data/VERSION +1 -0
- data/bin/enum +167 -0
- data/install.rb +33 -0
- data/lib/spruz.rb +22 -0
- data/lib/spruz/bijection.rb +31 -0
- data/lib/spruz/count_by.rb +8 -0
- data/lib/spruz/generator.rb +66 -0
- data/lib/spruz/go.rb +37 -0
- data/lib/spruz/hash_union.rb +8 -0
- data/lib/spruz/limited.rb +36 -0
- data/lib/spruz/memoize.rb +71 -0
- data/lib/spruz/minimize.rb +53 -0
- data/lib/spruz/module_group.rb +11 -0
- data/lib/spruz/null.rb +20 -0
- data/lib/spruz/once.rb +23 -0
- data/lib/spruz/p.rb +21 -0
- data/lib/spruz/partial_application.rb +29 -0
- data/lib/spruz/round.rb +38 -0
- data/lib/spruz/shuffle.rb +15 -0
- data/lib/spruz/subhash.rb +37 -0
- data/lib/spruz/time_dummy.rb +29 -0
- data/lib/spruz/to_proc.rb +9 -0
- data/lib/spruz/uniq_by.rb +8 -0
- data/lib/spruz/version.rb +8 -0
- data/lib/spruz/xt.rb +15 -0
- data/lib/spruz/xt/blank.rb +67 -0
- data/lib/spruz/xt/count_by.rb +11 -0
- data/lib/spruz/xt/full.rb +33 -0
- data/lib/spruz/xt/hash_union.rb +11 -0
- data/lib/spruz/xt/irb.rb +17 -0
- data/lib/spruz/xt/null.rb +5 -0
- data/lib/spruz/xt/p.rb +7 -0
- data/lib/spruz/xt/partial_application.rb +11 -0
- data/lib/spruz/xt/round.rb +13 -0
- data/lib/spruz/xt/shuffle.rb +11 -0
- data/lib/spruz/xt/subhash.rb +11 -0
- data/lib/spruz/xt/symbol_to_proc.rb +9 -0
- data/lib/spruz/xt/time_dummy.rb +7 -0
- data/lib/spruz/xt/uniq_by.rb +15 -0
- data/tests/test_spruz.rb +431 -0
- metadata +99 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Spruz
|
4
|
+
class Limited
|
5
|
+
# Create a Limited instance, that runs _maximum_ threads at most.
|
6
|
+
def initialize(maximum)
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@continue = ConditionVariable.new
|
9
|
+
@maximum = Integer(maximum)
|
10
|
+
raise ArgumentError, "maximum < 1" if @maximum < 1
|
11
|
+
@count = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
# The maximum number of worker threads.
|
15
|
+
attr_reader :maximum
|
16
|
+
|
17
|
+
# Execute _maximum_ number of threads in parallel.
|
18
|
+
def execute
|
19
|
+
@mutex.synchronize do
|
20
|
+
loop do
|
21
|
+
if @count < @maximum
|
22
|
+
@count += 1
|
23
|
+
Thread.new do
|
24
|
+
yield
|
25
|
+
@mutex.synchronize { @count -= 1 }
|
26
|
+
@continue.signal
|
27
|
+
end
|
28
|
+
return
|
29
|
+
else
|
30
|
+
@continue.wait(@mutex)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Spruz
|
2
|
+
module Memoize
|
3
|
+
class ::Module
|
4
|
+
class << self
|
5
|
+
# Returns the current memoize cache for all the stored objects and
|
6
|
+
# method call results.
|
7
|
+
def __memoize_cache__
|
8
|
+
@__memoize_cache__ ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
# Finalizer to delete the stored result for a garbage collected object.
|
12
|
+
def __memoize_cache_delete__
|
13
|
+
lambda do |id|
|
14
|
+
$DEBUG and warn "Deleted method results for object id='#{id}'"
|
15
|
+
__memoize_cache__.delete(id)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Automatically memoize calls of the the methods +method_ids+. The
|
21
|
+
# memoized results do NOT ONLY depend on the arguments, but ALSO on the
|
22
|
+
# object the method is called on.
|
23
|
+
def memoize_method(*method_ids)
|
24
|
+
method_ids.each do |method_id|
|
25
|
+
orig_method = instance_method(method_id)
|
26
|
+
__send__(:define_method, method_id) do |*args|
|
27
|
+
unless mc = ::Module.__memoize_cache__[__id__]
|
28
|
+
mc = ::Module.__memoize_cache__[__id__] ||= {}
|
29
|
+
ObjectSpace.define_finalizer(self, ::Module.__memoize_cache_delete__)
|
30
|
+
end
|
31
|
+
if mc.key?(args)
|
32
|
+
result = mc[args]
|
33
|
+
else
|
34
|
+
result = mc[args] = orig_method.bind(self).call(*args)
|
35
|
+
if $DEBUG
|
36
|
+
warn "#{self.class} cached method #{method_id}(#{args.inspect unless args.empty?}) = #{result.inspect} [#{__id__}]"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the current memoize cache for this Module.
|
45
|
+
def __memoize_cache__
|
46
|
+
@__memoize_cache__
|
47
|
+
end
|
48
|
+
|
49
|
+
# Automatically memoize calls of the functions +function_ids+. The
|
50
|
+
# memoized result does ONLY depend on the arguments given to the
|
51
|
+
# function.
|
52
|
+
def memoize_function(*function_ids)
|
53
|
+
mc = @__memoize_cache__ ||= {}
|
54
|
+
function_ids.each do |method_id|
|
55
|
+
orig_method = instance_method(method_id)
|
56
|
+
__send__(:define_method, method_id) do |*args|
|
57
|
+
if mc.key?(args)
|
58
|
+
result = mc[args]
|
59
|
+
else
|
60
|
+
result = mc[args] = orig_method.bind(self).call(*args)
|
61
|
+
if $DEBUG
|
62
|
+
warn "#{self.class} cached function #{method_id}(#{args.inspect unless args.empty?}) = #{result.inspect}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
result
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Spruz
|
2
|
+
# This module can be mixed into all classes, whose instances respond to the
|
3
|
+
# [] and size-methods, like for example Array. The returned elements from []
|
4
|
+
# should respond to the succ method.
|
5
|
+
module Minimize
|
6
|
+
# Returns a minimized version of this object, that is successive elements
|
7
|
+
# are substituted with ranges a..b. In the situation ..., x, y,... and y !=
|
8
|
+
# x.succ a range x..x is created, to make it easier to iterate over all the
|
9
|
+
# ranges in one run. A small example:
|
10
|
+
# [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ].minimize # => [ 'A'..'C', 'G'..'G', 'K'..'M' ]
|
11
|
+
#
|
12
|
+
# If the order of the original elements doesn't matter, it's a good idea to
|
13
|
+
# first sort them and then minimize:
|
14
|
+
# [ 5, 1, 4, 2 ].sort.minimize # => [ 1..2, 4..5 ]
|
15
|
+
def minimize
|
16
|
+
result = []
|
17
|
+
last_index = size - 1
|
18
|
+
size.times do |i|
|
19
|
+
result << [ self[0] ] if i == 0
|
20
|
+
if self[i].succ != self[i + 1] or i == last_index
|
21
|
+
result[-1] << self[i]
|
22
|
+
result << [ self[i + 1] ] unless i == last_index
|
23
|
+
end
|
24
|
+
end
|
25
|
+
result.map! { |a, b| a..b }
|
26
|
+
end
|
27
|
+
|
28
|
+
# First minimizes this object, then calls the replace method with the
|
29
|
+
# result.
|
30
|
+
def minimize!
|
31
|
+
replace minimize
|
32
|
+
end
|
33
|
+
|
34
|
+
# Invert a minimized version of an object. Some small examples:
|
35
|
+
# [ 'A'..'C', 'G'..'G', 'K'..'M' ].unminimize # => [ 'A', 'B', 'C', 'G', 'K', 'L', 'M' ]
|
36
|
+
# and
|
37
|
+
# [ 1..2, 4..5 ].unminimize # => [ 1, 2, 4, 5 ]
|
38
|
+
def unminimize
|
39
|
+
result = []
|
40
|
+
for range in self
|
41
|
+
for e in range
|
42
|
+
result << e
|
43
|
+
end
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
# Invert a minimized version of this object in place.
|
49
|
+
def unminimize!
|
50
|
+
replace unminimize
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/spruz/null.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Spruz
|
2
|
+
# Implementation of the null object pattern in Ruby.
|
3
|
+
module Null
|
4
|
+
def method_missing(*)
|
5
|
+
self
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
''
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
'NULL'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
NULL = Class.new do
|
18
|
+
include Spruz::Null
|
19
|
+
end.new
|
20
|
+
end
|
data/lib/spruz/once.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Spruz
|
2
|
+
module Once
|
3
|
+
include File::Constants
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def only_once(lock_filename = nil, locking_constant = nil)
|
8
|
+
lock_filename ||= $0
|
9
|
+
locking_constant ||= LOCK_EX
|
10
|
+
f = File.new(lock_filename, RDONLY)
|
11
|
+
f.flock(locking_constant) and yield
|
12
|
+
ensure
|
13
|
+
if f
|
14
|
+
f.flock LOCK_UN
|
15
|
+
f.close
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def try_only_once(lock_filename = nil, locking_constant = nil, &block)
|
20
|
+
only_once(lock_filename, locking_constant || LOCK_EX | LOCK_NB, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/spruz/p.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
module Spruz
|
4
|
+
module P
|
5
|
+
private
|
6
|
+
|
7
|
+
# Raise a runtime error with the inspected objects +objs+ (obtained by
|
8
|
+
# calling the #inspect method) as their message text. This is useful for
|
9
|
+
# quick debugging.
|
10
|
+
def p!(*objs)
|
11
|
+
raise((objs.size < 2 ? objs.first : objs).inspect)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Raise a runtime error with the inspected objects +objs+ (obtained by
|
15
|
+
# calling the #pretty_inspect method) as their message text. This is useful
|
16
|
+
# for quick debugging.
|
17
|
+
def pp!(*objs)
|
18
|
+
raise("\n" + (objs.size < 2 ? objs.first : objs).pretty_inspect.chomp)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Spruz
|
2
|
+
module PartialApplication
|
3
|
+
# If this module is included into a Proc (or similar object), it tampers
|
4
|
+
# with its Proc#arity method.
|
5
|
+
def self.included(modul)
|
6
|
+
modul.module_eval do
|
7
|
+
old_arity = instance_method(:arity)
|
8
|
+
define_method(:arity) do
|
9
|
+
@__arity__ or old_arity.bind(self).call
|
10
|
+
end
|
11
|
+
end
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a partial application of this Proc (or similar object) using
|
16
|
+
# _args_ as the already applied arguments.
|
17
|
+
def partial(*args)
|
18
|
+
if args.empty?
|
19
|
+
dup
|
20
|
+
elsif args.size > arity
|
21
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for #{arity})"
|
22
|
+
else
|
23
|
+
f = lambda { |*b| call(*(args + b)) }
|
24
|
+
f.instance_variable_set :@__arity__, arity - args.size
|
25
|
+
f
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/spruz/round.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Spruz
|
2
|
+
# A bit more versatile rounding for Ruby
|
3
|
+
module Round
|
4
|
+
def self.included(klass)
|
5
|
+
if klass.instance_method(:round)
|
6
|
+
klass.class_eval do
|
7
|
+
begin
|
8
|
+
remove_method :round
|
9
|
+
rescue NameError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
super
|
13
|
+
else
|
14
|
+
raise NoMethodError, 'no round method found'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def round(places = 0)
|
19
|
+
unless Integer === places
|
20
|
+
raise TypeError, "argument places has to be an Integer"
|
21
|
+
end
|
22
|
+
if places < 0
|
23
|
+
max_places = -Math.log(self.abs + 1) / Math.log(10)
|
24
|
+
raise ArgumentError, "places has to be >= #{max_places.ceil}" if max_places > places
|
25
|
+
end
|
26
|
+
t = self
|
27
|
+
f = 10.0 ** places
|
28
|
+
t *= f
|
29
|
+
if t >= 0.0
|
30
|
+
t = (t + 0.5).floor
|
31
|
+
elsif t < 0.0
|
32
|
+
t = (t - 0.5).ceil
|
33
|
+
end
|
34
|
+
t /= f
|
35
|
+
t.nan? ? self : t
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Spruz
|
2
|
+
module Subhash
|
3
|
+
# Create a subhash from this hash, that only contains key-value pairs
|
4
|
+
# matching +patterns+ and return it. +patterns+ can be for example /^foo/
|
5
|
+
# to put 'foobar' and 'foobaz' or 'foo'/:foo to put 'foo' into the subhash.
|
6
|
+
#
|
7
|
+
# If a block is given this method yields to it after the first pattern
|
8
|
+
# matched with a 3-tuple of +(key, value, match_data)+ using the return
|
9
|
+
# value of the block as the value of the result hash. +match_data+ is a
|
10
|
+
# MatchData instance if the matching pattern was a regular rexpression
|
11
|
+
# otherwise it is nil.
|
12
|
+
def subhash(*patterns)
|
13
|
+
patterns.map! { |pat| pat.respond_to?(:match) ? pat : pat.to_s }
|
14
|
+
result =
|
15
|
+
if default_proc
|
16
|
+
self.class.new(&default_proc)
|
17
|
+
else
|
18
|
+
self.class.new(default)
|
19
|
+
end
|
20
|
+
if block_given?
|
21
|
+
each do |k, v|
|
22
|
+
patterns.each { |pat|
|
23
|
+
if pat === k.to_s
|
24
|
+
result[k] = yield(k, v, $~)
|
25
|
+
break
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
else
|
30
|
+
each do |k, v|
|
31
|
+
result[k] = v if patterns.any? { |pat| pat === k.to_s }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Spruz
|
2
|
+
module TimeDummy
|
3
|
+
def self.included(modul)
|
4
|
+
modul.module_eval do
|
5
|
+
class << self
|
6
|
+
alias really_new new
|
7
|
+
end
|
8
|
+
|
9
|
+
extend ClassMethods
|
10
|
+
|
11
|
+
class << self
|
12
|
+
alias now new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
attr_accessor :dummy
|
19
|
+
|
20
|
+
def new
|
21
|
+
if dummy
|
22
|
+
dummy.dup
|
23
|
+
else
|
24
|
+
really_new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|