utilrb 1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Changes.txt CHANGED
@@ -1,74 +1,90 @@
1
+ === Version 1.1
2
+
3
+ * changes and fixes:
4
+ - fix documentation here and there
5
+ - make sure all defined #to_s methods handle recursion
6
+ - defined Module#cached_enum: returns an Enumerator method for
7
+ a "each_" method, cachin the enumerator object. Works also
8
+ for iteration with one argument (caching one Enumerator by
9
+ argument)
10
+ - fix Time.from_hms when the millisecond field has leading zeroes
11
+ - optimizations here and there:
12
+ * Array#to_value_set does not rely on rb_iterate
13
+ * specific implementation of ValueSet#dup
14
+ * more generally, avoid object allocation where possible
15
+ - ColumnFormatter object: formats CSV output for display
16
+
1
17
  === Version 1.0
2
18
  Bumped version number because of incompatible changes. Make it 1.0 since
3
19
  having 0. version numbers for this kind of library is completely meaningless
4
20
 
5
- * incompatible changes:
6
- - Enumerable#+ acts on too many objects. Define it only on Enumerable::Enumerator
7
- - remove automatic convertion into ValueSet for methods which were doing it
8
- - removed Queue#get: fastthread is now the preferred threading library in
9
- Ruby 1.8.6, so Queue#get cannot be implemented anymore
10
-
11
- * new features:
12
- - PkgConfig class
13
- - define Proc#same_body?, Proc#file and Proc#line
14
- - Module#attr_predicate
15
- - 'since', 'until' and block parameters for Exception#full_message
16
- - ValueSet#intersects? (more efficient than checking that the intersection
17
- is empty)
18
-
19
- * changes and fixes:
20
- - properly handle singleton classes in has_ancestor?
21
- - properly handle recursion in ValueSet#to_s and Array#to_s
22
- - add the 'setup' task on the Rakefile to build the C extension
23
- - make Module#has_ancestor? work event if the C extension is not built
21
+ * incompatible changes:
22
+ - Enumerable#+ acts on too many objects. Define it only on Enumerable::Enumerator
23
+ - remove automatic convertion into ValueSet for methods which were doing it
24
+ - removed Queue#get: fastthread is now the preferred threading library in
25
+ Ruby 1.8.6, so Queue#get cannot be implemented anymore
26
+
27
+ * new features:
28
+ - PkgConfig class
29
+ - define Proc#same_body?, Proc#file and Proc#line
30
+ - Module#attr_predicate
31
+ - 'since', 'until' and block parameters for Exception#full_message
32
+ - ValueSet#intersects? (more efficient than checking that the intersection
33
+ is empty)
34
+
35
+ * changes and fixes:
36
+ - properly handle singleton classes in has_ancestor?
37
+ - properly handle recursion in ValueSet#to_s and Array#to_s
38
+ - add the 'setup' task on the Rakefile to build the C extension
39
+ - make Module#has_ancestor? work event if the C extension is not built
24
40
 
25
41
  === Version 0.2.3
26
42
 
27
- * new features:
28
- - BenchmarkAllocation, a Benchmark-like interface to benchmark object
29
- allocation
30
- - ValueSet#delete_if
31
- - Time#from_hms takes a time as "h:m:s.ms" and builds the corresponding
32
- Time object. Note that "s", "s.ms", "m:s", "m:s.ms", ... are accepted
33
- formats
34
-
35
- * changes and fixes:
36
- - define Queue#get only if we are using Ruby's core Queue. The current
37
- implementation is incompatible with fastthread for instance (and
38
- fastthhread's maintainer does not want #get on its Queue). Included
39
- a patch to define Queue#get on fastthread
40
- - fix ValueSet#== raising if the argument is not a ValueSet
41
- - fix brain-dead SequenceEnumerator#+, which was changing its receiver
43
+ * new features:
44
+ - BenchmarkAllocation, a Benchmark-like interface to benchmark object
45
+ allocation
46
+ - ValueSet#delete_if
47
+ - Time#from_hms takes a time as "h:m:s.ms" and builds the corresponding
48
+ Time object. Note that "s", "s.ms", "m:s", "m:s.ms", ... are accepted
49
+ formats
50
+
51
+ * changes and fixes:
52
+ - define Queue#get only if we are using Ruby's core Queue. The current
53
+ implementation is incompatible with fastthread for instance (and
54
+ fastthhread's maintainer does not want #get on its Queue). Included
55
+ a patch to define Queue#get on fastthread
56
+ - fix ValueSet#== raising if the argument is not a ValueSet
57
+ - fix brain-dead SequenceEnumerator#+, which was changing its receiver
42
58
 
43
59
  === Version 0.2.2
44
60
  The "don't forget to bump version number" release. 0.2 was supposed to be 0.2.1
45
61
 
46
- * new features:
47
- - Queue#get: waits for the queue to be not empty and returns all elements at
48
- once. A non_block parameter is given which makes get() return [] if the
49
- queue is empty
62
+ * new features:
63
+ - Queue#get: waits for the queue to be not empty and returns all elements at
64
+ once. A non_block parameter is given which makes get() return [] if the
65
+ queue is empty
50
66
 
51
- * changes:
52
- - Array#to_s and Hash#to_s now enclose the result in [] and {}. The difference
53
- with #inspect is that we call #to_s on the elements.
67
+ * changes:
68
+ - Array#to_s and Hash#to_s now enclose the result in [] and {}. The difference
69
+ with #inspect is that we call #to_s on the elements.
54
70
 
55
71
  === Version 0.2.1
56
- * new features:
57
- - Kernel#is_singleton?
58
- - Module#inherited_enumerable (class_inherited_enumerable on steroids)
59
- - Module#attribute() can be used in singleton classes (previously we had to
60
- call class_attribute() in the class itself)
61
- - UnboundMethod#call(obj, *args, &block) calls the method on obj with
62
- the provided arguments (does m.bind(obj).call(*args, &block))
63
-
64
- * changes:
65
- - changed semantics of Module::include for inclusion of modules in modules:
66
- the source_module::ClassExtension gets included in
67
- target_module::ClassExtension. Previously, it was extending the target's
68
- singleton class. This way, Module really acts as a mixin for both class
69
- methods and instance methods
72
+ * new features:
73
+ - Kernel#is_singleton?
74
+ - Module#inherited_enumerable (class_inherited_enumerable on steroids)
75
+ - Module#attribute() can be used in singleton classes (previously we had to
76
+ call class_attribute() in the class itself)
77
+ - UnboundMethod#call(obj, *args, &block) calls the method on obj with
78
+ the provided arguments (does m.bind(obj).call(*args, &block))
79
+
80
+ * changes:
81
+ - changed semantics of Module::include for inclusion of modules in modules:
82
+ the source_module::ClassExtension gets included in
83
+ target_module::ClassExtension. Previously, it was extending the target's
84
+ singleton class. This way, Module really acts as a mixin for both class
85
+ methods and instance methods
70
86
 
71
87
  === Version 0.1
72
- * Initial release
88
+ * Initial release
73
89
 
74
90
 
data/Manifest.txt CHANGED
@@ -13,11 +13,13 @@ ext/value_set.cc
13
13
  lib/utilrb.rb
14
14
  lib/utilrb/array.rb
15
15
  lib/utilrb/array/to_s.rb
16
+ lib/utilrb/column_formatter.rb
16
17
  lib/utilrb/common.rb
17
18
  lib/utilrb/enumerable.rb
18
19
  lib/utilrb/enumerable/null.rb
19
20
  lib/utilrb/enumerable/random_element.rb
20
21
  lib/utilrb/enumerable/sequence.rb
22
+ lib/utilrb/enumerable/to_s_helper.rb
21
23
  lib/utilrb/enumerable/uniq.rb
22
24
  lib/utilrb/exception.rb
23
25
  lib/utilrb/exception/full_message.rb
@@ -40,6 +42,7 @@ lib/utilrb/module.rb
40
42
  lib/utilrb/module/ancestor_p.rb
41
43
  lib/utilrb/module/attr_enumerable.rb
42
44
  lib/utilrb/module/attr_predicate.rb
45
+ lib/utilrb/module/cached_enum.rb
43
46
  lib/utilrb/module/define_method.rb
44
47
  lib/utilrb/module/include.rb
45
48
  lib/utilrb/module/inherited_enumerable.rb
@@ -70,5 +73,6 @@ test/test_object.rb
70
73
  test/test_objectstats.rb
71
74
  test/test_pkgconfig.rb
72
75
  test/test_proc.rb
76
+ test/test_set.rb
73
77
  test/test_time.rb
74
78
  test/test_unbound_method.rb
data/Rakefile CHANGED
@@ -20,7 +20,7 @@ Hoe.new('utilrb', Utilrb::VERSION) do |p|
20
20
  p.summary = 'Yet another Ruby toolkit'
21
21
  p.description = p.paragraphs_of('README.txt', 3..6).join("\n\n")
22
22
  p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
23
- p.changes = p.paragraphs_of('Changes.txt', 0..3).join("\n\n")
23
+ p.changes = p.paragraphs_of('Changes.txt', 0..1).join("\n\n")
24
24
 
25
25
  p.extra_deps << 'facets'
26
26
  p.rdoc_pattern = /(ext\/.*cc$|lib)|txt/
data/ext/faster.cc CHANGED
@@ -76,7 +76,7 @@ static VALUE proc_file(VALUE self)
76
76
  }
77
77
 
78
78
  /* call-seq:
79
- * proc.file
79
+ * proc.line
80
80
  *
81
81
  * Returns the line at which the proc body is defined, or nil
82
82
  */
data/ext/value_set.cc CHANGED
@@ -101,6 +101,22 @@ static VALUE value_set_include_p(VALUE vself, VALUE vother)
101
101
  */
102
102
  static VALUE value_set_to_value_set(VALUE self) { return self; }
103
103
 
104
+ /* call-seq:
105
+ * set.dup => other_set
106
+ *
107
+ * Duplicates this set, without duplicating the pointed-to objects
108
+ */
109
+ static VALUE value_set_dup(VALUE vself, VALUE vother)
110
+ {
111
+ ValueSet const& self = get_wrapped_set(vself);
112
+ VALUE vresult = rb_funcall(cValueSet, id_new, 0);
113
+ ValueSet& result = get_wrapped_set(vresult);
114
+ for (ValueSet::const_iterator it = self.begin(); it != self.end(); ++it)
115
+ result.insert(result.end(), *it);
116
+
117
+ return vresult;
118
+ }
119
+
104
120
  /* call-seq:
105
121
  * set.include_all?(other) => true or false
106
122
  *
@@ -302,6 +318,24 @@ static VALUE enumerable_to_value_set_i(VALUE i, VALUE* memo)
302
318
  return Qnil;
303
319
  }
304
320
 
321
+ /* call-seq:
322
+ * to_value_set => value_set
323
+ *
324
+ * Converts this array into a ValueSet object
325
+ */
326
+ static VALUE array_to_value_set(VALUE self)
327
+ {
328
+ VALUE vresult = rb_funcall(cValueSet, id_new, 0);
329
+ ValueSet& result = get_wrapped_set(vresult);
330
+
331
+ VALUE* ptr = RARRAY(self)->ptr;
332
+ long size = RARRAY(self)->len;
333
+ for (int i = 0; i < size; ++i)
334
+ result.insert(ptr[i]);
335
+
336
+ return vresult;
337
+ }
338
+
305
339
  /* call-seq:
306
340
  * enum.to_value_set => value_set
307
341
  *
@@ -328,6 +362,7 @@ static VALUE enumerable_to_value_set(VALUE self)
328
362
  extern "C" void Init_value_set()
329
363
  {
330
364
  rb_define_method(rb_mEnumerable, "to_value_set", RUBY_METHOD_FUNC(enumerable_to_value_set), 0);
365
+ rb_define_method(rb_cArray, "to_value_set", RUBY_METHOD_FUNC(array_to_value_set), 0);
331
366
 
332
367
  cValueSet = rb_define_class("ValueSet", rb_cObject);
333
368
  id_new = rb_intern("new");
@@ -344,6 +379,7 @@ extern "C" void Init_value_set()
344
379
  rb_define_method(cValueSet, "delete", RUBY_METHOD_FUNC(value_set_delete), 1);
345
380
  rb_define_method(cValueSet, "==", RUBY_METHOD_FUNC(value_set_equal), 1);
346
381
  rb_define_method(cValueSet, "to_value_set", RUBY_METHOD_FUNC(value_set_to_value_set), 0);
382
+ rb_define_method(cValueSet, "dup", RUBY_METHOD_FUNC(value_set_dup), 0);
347
383
  rb_define_method(cValueSet, "empty?", RUBY_METHOD_FUNC(value_set_empty_p), 0);
348
384
  rb_define_method(cValueSet, "size", RUBY_METHOD_FUNC(value_set_size), 0);
349
385
  rb_define_method(cValueSet, "clear", RUBY_METHOD_FUNC(value_set_clear), 0);
@@ -1,17 +1,10 @@
1
+ require 'utilrb/enumerable/to_s_helper'
1
2
  class Array
2
3
  # Displays arrays as [ a, b, [c, d], ... ] instead of the standard #join
3
4
  # Unlike #inspect, it calls #to_s on the elements too
4
5
  def to_s
5
- stack = (Thread.current[:array_to_s] ||= [])
6
- if stack.include?(object_id)
7
- "..."
8
- else
9
- begin
10
- stack.push object_id
11
- "[" << map { |obj| obj.to_s }.join(", ") << "]"
12
- ensure
13
- stack.pop
14
- end
6
+ EnumerableToString.to_s_helper(self, '[', ']') do |obj|
7
+ obj.to_s
15
8
  end
16
9
  end
17
10
  end
@@ -0,0 +1,73 @@
1
+ # Displays a set of data into a column-formatted table
2
+ class ColumnFormatter
3
+ MARGIN = 3
4
+ SCREEN_WIDTH = 90
5
+
6
+ # data is an array of hashes. Each line of the array is a line to display,
7
+ # a hash being a column_name => value data set. The special 'label' column
8
+ # name is used to give a name to each line.
9
+ #
10
+ # If a block is given, the method yields the column names and the block must
11
+ # return the array of columns to be displayed, sorted into the order of
12
+ # the columns.
13
+ def self.from_hashes(data, io = STDOUT, margin = MARGIN, screen_width = SCREEN_WIDTH)
14
+ width = Hash.new
15
+
16
+ # First, determine the columns width and height, and
17
+ # convert data into strings
18
+ data = data.map do |line_data|
19
+ line_data = line_data.inject({}) do |h, (label, data)|
20
+ h[label.to_s] = data.to_s
21
+ h
22
+ end
23
+
24
+ line_data.each do |label, data|
25
+ unless width.has_key?(label)
26
+ width[label] = label.length
27
+ end
28
+
29
+ if width[label] < data.length
30
+ width[label] = data.length
31
+ end
32
+ end
33
+ end
34
+
35
+ # Then ask the user to sort the keys for us
36
+ names = width.keys.dup
37
+ names.delete('label')
38
+ names = if block_given? then yield(names)
39
+ else names.sort
40
+ end
41
+
42
+ # Finally, format and display
43
+ while !names.empty?
44
+ # we determine the set of columns to display
45
+ if width.has_key?('label')
46
+ line_n = ['label']
47
+ line_w = width['label']
48
+ format = ["% #{line_w}s"]
49
+ else
50
+ line_n = []
51
+ line_w = 0
52
+ format = []
53
+ end
54
+
55
+ while !names.empty? && (line_w < screen_width)
56
+ col_n = names.shift
57
+ col_w = width[col_n]
58
+ line_n << col_n
59
+ line_w += col_w
60
+ format << "% #{col_w}s"
61
+ end
62
+
63
+ format = format.join(" " * margin)
64
+ io.puts format % line_n
65
+ data.each do |line_data|
66
+ line_data = line_data.values_at(*line_n)
67
+ line_data.map! { |v| v || '-' }
68
+ io.puts format % line_data
69
+ end
70
+ end
71
+ end
72
+ end
73
+
data/lib/utilrb/common.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Utilrb
2
- VERSION = "1.0" unless defined? Utilrb::VERSION
2
+ VERSION = "1.1" unless defined? Utilrb::VERSION
3
3
 
4
4
  unless defined? UTILRB_FASTER_MODE
5
5
  if ENV['UTILRB_FASTER_MODE'] == 'no'
@@ -2,9 +2,9 @@ require 'enumerator'
2
2
 
3
3
  # An enumerator which iterates on multiple iterators in sequence
4
4
  #
5
- # ([1, 2].enum_for + [2, 3].enum_for).each => 1, 2, 2, 3
5
+ # ([1, 2].enum_for + [2, 3].enum_for).to_a # => [1, 2, 2, 3]
6
6
  #
7
- # See also Enumerator#+
7
+ # See also Enumerable::Enumerator#+
8
8
  class SequenceEnumerator
9
9
  def initialize; @sequence = Array.new end
10
10
 
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  class Enumerable::Enumerator # :nodoc
24
24
  # Builds a sequence of enumeration object.
25
- # ([1, 2].enum_for + [2, 3]).each => 1, 2, 2, 3
25
+ # ([1, 2].enum_for + [2, 3].enum_for).to_a # => [1, 2, 2, 3]
26
26
  def +(other_enumerator) # :nodoc
27
27
  SequenceEnumerator.new << self << other_enumerator
28
28
  end
@@ -0,0 +1,17 @@
1
+ module EnumerableToString
2
+ # This method is a generic implementaion of #to_s on enumerables.
3
+ def self.to_s_helper(enumerable, start, stop)
4
+ stack = (Thread.current[:to_s_helper] ||= [])
5
+ if stack.include?(enumerable.object_id)
6
+ "..."
7
+ else
8
+ begin
9
+ stack.push enumerable.object_id
10
+ start.dup << enumerable.map { |el| yield(el) }.join(", ") << stop
11
+ ensure
12
+ stack.pop
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -2,11 +2,19 @@ require 'utilrb/common'
2
2
  require 'enumerator'
3
3
  require 'set'
4
4
 
5
- # Enumerator object which removes duplicate entries. See
6
- # Kernel#each_uniq
5
+ # Enumerator object which removes duplicate entries.
6
+ # See also Object#enum_uniq and Enumerable#each_uniq
7
7
  class UniqEnumerator < Enumerable::Enumerator
8
- def initialize(root, enum_with, args, key = nil)
9
- super(root, enum_with, *args)
8
+ # Creates the enumerator on +obj+ using the method +enum_with+ to
9
+ # enumerate. The method will be called with the arguments in +args+.
10
+ #
11
+ # If +key+ is given, it is a proc object which should return the key on
12
+ # which we base ourselves to compare two objects. If it is not given,
13
+ # UniqEnumerator uses the object itself
14
+ #
15
+ # See also Object#enum_uniq and Enumerable#each_uniq
16
+ def initialize(obj, enum_with, args, key = nil)
17
+ super(obj, enum_with, *args)
10
18
  @key = key
11
19
  @result = Hash.new
12
20
  end
@@ -29,12 +37,12 @@ class UniqEnumerator < Enumerable::Enumerator
29
37
  self
30
38
  end
31
39
  end
32
-
33
- include Enumerable
34
40
  end
35
41
 
36
42
  class Object
37
- # Enumerate removing the duplicate entries
43
+ # Enumerate using the <tt>each(*args)</tt> method, removing the duplicate
44
+ # entries. If +filter+ is given, it should return an object the enumerator
45
+ # will compare for equality (instead of using the objects themselves)
38
46
  def enum_uniq(enum_with = :each, *args, &filter)
39
47
  UniqEnumerator.new(self, enum_with, args, filter)
40
48
  end
@@ -43,10 +51,9 @@ end
43
51
  Utilrb.unless_faster do
44
52
  module Enumerable
45
53
  # call-seq:
46
- # enum.each_uniq { |obj| ... }
54
+ # each_uniq { |obj| ... }
47
55
  #
48
56
  # Yields all unique values found in +enum+
49
- #
50
57
  def each_uniq(&iterator)
51
58
  seen = Set.new
52
59
  each do |obj|