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.
@@ -0,0 +1,8 @@
1
+ module Spruz
2
+ module HashUnion
3
+ def |(other)
4
+ other = other.to_hash if other.respond_to?(:to_hash)
5
+ other.merge(self)
6
+ end
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,11 @@
1
+ module Spruz
2
+ module ModuleGroup
3
+ def self.[](*modules)
4
+ modul = Module.new
5
+ modules.each do |m|
6
+ m.module_eval { include modul }
7
+ end
8
+ modul
9
+ end
10
+ end
11
+ 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
@@ -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,15 @@
1
+ module Spruz
2
+ module Shuffle
3
+ def shuffle!
4
+ (size - 1) .downto(1) do |i|
5
+ j = rand(i + 1)
6
+ self[i], self[j] = self[j], self[i]
7
+ end
8
+ self
9
+ end
10
+
11
+ def shuffle
12
+ dup.shuffle!
13
+ end
14
+ end
15
+ 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
@@ -0,0 +1,9 @@
1
+ module Spruz
2
+ module ToProc
3
+ def to_proc
4
+ lambda do |obj, *args|
5
+ obj.__send__(self, *args[0..-1])
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Spruz
2
+ module UniqBy
3
+ def uniq_by(&b)
4
+ b ||= lambda { |x| x }
5
+ inject({}) { |h, e| h[b[e]] ||= e; h }.values
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Spruz
2
+ # Spruz version
3
+ VERSION = '0.1.0'
4
+ VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
5
+ VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
+ VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
7
+ VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
8
+ end