utilrb 3.1.0 → 3.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dfc6c628c67691e4051455e910ea27774e48d1f8
4
- data.tar.gz: 3985cf51ce618e65f82a503ec1d3708a76986c0d
2
+ SHA256:
3
+ metadata.gz: e7c52ce8cf8f86508f209e723a1dc3db6b0f6715cf16c241a8040513d4be8075
4
+ data.tar.gz: e9d068b547eed7e0343ca896006546824da96ba5615d8ef90e75a0b5b4856678
5
5
  SHA512:
6
- metadata.gz: 7c0826bbdc14832b30ee1f08585b04f8ae203bcaca23eebf9b2a8441e2c63599a12e8d9a774c673b01652530f0aff811162dc21840cc3aed36d5950878a70243
7
- data.tar.gz: 7a2e844418cc195a2515bb2575f684ed4d264c481a2ed19713e688c7ada8802d06c85feca9b30009e1579b012f87920d444a366487dc1ccb0d0915e19636d95b
6
+ metadata.gz: a89739a884d6acbb494a868eb93e4b0510deda9f5ce5aae6553ca31810434ba79e5bf267ab9b242e96ac13f9f9bb25cf2e18b83b543fb8bbbbd7a693a0e98d8c
7
+ data.tar.gz: 8f09934f484ceadaf67c6be2a089f776f94040164e11de00890ed9075818818687f5864a2f6b6d39a1119a9acadb58f01afe2936c27348ce428743f070f27fd7
@@ -4,7 +4,7 @@ require 'utilrb/thread_pool'
4
4
  module Utilrb
5
5
  # Simple event loop which supports timers and defers blocking operations to
6
6
  # a thread pool those results are queued and being processed by the event
7
- # loop thread at the end of each step.
7
+ # loop thread at the end of each step.
8
8
  #
9
9
  # All events must be code blocks which will be executed at the end of each step.
10
10
  # There is no support for filtering or event propagations.
@@ -13,17 +13,17 @@ module Utilrb
13
13
  # can be used.
14
14
  #
15
15
  # @example Example for using the EventLoop
16
- # event_loop = EventLoop.new
17
- # event_loop.once do
16
+ # event_loop = EventLoop.new
17
+ # event_loop.once do
18
18
  # puts "called once"
19
19
  # end
20
20
  #
21
- # event_loop.every(1.0) do
21
+ # event_loop.every(1.0) do
22
22
  # puts "called every second"
23
23
  # end
24
24
  #
25
25
  # callback = Proc.new |result|
26
- # puts result
26
+ # puts result
27
27
  # end
28
28
  # event_loop.defer callback do
29
29
  # sleep 2
@@ -31,14 +31,14 @@ module Utilrb
31
31
  # end
32
32
  #
33
33
  # event_loop.exec
34
- #
34
+ #
35
35
  # @author Alexander Duda <Alexander.Duda@dfki.de>
36
36
  class EventLoop
37
37
  # Timer for the {EventLoop} which supports single shot and periodic activation
38
38
  #
39
39
  # @example
40
40
  # loop = EventLoop.new
41
- # timer = EventLoop.every(0.1) do
41
+ # timer = EventLoop.every(0.1) do
42
42
  # puts 123
43
43
  # end
44
44
  # loop.exec
@@ -52,15 +52,15 @@ module Utilrb
52
52
  # A timer
53
53
  #
54
54
  # @param [EventLoop] event_loop the {EventLoop} the timer belongs to
55
- # @param[Float] period The period of the timer in seconds.
56
- # @param[Boolean] single_shot if true the timer will fire only once
57
- # @param[#call] block The code block which will be executed each time the timer fires
55
+ # @param [Float] period The period of the timer in seconds.
56
+ # @param [Boolean] single_shot if true the timer will fire only once
57
+ # @param [#call] block The code block which will be executed each time the timer fires
58
58
  # @see EventLoop#once
59
59
  def initialize(event_loop,period=0,single_shot=false,&block)
60
60
  @block = block
61
61
  @event_loop = event_loop
62
62
  @last_call = Time.now
63
- @period = period
63
+ @period = Float(period)
64
64
  @single_shot = single_shot
65
65
  @stopped = true
66
66
  @doc = Kernel.caller.find do |s|
@@ -75,7 +75,7 @@ module Utilrb
75
75
  end
76
76
 
77
77
  def stopped?
78
- @stopped
78
+ @stopped
79
79
  end
80
80
 
81
81
  # Returns true if the timer is currently running.
@@ -128,7 +128,7 @@ module Utilrb
128
128
  @single_shot == true
129
129
  end
130
130
 
131
- # Executes the code block tight to the timer and
131
+ # Executes the code block tight to the timer and
132
132
  # saves a time stamp.
133
133
  #
134
134
  # @param [Time] time The time stamp
@@ -163,7 +163,7 @@ module Utilrb
163
163
  def ignore!
164
164
  @ignore = true
165
165
  end
166
-
166
+
167
167
  def ignore?
168
168
  @ignore
169
169
  end
@@ -242,7 +242,7 @@ module Utilrb
242
242
  # queue the call if the task blocks for ever and it will never simultaneously
243
243
  # defer the call to more than one worker thread.
244
244
  #
245
- # @param [Hash] options The options
245
+ # @param [Hash] options The options
246
246
  # @option options [Float] :period The period
247
247
  # @option options [Boolean] :start Starts the timer right away (default = true)
248
248
  # @param [#call] work The proc which will be deferred
@@ -286,12 +286,12 @@ module Utilrb
286
286
  # else
287
287
  # puts r
288
288
  # end
289
- # end
289
+ # end
290
290
  # defer({:callback => callback}) do
291
291
  # raise
292
292
  # end
293
293
  #
294
- # @param [Hash] options The options
294
+ # @param [Hash] options The options
295
295
  # @option (see ThreadPool::Task#initialize)
296
296
  # @option options [Proc] :callback The callback
297
297
  # @option options [class] :known_errors Known erros which will be rescued
@@ -344,9 +344,7 @@ module Utilrb
344
344
  end
345
345
  end
346
346
  end
347
- @mutex.synchronize do
348
- @thread_pool << task
349
- end
347
+ add_task task
350
348
  task
351
349
  end
352
350
 
@@ -358,6 +356,7 @@ module Utilrb
358
356
  # @return [Utilrb::EventLoop::Timer,Event]
359
357
  def once(delay=nil,&block)
360
358
  raise ArgumentError "no block given" unless block
359
+
361
360
  if delay && delay > 0
362
361
  timer = Timer.new(self,delay,true,&block)
363
362
  timer.start(timer.period, false)
@@ -368,7 +367,7 @@ module Utilrb
368
367
 
369
368
  # Calls the give block in the event loop thread. If the current thread
370
369
  # is the event loop thread it will execute it right a way and returns
371
- # the result of the code block call. Otherwise, it returns an handler to
370
+ # the result of the code block call. Otherwise, it returns an handler to
372
371
  # the Event which was queued.
373
372
  #
374
373
  #@return [Event,Object]
@@ -387,7 +386,7 @@ module Utilrb
387
386
  !@events.empty? || !@errors.empty?
388
387
  end
389
388
 
390
- # Adds a timer to the event loop which will execute
389
+ # Adds a timer to the event loop which will execute
391
390
  # the given code block with the given period from the
392
391
  # event loop thread.
393
392
  #
@@ -422,7 +421,7 @@ module Utilrb
422
421
  end
423
422
 
424
423
  # Errors caught during event loop callbacks are forwarded to
425
- # registered code blocks. The code blocks are called from
424
+ # registered code blocks. The code blocks are called from
426
425
  # the event loop thread.
427
426
  #
428
427
  # @param @error_classes The error classes the block should be called for
@@ -442,7 +441,7 @@ module Utilrb
442
441
  raise "current thread is not the event loop thread" if !thread?
443
442
  end
444
443
 
445
- # Returns true if the current thread is the
444
+ # Returns true if the current thread is the
446
445
  # event loop thread.
447
446
  #
448
447
  # @return [Boolean]
@@ -487,7 +486,7 @@ module Utilrb
487
486
 
488
487
  # Cancels the given timer if it is running otherwise
489
488
  # it does nothing.
490
- #
489
+ #
491
490
  # @param [Timer] timer The timer
492
491
  def cancel_timer(timer)
493
492
  @mutex.synchronize do
@@ -495,12 +494,12 @@ module Utilrb
495
494
  end
496
495
  end
497
496
 
498
- # Resets all timers to fire not before their hole
497
+ # Resets all timers to fire not before their hole
499
498
  # period is passed counting from the given point in time.
500
499
  #
501
500
  # @param [Time] time The time
502
501
  def reset_timers(time = Time.now)
503
- @mutex.synchronize do
502
+ @mutex.synchronize do
504
503
  @timers.each do |timer|
505
504
  timer.reset time
506
505
  end
@@ -529,10 +528,12 @@ module Utilrb
529
528
  @stop = true
530
529
  end
531
530
 
532
- # Steps with the given period until the given
531
+ class WaitForTimeout < RuntimeError; end
532
+
533
+ # Steps with the given period until the given
533
534
  # block returns true.
534
535
  #
535
- # @param [Float] period The period
536
+ # @param [Float] period The period
536
537
  # @param [Float] timeout The timeout in seconds
537
538
  # @yieldreturn [Boolean]
538
539
  def wait_for(period=0.05,timeout=nil,&block)
@@ -541,7 +542,7 @@ module Utilrb
541
542
  exec period do
542
543
  stop if block.call
543
544
  if timeout && timeout <= (Time.now-start).to_f
544
- raise RuntimeError,"Timeout during wait_for"
545
+ raise WaitForTimeout,"timed out during wait_for"
545
546
  end
546
547
  end
547
548
  @stop = old_stop
@@ -587,7 +588,7 @@ module Utilrb
587
588
  validate_thread
588
589
  reraise_error(@errors.shift) if !@errors.empty?
589
590
 
590
- #copy all work otherwise it would not be allowed to
591
+ #copy all work otherwise it would not be allowed to
591
592
  #call any event loop functions from a timer
592
593
  timers,call = @mutex.synchronize do
593
594
  @every_cylce_events.delete_if(&:ignore?)
@@ -620,7 +621,7 @@ module Utilrb
620
621
  end
621
622
  handle_errors{call.call} if call
622
623
  reraise_error(@errors.shift) if !@errors.empty?
623
-
624
+
624
625
  #allow thread pool to take over
625
626
  Thread.pass
626
627
  end
@@ -655,7 +656,9 @@ module Utilrb
655
656
  #
656
657
  # @param [ThreadPool::Task] task The task.
657
658
  def add_task(task)
658
- thread_pool << task
659
+ @mutex.synchronize do
660
+ @thread_pool << task
661
+ end
659
662
  end
660
663
 
661
664
  # Clears all timers, events and errors
@@ -670,29 +673,28 @@ module Utilrb
670
673
  end
671
674
  end
672
675
 
673
- # Clears all errors which occurred during the last step and are not marked as known
676
+ # Clears all errors which occurred during the last step and are not marked as known
674
677
  # If the errors were not cleared they are re raised the next time step is called.
675
678
  def clear_errors
676
679
  @errors.clear
677
680
  end
678
681
 
679
- def handle_error(error,save = true)
682
+ def handle_error(error, save = true)
683
+ @errors << error if save
684
+
680
685
  call do
681
- on_error = @mutex.synchronize do
682
- @on_error.find_all{|key,e| error.is_a? key}.map(&:last).flatten
683
- end
686
+ on_error = @on_error.find_all{|key,e| error.is_a? key}.map(&:last).flatten
684
687
  on_error.each do |handler|
685
688
  handler.call error
686
689
  end
687
- @errors << error if save == true
688
690
  end
689
691
  end
690
692
 
691
693
  private
692
694
  # Calls the given block and rescues all errors which can be handled
693
- # by the added error handler. If an error cannot be handled it is
695
+ # by the added error handler. If an error cannot be handled it is
694
696
  # stored and re raised after all events and timers are processed. If
695
- # more than one error occurred which cannot be handled they are stored
697
+ # more than one error occurred which cannot be handled they are stored
696
698
  # until the next step is called and re raised until all errors are processed.
697
699
  #
698
700
  # @info This method must be called from the event loop thread, otherwise
@@ -741,7 +743,7 @@ module Utilrb
741
743
  # accessor.method(*args) but called from a thread pool. Thereby the code
742
744
  # block is used as callback called from the main thread after the
743
745
  # call returned. If an error occurred it will be:
744
- # * given to the callback as second argument
746
+ # * given to the callback as second argument
745
747
  # * forwarded to the error handlers of the event loop
746
748
  # * raised at the beginning of the next step if not marked as known error
747
749
  #
@@ -759,17 +761,17 @@ module Utilrb
759
761
  # end
760
762
  #
761
763
  # ali do |result,exception|
762
- # if exception
764
+ # if exception
763
765
  # :ignore_error
764
766
  # else
765
767
  # puts result
766
768
  # end
767
769
  # end
768
770
  #
769
- # If the callback accepts only one argument
771
+ # If the callback accepts only one argument
770
772
  # the callback will not be called in an event of an error but
771
773
  # the error will still be forwarded to the error handlers.
772
- #
774
+ #
773
775
  # If the result shall be filtered before returned a filter method can
774
776
  # be specified which is called from the event loop thread just before
775
777
  # the result is returned.
@@ -816,7 +818,7 @@ module Utilrb
816
818
  # @param [Symbol] method The method called on the designated object.
817
819
  # @param [Hash] options The options
818
820
  # @option options [Symbol] :alias The alias of the method
819
- # @option options [Symbol] :sync_key The sync key
821
+ # @option options [Symbol] :sync_key The sync key
820
822
  # @option options [Symbol] :filter The filter method
821
823
  # @option options [Symbol] :on_error Method which is called if an error occured
822
824
  # @option options [class] :known_errors Known errors which will be rescued but still be forwarded.
@@ -845,12 +847,12 @@ module Utilrb
845
847
 
846
848
  def options(options = Hash.new,&block)
847
849
  @stack << @stack.last.merge(options)
848
- block.call
850
+ block.call
849
851
  @stack.pop
850
852
  end
851
853
 
852
854
  def thread_safe(&block)
853
- options :sync_key => nil do
855
+ options :sync_key => nil do
854
856
  block.call
855
857
  end
856
858
  end
@@ -949,7 +951,7 @@ module Utilrb
949
951
  end
950
952
  end
951
953
 
952
- # Defines multiple method as delegator instance methods
954
+ # Defines multiple method as delegator instance methods
953
955
  # @see #def_event_loop_delegator
954
956
  def self.def_event_loop_delegators(klass,accessor,event_loop, *methods)
955
957
  methods.flatten!
@@ -1,5 +1,6 @@
1
1
  require 'utilrb/kernel/options'
2
2
 
3
+ unless Exception.method_defined?(:full_message)
3
4
  class Exception
4
5
  # Returns the full exception message, with backtrace, like the one we get from
5
6
  # the Ruby interpreter when the program is aborted (well, almost like that)
@@ -53,4 +54,4 @@ class Exception
53
54
  msg
54
55
  end
55
56
  end
56
-
57
+ end
@@ -1,6 +1,3 @@
1
- class Hash
2
- def slice(*keys)
3
- keys.inject({}) { |h, k| h[k] = self[k] if has_key?(k); h }
4
- end
5
- end
1
+ STDERR.puts "[DEPRECATED] utilrb/hash/slice is deprecated, use backports' backports/2.5.0/hash/slice instead or the built-in method in Ruby >= 2.5"
2
+ require 'backports/2.5.0/hash/slice'
6
3
 
@@ -1,5 +1,4 @@
1
1
  require 'utilrb/hash/to_sym_keys'
2
- require 'utilrb/hash/slice'
3
2
  module Kernel
4
3
  def filter_options_handle_single_entry(known_options, options, key)
5
4
  key = key.to_sym
@@ -60,11 +59,11 @@ module Kernel
60
59
  end
61
60
 
62
61
  # Validates an option hash, with default value support. See #filter_options
63
- #
64
- # In the first form, +option_hash+ should contain keys which are also
62
+ #
63
+ # In the first form, +option_hash+ should contain keys which are also
65
64
  # in known_hash. The non-nil values of +known_hash+ are used as default
66
65
  # values. In the second form, +known_array+ is an array of option
67
- # keys. +option_hash+ keys shall be in +known_array+. +nil+ is treated
66
+ # keys. +option_hash+ keys shall be in +known_array+. +nil+ is treated
68
67
  # as an empty option hash, all keys are converted into symbols.
69
68
  #
70
69
  def validate_options(options, *known_options)
@@ -1,5 +1,4 @@
1
1
  require 'facets/module/spacename'
2
- require 'facets/kernel/constant'
3
2
  require 'utilrb/object/attribute'
4
3
  require 'utilrb/logger/forward'
5
4
  require 'weakref'
@@ -28,6 +27,16 @@ class Logger
28
27
  log_children << WeakRef.new(child)
29
28
  end
30
29
 
30
+ def deregister_log_child(child)
31
+ log_children.delete_if do |ref|
32
+ begin
33
+ ref.__getobj__ == child
34
+ rescue WeakRef::RefError
35
+ true
36
+ end
37
+ end
38
+ end
39
+
31
40
  def each_log_child
32
41
  return enum_for(__method__) if !block_given?
33
42
 
@@ -41,36 +50,47 @@ class Logger
41
50
  end
42
51
  end
43
52
 
53
+ # @api private
54
+ #
55
+ # Resets the default logger of this context's children
56
+ #
57
+ # This is called whenever the context children is reset, since the
58
+ # cached default logger is now invalid
59
+ def reset_children_default_logger
60
+ children = log_children
61
+ @log_children = Array.new
62
+ children.each do |ref|
63
+ begin
64
+ ref.__getobj__.reset_default_logger
65
+ rescue WeakRef::RefError
66
+ end
67
+ end
68
+ end
69
+
44
70
  # Allows to change the logger object at this level of the hierarchy
45
71
  #
46
72
  # This is usually not used directly: a new logger can be created with
47
73
  # Hierarchy#make_own_logger and removed with Hierarchy#reset_own_logger
48
74
  def logger=(new_logger)
49
75
  @logger = new_logger
50
- each_log_child do |child|
51
- child.reset_default_logger
52
- end
76
+ reset_children_default_logger
53
77
  end
54
78
 
55
79
  # Removes a logger defined at this level of the module hierarchy. The
56
80
  # logging methods will now access the parent's module logger.
57
81
  def reset_own_logger
58
- @logger = nil
59
- each_log_child do |child|
60
- child.reset_default_logger
61
- end
82
+ self.logger = nil
62
83
  end
63
84
 
64
85
  def reset_default_logger
65
86
  @__utilrb_hierarchy__default_logger = nil
66
- each_log_child do |child|
67
- child.reset_default_logger
68
- end
87
+ @parent_module.deregister_log_child(self) if @parent_module
88
+ reset_children_default_logger
69
89
  end
70
90
 
71
91
  def logger
72
92
  if defined?(@logger) && @logger
73
- return @logger
93
+ return @logger
74
94
  elsif defined?(@__utilrb_hierarchy__default_logger) && @__utilrb_hierarchy__default_logger
75
95
  return @__utilrb_hierarchy__default_logger
76
96
  end
@@ -84,7 +104,7 @@ class Logger
84
104
  # attribute.
85
105
  #
86
106
  # This module is usually used in conjunction with the Logger::Root method:
87
- #
107
+ #
88
108
  # module First
89
109
  # extend Logger.Root("First", :INFO)
90
110
  #
@@ -120,7 +140,7 @@ class Logger
120
140
  def self.extended(obj) # :nodoc:
121
141
  obj.logger # initialize the default logger. Also does some checking
122
142
  if obj.kind_of?(Module) && !obj.spacename.empty?
123
- parent_module = constant(obj.spacename)
143
+ parent_module = const_get(obj.spacename)
124
144
  if (parent_module.singleton_class.ancestors.include?(::Logger::Forward))
125
145
  obj.send(:extend, ::Logger::Forward)
126
146
  end
@@ -141,7 +161,7 @@ class Logger
141
161
  if m.name && !m.spacename.empty?
142
162
  parent_module =
143
163
  begin
144
- constant(m.spacename)
164
+ const_get(m.spacename)
145
165
  rescue NameError
146
166
  end
147
167
  if parent_module.respond_to?(:logger)
@@ -164,6 +184,7 @@ class Logger
164
184
  raise NoParentLogger, "cannot find a logger for #{self}"
165
185
  end
166
186
  if parent_module.respond_to? :register_log_child
187
+ @parent_module = parent_module
167
188
  parent_module.register_log_child(self)
168
189
  end
169
190
  parent_module.logger
@@ -173,5 +194,3 @@ class Logger
173
194
  end
174
195
  end
175
196
  end
176
-
177
-
@@ -1,10 +1,13 @@
1
- class Module
1
+ # frozen_string_literal: true
2
+
3
+ class Module # rubocop:disable Style/Documentation
2
4
  # call-seq:
3
5
  # dsl_attribute(name)
4
6
  # dsl_attribute(name,name2,name3)
5
7
  # dsl_attribute(name) { |value| ... }
6
8
  #
7
- # This defines a +name+ instance method on the given class which accepts zero or one argument
9
+ # This defines a +name+ instance method on the given class which accepts
10
+ # zero or one argument
8
11
  #
9
12
  # Without any argument, it acts as a getter for the +@name+ attribute. With
10
13
  # one argument, it acts instead as a setter for the same attribute and
@@ -13,8 +16,8 @@ class Module
13
16
  # instance variable. This block can therefore both filter the value
14
17
  # (convert it to a desired form) and validate it.
15
18
  #
16
- # The goal of this method is to have a nicer way to handle attribute in DSLs: instead
17
- # of
19
+ # The goal of this method is to have a nicer way to handle attribute in
20
+ # DSLs: instead of
18
21
  #
19
22
  # model = create_model do
20
23
  # self.my_model_attribute = 'bla'
@@ -35,37 +38,46 @@ class Module
35
38
  # end
36
39
  #
37
40
  def dsl_attribute(*names, &filter_block)
38
- if names.size > 1 && filter_block
39
- raise ArgumentError, "multiple names as argument are only supported if no block is given"
41
+ if names.size > 1
42
+ if filter_block
43
+ raise ArgumentError,
44
+ "multiple names as argument are only supported if no block is given"
45
+ end
46
+
47
+ names.each { |name| dsl_attribute(name) }
48
+ return
40
49
  end
41
- names.each do |name|
42
- class_eval do
50
+
51
+ name = names.first
52
+
53
+ class_eval do
43
54
  if filter_block
44
55
  define_method("__dsl_attribute__#{name}__filter__", &filter_block)
45
56
  end
46
57
 
47
- define_method(name) do |*value|
48
- if value.empty?
49
- instance_variable_get("@#{name}")
50
- elsif filter_block
51
- if filter_block.arity >= 0 && value.size != filter_block.arity
52
- raise ArgumentError, "too many arguments. Got #{value.size}, expected #{filter_block.arity}"
53
- end
54
-
55
- filtered_value = send("__dsl_attribute__#{name}__filter__", *value)
56
- instance_variable_set("@#{name}", filtered_value)
57
- self
58
- else
58
+ define_method(name) do |*value, **kw|
59
+ if value.empty? && kw.empty?
60
+ instance_variable_get("@#{name}")
61
+ elsif filter_block
62
+ # Ruby 2.7 madness. The second version would pass {} as
63
+ # second argument
64
+ filtered_value =
65
+ if kw.empty?
66
+ send("__dsl_attribute__#{name}__filter__", *value)
67
+ else
68
+ send("__dsl_attribute__#{name}__filter__", *value, **kw)
69
+ end
70
+ instance_variable_set("@#{name}", filtered_value)
71
+ self
72
+ else
59
73
  if value.size == 1
60
74
  instance_variable_set("@#{name}", value.first)
61
75
  else
62
76
  instance_variable_set("@#{name}", value)
63
77
  end
64
- self
65
- end
66
- end
67
- end
78
+ self
79
+ end
80
+ end
68
81
  end
69
82
  end
70
83
  end
71
-
@@ -1,8 +1,20 @@
1
1
 
2
2
  class Object
3
- # Return the object address (for non immediate
4
- # objects).
5
- def address; Object.address_from_id(object_id) end
3
+ if RUBY_VERSION < "2.7.0"
4
+ # Return the object address (for non immediate
5
+ # objects).
6
+ def address
7
+ Object.address_from_id(object_id)
8
+ end
9
+ else
10
+ BUILTIN_OBJECT_TO_S = Object.instance_method(:to_s)
11
+ def address
12
+ to_s = BUILTIN_OBJECT_TO_S.bind(self).call
13
+ if (m = /:(0x[0-9a-f]+)/.match(to_s))
14
+ Integer(m[1])
15
+ end
16
+ end
17
+ end
6
18
 
7
19
  # Converts the object_id of a non-immediate object
8
20
  # to its memory address
@@ -27,6 +27,8 @@ class Object
27
27
  class_eval <<-EOD, __FILE__, __LINE__+1
28
28
  def #{name}
29
29
  if instance_variable_defined?(:@#{name}) then @#{name}
30
+ elsif frozen?
31
+ #{name}_defval
30
32
  else @#{name} = #{name}_defval
31
33
  end
32
34
  end
@@ -40,4 +42,3 @@ class Object
40
42
  singleton_class.class_eval { attribute(attr_def, &init) }
41
43
  end
42
44
  end
43
-