utilrb 1.0 → 1.1

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.
@@ -1,8 +1,11 @@
1
+ require 'utilrb/enumerable/to_s_helper'
1
2
  class Hash
2
3
  # Displays hashes as { a => A, b => B, ... } instead of the standard #join
3
4
  # Unlike #inspect, it calls #to_s on the elements too
4
5
  def to_s
5
- "{" << map { |k, v| "#{k} => #{v}" }.join(", ") << "}"
6
+ EnumerableToString.to_s_helper(self, '{', '}') do |k, v|
7
+ "#{k} => #{v}"
8
+ end
6
9
  end
7
10
  end
8
11
 
@@ -6,8 +6,8 @@ module Kernel
6
6
  # converted to symbols for consistency.
7
7
  #
8
8
  # The following rules apply:
9
- # * if a hash is given, non-nil values are treated as default values.
10
- # * an array is equivalent to a hash where all values are 'nil'
9
+ # * if a hash is given, non-nil values are treated as default values.
10
+ # * an array is equivalent to a hash where all values are 'nil'
11
11
  #
12
12
  # See #validate_options and #filter_and_validate_options
13
13
  #
@@ -54,7 +54,7 @@ module Kernel
54
54
  end
55
55
 
56
56
  # call-seq:
57
- # validate_option(options, name, required, message) { |v| ... }
57
+ # validate_option(options, name, required, message) { |v| ... }
58
58
  # validate_option(options, name, required, message)
59
59
  #
60
60
  # Validates option +name+ in the +options+ hash. If required is true,
@@ -1,9 +1,13 @@
1
1
  class Logger
2
2
  # Forward logger output methods to the logger attribute, so that
3
3
  # we can do
4
+ # module MyModule
5
+ # extend Logger::Forward
6
+ # end
4
7
  # MyModule.debug "debug_info"
5
8
  # instead of
6
9
  # MyModule.logger.debug "debug_info"
10
+ #
7
11
  module Forward
8
12
  [ :debug, :info, :warn, :error, :fatal, :unknown ].each do |level|
9
13
  class_eval <<-EOF
@@ -12,12 +12,12 @@ class Module
12
12
  # singleton.has_ancestor?(MyClass) # => true
13
13
  #
14
14
  def has_ancestor?(klass)
15
- self == klass || self < klass || (is_singleton? && superclass.has_ancestor?(klass))
15
+ self <= klass || (is_singleton? && superclass.has_ancestor?(klass))
16
16
  end
17
17
  end
18
18
  Utilrb.unless_faster do
19
19
  def has_ancestor?(klass) # :nodoc:
20
- self == klass || self < klass || superclass == klass || superclass < klass
20
+ self <= klass || superclass == klass || superclass < klass
21
21
  end
22
22
  end
23
23
  end
@@ -1,14 +1,27 @@
1
1
  require 'utilrb/object/attribute'
2
2
 
3
3
  class Module
4
- # Define 'name' to be a read-only enumerable attribute. The method
5
- # defines a +attr_name+ read-only attribute and an enumerator method
6
- # each_#{name}. +init_block+ is used to initialize the attribute.
4
+ # Support for attributes that are enumerables. This methods defines two
5
+ # methods:
6
+ # obj.attr_name # => enumerable
7
+ # obj.each_name(key = nil) { |value| ... } # => obj
7
8
  #
8
- # The enumerator method accepts a +key+ argument. If the attribute is
9
- # a key => enumerable map, then the +key+ attribute can be used to iterate
10
- #
11
- # +enumerator+ is the name of the enumeration method
9
+ # The first one returns the enumerable object itself. The second one
10
+ # iterates on the values in attr_name. If +key+ is not nil, then #attr_name
11
+ # is supposed to be a hash of enumerables, and +key+ is used to select the
12
+ # enumerable to iterate on.
13
+ #
14
+ # The following calls are equivalent
15
+ # obj.attr_name.each { |value| ... }
16
+ # obj.each_name { |value| ... }
17
+ #
18
+ # And these two are equivalent:
19
+ # obj.attr_name[key].each { |value| ... }
20
+ # obj.each_name(key) { |value| ... }
21
+ #
22
+ # +enumerator+ is the name of the enumeration method we should use.
23
+ # +init_block+, if given, should return the value at which we should
24
+ # initialize #attr_name.
12
25
  def attr_enumerable(name, attr_name = name, enumerator = :each, &init_block)
13
26
  class_eval do
14
27
  attribute(attr_name, &init_block)
@@ -21,6 +34,7 @@ class Module
21
34
  else
22
35
  #{attr_name}.#{enumerator}(&iterator)
23
36
  end
37
+ self
24
38
  end
25
39
  EOF
26
40
  end
@@ -1,8 +1,9 @@
1
1
  class Module
2
- # Defines a +name+ predicate (name should end with '?'), and if writable is true
3
- # a writer method which is +name+ without '?'
2
+ # Defines a +name?+ predicate, and if writable is true a #name= method.
3
+ # Note that +name+ can end with '?', in which case the ending '?' is
4
+ # removed.
4
5
  #
5
- # The predicate reads the instance variable which is +name+ without the '?'
6
+ # The methods use the @name instance variable internally
6
7
  def attr_predicate(name, writable = false)
7
8
  attr_name = name.to_s.gsub(/\?$/, '')
8
9
  attr_reader attr_name
@@ -0,0 +1,26 @@
1
+ class Module
2
+ # Creates <tt>enum_#{name}</tt> method which returs an Enumerator object
3
+ # for the <tt>each_#{enum_name}</tt> method. This enumerator is created
4
+ # once.
5
+ #
6
+ # If +with_arg+ is true, it is supposed that the 'each_' method requires
7
+ # one argument, which is given in argument of the 'enum' method. In that
8
+ # case, an enumerator is created for each argument
9
+ def cached_enum(enum_name, name, with_arg)
10
+ if with_arg
11
+ class_eval <<-EOD
12
+ def enum_#{name}(arg)
13
+ @enum_#{name} ||= Hash.new
14
+ @enum_#{name}[arg] ||= enum_for(:each_#{enum_name}, arg)
15
+ end
16
+ EOD
17
+ else
18
+ class_eval <<-EOD
19
+ def enum_#{name}
20
+ @enum_#{name} ||= enum_for(:each_#{enum_name})
21
+ end
22
+ EOD
23
+ end
24
+ end
25
+ end
26
+
@@ -16,7 +16,7 @@ class Module
16
16
  # define_method_with_block('my_method') do |block, *args|
17
17
  # end
18
18
  #
19
- # +block+ is +nil+ if no block is given on the method call
19
+ # +block+ is +nil+ if no block is given during the method call
20
20
  #
21
21
  def define_method_with_block(name, &mdef)
22
22
  class_eval <<-EOD
@@ -6,11 +6,11 @@ class Module
6
6
  # Includes a module in this one, with support for class extensions
7
7
  #
8
8
  # If a module defines a ClassExtension submodule, then
9
- # * if it is included in a module, the target's ClassExtension
10
- # module includes the source ClassExtension (and if there is no
11
- # ClassExtension in the target, it is created)
12
- # * if it is included in a Class, the ClassExtension module
13
- # extends the class.
9
+ # * if it is included in a module, the target's ClassExtension
10
+ # module includes the source ClassExtension (and if there is no
11
+ # ClassExtension in the target, it is created)
12
+ # * if it is included in a Class, the ClassExtension module
13
+ # extends the class.
14
14
  def include(mod)
15
15
  __instance_include__ mod
16
16
  return unless mod.const_defined?(:ClassExtension)
@@ -15,37 +15,54 @@ class Module
15
15
  class_eval <<-EOF
16
16
  def each_#{name}(key = nil, uniq = true)
17
17
  if key
18
- if #{attribute_name}.has_key?(key)
19
- yield(#{attribute_name}[key])
20
- return self if uniq
18
+ for klass in ancestors
19
+ if klass.instance_variable_defined?(:@#{attribute_name})
20
+ if klass.#{attribute_name}.has_key?(key)
21
+ yield(klass.#{attribute_name}[key])
22
+ return self if uniq
23
+ end
24
+ end
25
+ end
26
+ elsif !uniq
27
+ for klass in ancestors
28
+ if klass.instance_variable_defined?(:@#{attribute_name})
29
+ klass.#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
30
+ end
21
31
  end
22
- elsif uniq
23
- @enum_#{name}_uniq ||= enum_uniq(:each_#{name}, nil, false) { |k, v| k }
24
- @enum_#{name}_uniq.each { |el| yield(el) }
25
- return self
26
32
  else
27
- #{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
28
- end
29
- if superclass = ancestors[1..-1].find { |k| k.respond_to?(:each_#{name}) }
30
- superclass.each_#{name}(key, uniq) { |el| yield(el) }
33
+ seen = Set.new
34
+ for klass in ancestors
35
+ if klass.instance_variable_defined?(:@#{attribute_name})
36
+ klass.#{attribute_name}.#{options[:enum_with]} do |el|
37
+ unless seen.include?(el.first)
38
+ seen << el.first
39
+ yield(el)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
31
45
  end
32
46
  self
33
47
  end
34
48
  def has_#{name}?(key)
35
- return true if #{attribute_name}[key]
36
- if superclass = ancestors[1..-1].find { |k| k.respond_to?(:has_#{name}) }
37
- superclass.has_#{name}(key)
49
+ for klass in ancestors
50
+ if klass.instance_variable_defined?(:@#{attribute_name})
51
+ return true if klass.#{attribute_name}.has_key?(key)
52
+ end
38
53
  end
54
+ false
39
55
  end
40
56
  EOF
41
57
  else
42
58
  class_eval <<-EOF
43
- def each_#{name}(&iterator)
44
- #{attribute_name}.#{options[:enum_with]}(&iterator) if #{attribute_name}
45
- if superclass = ancestors[1..-1].find { |k| k.respond_to?(:each_#{name}) }
46
- superclass.each_#{name} { |el| yield(el) }
59
+ def each_#{name}
60
+ for klass in ancestors
61
+ if klass.instance_variable_defined?(:@#{attribute_name})
62
+ klass.#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
63
+ end
47
64
  end
48
- self
65
+ self
49
66
  end
50
67
  EOF
51
68
  end
@@ -69,38 +86,80 @@ class Module
69
86
  # define a +each_value+ method). +attribute_name+ defines the attribute
70
87
  # name. +init+ is a block called to initialize the attribute.
71
88
  # Valid options in +options+ are:
72
- # map:: the attribute should respond to +[]+. The enumeration method takes two
73
- # arguments, +key+ and +uniq+. If +key+ is given, we iterate on the values
74
- # given by <tt>attribute[key]</tt>. If +uniq+ is true, the enumeration will
75
- # yield at most one value for each +key+ found (so, if both +key+ and +uniq+ are
76
- # given, the enumeration yields at most one value)
89
+ # map::
90
+ # If true, the attribute should respond to +[]+. In that case, the
91
+ # enumeration method is each_value(key = nil, uniq = false) If +key+ is
92
+ # given, we iterate on the values given by <tt>attribute[key]</tt>. If
93
+ # +uniq+ is true, the enumeration will yield at most one value for each
94
+ # +key+ found (so, if both +key+ and +uniq+ are given, the enumeration
95
+ # yields at most one value). See the examples below
96
+ # enum_with:: the enumeration method of the enumerable, if it is not +each+
97
+ #
98
+ # === Example
99
+ # Let's define some classes and look at the ancestor chain
100
+ #
101
+ # class A; end
102
+ # module M; end
103
+ # class B < A; include M end
104
+ # A.ancestors # => [A, Object, Kernel]
105
+ # B.ancestors # => [B, M, A, Object, Kernel]
106
+ #
107
+ # ==== Attributes for which 'map' is not set
108
+ #
109
+ # class A
110
+ # inherited_enumerable("value", "values") do
111
+ # Array.new
112
+ # end
113
+ # end
114
+ # module M
115
+ # inherited_enumerable("mod") do
116
+ # Array.new
117
+ # end
118
+ # end
119
+ #
120
+ # A.values << 1 # => [1]
121
+ # B.values << 2 # => [2]
122
+ # M.mod << 1 # => [1]
123
+ # b = B.new
124
+ # class << b
125
+ # self.values << 3 # => [3]
126
+ # self.mod << 4 # => [4]
127
+ # end
128
+ # M.mod << 2 # => [1, 2]
129
+ #
130
+ # A.enum_for(:each_value).to_a # => [1]
131
+ # B.enum_for(:each_value).to_a # => [2, 1]
132
+ # b.singleton_class.enum_for(:each_value).to_a # => [3, 2, 1]
133
+ # b.singleton_class.enum_for(:each_mod).to_a # => [4, 1, 2]
77
134
  #
78
- # For instance
135
+ # ==== Attributes for which 'map' is set
79
136
  #
80
- # class A
81
- # class_inherited_enumerable("value", "enum") { Array.new }
82
- # end
83
- # module M
84
- # inherited_enumerable("attr") { Array.new }
85
- # end
86
- # class B < A
87
- # include M
88
- # end
89
- # b = B.new
137
+ # class A
138
+ # inherited_enumerable("mapped", "map", :map => true) do
139
+ # Hash.new { |h, k| h[k] = Array.new }
140
+ # end
141
+ # end
142
+ #
143
+ # A.map['name'] = 'A' # => "A"
144
+ # A.map['universe'] = 42
145
+ # B.map['name'] = 'B' # => "B"
146
+ # B.map['half_of_it'] = 21
147
+ #
148
+ # Let's see what happens if we don't specify the key option.
149
+ # A.enum_for(:each_mapped).to_a # => [["name", "A"], ["universe", 42]]
150
+ # If the +uniq+ option is set (the default), we see only B's value for 'name'
151
+ # B.enum_for(:each_mapped).to_a # => [["half_of_it", 21], ["name", "B"], ["universe", 42]]
152
+ # If the +uniq+ option is not set, we see both values for 'name'. Note that
153
+ # since 'map' is a Hash, the order of keys in one class is not guaranteed.
154
+ # Nonetheless, we have the guarantee that values from B appear before
155
+ # those from A
156
+ # B.enum_for(:each_mapped, nil, false).to_a # => [["half_of_it", 21], ["name", "B"], ["name", "A"], ["universe", 42]]
90
157
  #
91
- # A.enum << 1
92
- # B.enum << 2
93
- # M.attr << 1
94
- # class << b
95
- # enum << 3
96
- # attr << 4
97
- # end
98
- # M.attr << 2
99
158
  #
100
- # A.each_enum => 1
101
- # B.each_enum => 2, 1
102
- # b.singleton_class.each_enum => 3, 2, 1
103
- # b.singleton_class.each_attr => 4, 1, 2
159
+ # Now, let's see how 'key' behaves
160
+ # A.enum_for(:each_mapped, 'name').to_a # => ["A"]
161
+ # B.enum_for(:each_mapped, 'name').to_a # => ["B"]
162
+ # B.enum_for(:each_mapped, 'name', false).to_a # => ["B", "A"]
104
163
  #
105
164
  def inherited_enumerable(name, attribute_name = name, options = Hash.new, &init)
106
165
  singleton_class.class_eval { define_inherited_enumerable(name, attribute_name, options, &init) }
@@ -39,49 +39,35 @@ end
39
39
 
40
40
  Utilrb.if_faster do
41
41
  class Object
42
- # :call-seq
43
- # attribute :name => default_value
44
- # attribute(:name) { default_value }
45
- #
46
- # In the first form, defines a read-write attribute
47
- # named 'name' with default_value for default value.
48
- # In the second form, the block is called if the attribute
49
- # is read before it has been ever written, and its return
50
- # value is used as default value.
51
- def attribute(attr_def, &init)
42
+ def attribute(attr_def, &init) # :nodoc:
52
43
  if Hash === attr_def
53
44
  name, defval = attr_def.to_a.flatten
54
45
  else
55
46
  name = attr_def
56
47
  end
57
48
 
58
- if is_singleton? || instance_of?(Module)
59
- class_eval do
60
- attr_writer name
61
- define_method("#{name}_defval") do
62
- defval || (instance_eval(&init) if init)
63
- end
49
+ class_eval do
50
+ attr_writer name
51
+ if !defval && init
52
+ define_method("#{name}_defval", &init)
53
+ else
54
+ define_method("#{name}_defval") { defval }
64
55
  end
56
+ end
65
57
 
66
- class_eval <<-EOD
67
- def #{name}
68
- if defined? @#{name} then @#{name}
69
- else @#{name} = #{name}_defval
70
- end
71
- end
72
- EOD
73
- else
74
- class_eval { attr_writer name }
75
- define_method(name) do
76
- singleton_class.class_eval { attr_reader name }
77
- instance_variable_set("@#{name}", defval || (instance_eval(&init) if init))
58
+ class_eval <<-EOD
59
+ def #{name}
60
+ if defined? @#{name} then @#{name}
61
+ else @#{name} = #{name}_defval
78
62
  end
79
63
  end
64
+ EOD
80
65
  end
81
66
  end
82
67
  end
83
68
 
84
69
  class Object
70
+ # Like #attribute, but on the singleton class of this object
85
71
  def class_attribute(attr_def, &init)
86
72
  singleton_class.class_eval { attribute(attr_def, &init) }
87
73
  end
@@ -7,20 +7,16 @@ end
7
7
 
8
8
  if RUBY_VERSION >= "1.9"
9
9
  class Object
10
- # Returns the singleton class for this object
11
- #
12
- # The first element of #ancestors on the returned singleton class is
13
- # the singleton class itself. A #singleton_instance accessor is also
14
- # defined, which returns the object instance the class is the singleton
15
- # of.
16
- def singleton_class
10
+ def singleton_class # :nodoc:
17
11
  if defined? @singleton_class
18
12
  return @singleton_class
19
13
  else
20
14
  @singleton_class = class << self
21
15
  class << self
22
- alias __ancestors__ ancestors
23
- def ancestors; __ancestors__.unshift(self) end
16
+ alias __ancestors__ ancestors # :nodoc:
17
+ def ancestors # :nodoc:
18
+ __ancestors__.unshift(self)
19
+ end
24
20
  end
25
21
 
26
22
  self
@@ -57,8 +53,10 @@ else
57
53
  "#{@superclass.name}!0x#{@singleton_instance.address.to_s(16)}"
58
54
  end
59
55
 
60
- alias __ancestors__ ancestors
61
- def ancestors; __ancestors__.unshift(self) end
56
+ alias __ancestors__ ancestors # :nodoc:
57
+ def ancestors # :nodoc:
58
+ __ancestors__.unshift(self)
59
+ end
62
60
  end
63
61
  end
64
62