Ron 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,30 @@
1
- === 0.1.1 / 2009-08-05
1
+ === 0.1.2 / 21dec2009
2
+ * 5 Major Bugfixes:
3
+ * preserve modules an object was extended by
4
+ * (but not Recursive, which is an artifact of using ron)
5
+ * single quote and backslash now handled right in String
6
+ * all significant digits now preserved in Float (really)
7
+ * many small improvements to object graph copying (see below)
8
+ * ported to MRI 1.9
9
+
10
+ * 2 Minor Bugfixes:
11
+ * preserve classes of descendants Regexp, Range, Array and Hash
12
+ * many changes to tests to improve coverage and correctness
13
+ * also, tests now work around bugs in yaml
14
+
15
+ * 1 Minor Enhancement:
16
+ * reworked build/gem scripts
17
+
18
+ * Improvements to object graph copying:
19
+ * depth-first copying is now available
20
+ * call may pass in a hash of objects to replace
21
+ * replacements when parent is nil or false are now an error
22
+ * protect against changes to hash, set, or array during traversal
23
+ * avoid inf-loop if replacing item with many items
24
+ * now possible to replace an object with another that contains the first
25
+ * in depth_graphcopy, delay all changes til right before the end
26
+
27
+ === 0.1.1 / 5aug2009
2
28
  * 1 Major Enhancement:
3
29
  * significant improvements and bugfixes in graphcopy
4
30
 
@@ -7,7 +33,7 @@
7
33
  * ported to ruby 1.9
8
34
  * rewrote Class#- to be more compatible with Reg
9
35
 
10
- === 0.1.0 / 2006-10-07
36
+ === 0.1.0 / 7oct2006
11
37
 
12
38
  * 1 major enhancement
13
39
  * Birthday!
data/Makefile CHANGED
@@ -1,3 +1,12 @@
1
+ name=Ron
2
+ lname=ron
3
+ gemname=Ron
4
+
5
+ #everything after this line is generic
6
+
7
+ version=$(shell ruby -r ./lib/$(lname)/version.rb -e "puts $(name)::VERSION")
8
+ filelist=$(shell git ls-files)
9
+
1
10
  .PHONY: all test docs gem tar pkg email
2
11
  all: test
3
12
 
@@ -10,23 +19,25 @@ docs:
10
19
  pkg: gem tar
11
20
 
12
21
  gem:
13
- gem build ron.gemspec
22
+ gem build $(lname).gemspec
14
23
 
15
24
  tar:
16
- tar czf Ron-`ruby -r ./lib/ron/version.rb -e 'puts Ron::VERSION'`.tar.gz `git ls-files`
25
+ tar cf - $(filelist) | ( mkdir $(gemname)-$(version); cd $(gemname)-$(version); tar xf - )
26
+ tar czf $(gemname)-$(version).tar.gz $(gemname)-$(version)
27
+ rm -rf $(gemname)-$(version)
17
28
 
18
29
  email: README.txt History.txt
19
30
  ruby -e ' \
20
31
  require "rubygems"; \
21
- load "./ron.gemspec"; \
22
- spec= Gem::Specification.list.find{|x| x.name=="Ron"}; \
32
+ load "./$(lname).gemspec"; \
33
+ spec= Gem::Specification.list.find{|x| x.name=="$(gemname)"}; \
23
34
  puts "\
24
- Subject: [ANN] Ron #{spec.version} Released \
25
- \n\nRon version #{spec.version} has been released! \n\n\
35
+ Subject: [ANN] $(name) #{spec.version} Released \
36
+ \n\n$(name) version #{spec.version} has been released! \n\n\
26
37
  #{Array(spec.homepage).map{|url| " * #{url}\n" }} \
27
38
  \n\
28
- #{Ron::Description} \
39
+ #{$(name)::Description} \
29
40
  \n\nChanges:\n\n \
30
- #{Ron::Latest_changes} \
41
+ #{$(name)::Latest_changes} \
31
42
  "\
32
43
  '
data/lib/ron.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  #require 'warning'
5
5
  require 'ron/graphedge'
6
6
  require 'continuation' unless defined? Continuation
7
+ require 'ron/float_accurate_to_s'
7
8
 
8
9
  #to_ron
9
10
 
@@ -41,8 +42,15 @@ module Ron
41
42
  def self.load str
42
43
  eval str
43
44
  end
45
+
46
+ def self.extension_modules_of(obj)
47
+ ancs=class<<obj; ancestors end
48
+ ancs=ancs[0...ancs.index(obj.class)]
49
+ return ancs
50
+ rescue TypeError
51
+ return []
52
+ end
44
53
 
45
- CaptureCtx=[]
46
54
  =begin
47
55
  def self.recurse_safe_objects_equal?(o1,o2,session={})
48
56
  pair=[o1.__id__,o2.__id__]
@@ -51,13 +59,13 @@ module Ron
51
59
 
52
60
  o1.class==o2.class and
53
61
  case o1
54
- when Array:
62
+ when Array
55
63
  o1.size==o2.size and
56
64
  o1.each_with_index{|i1,idx|
57
65
  recurse_safe_objects_equal?(i1,o2[idx],session) or return
58
66
  }
59
67
 
60
- when Hash:
68
+ when Hash
61
69
  #oops, this depends on #== and #hash working right for recursive structures, which they don't.
62
70
  o1.size==o2.size or return
63
71
  recurse_safe_objects_equal? o1.default,o2.default,session or return
@@ -65,28 +73,28 @@ module Ron
65
73
  return unless (o2.key? idx and recurse_safe_objects_equal? i1, o2[idx],session)
66
74
  }
67
75
 
68
- when Range:
76
+ when Range
69
77
  o1.exclude_end?()==o2.exclude_end?() and
70
78
  recurse_safe_objects_equal? o1.begin, o2.begin,session and
71
79
  recurse_safe_objects_equal? o1.end, o2.end,session
72
80
 
73
- when Struct:
81
+ when Struct
74
82
  (mems=o1.members).size==o2.members.size and
75
83
  mems.each{|i|
76
84
  recurse_safe_objects_equal? o1[i], (o2[i] rescue return),session or return
77
85
  }
78
- when Binding:
86
+ when Binding
79
87
  recurse_safe_objects_equal? o1.to_h, o2.to_h, session
80
- when Proc,Integer,Float,String:
88
+ when Proc,Integer,Float,String
81
89
  o1==o2
82
90
  when Thread,ThreadGroup,Process,IO,Symbol,
83
- Continuation,Class,Module:
91
+ Continuation,Class,Module
84
92
  return o1.equal?(o2)
85
- when Exception:
93
+ when Exception
86
94
  o1.message==o2.message
87
- when MatchData:
95
+ when MatchData
88
96
  o1.to_a==o2.to_a
89
- when Time:
97
+ when Time
90
98
  o1.eql? o2
91
99
  else true
92
100
  end and
@@ -142,40 +150,65 @@ end
142
150
  [Fixnum,NilClass,FalseClass,TrueClass,Symbol].each{|k|
143
151
  k.class_eval{ alias to_ron inspect; undef to_ron_list }
144
152
  }
145
- [Bignum,Float,].each{|k|
146
- k.class_eval{ def to_ron_list(session) [inspect] end }
147
- }
153
+
154
+ class Bignum
155
+ def to_ron_list(session) [inspect] end
156
+ end
157
+
158
+ class Float
159
+ def to_ron_list(session)
160
+ [accurate_to_s]
161
+ end
162
+ end
148
163
 
149
164
  class String
150
165
  def to_ron_list session
151
-
152
- [ "'", gsub(/['\\]/){ '\\\\'+$&}, "'" ]
166
+ result= [ "'", gsub(/['\\]/){ '\\'+$&}, "'" ]
167
+ if self.class!=String
168
+ result=[self.class.name, ".new(", result, ")"]
169
+ end
170
+ result
153
171
  end
154
172
  end
155
173
 
156
174
  class Regexp
157
175
  def to_ron_list session
158
-
159
- [ inspect ]
176
+ if self.class==Regexp
177
+ [ inspect ]
178
+ else
179
+ [self.class.name, ".new(", self.source.inspect, ")"]
180
+ end
160
181
  end
161
182
  end
162
183
 
163
184
  class Array
164
185
  def to_ron_list session
165
- ["["] +
186
+ result=["["] +
166
187
  map{|i|
167
188
  i.to_ron_list2(session)<<', '
168
189
  }.flatten<<
169
190
  "]"
191
+ result.unshift self.class.name unless self.class==Array
192
+ result
170
193
  end
171
194
  end
172
195
 
173
196
  class Hash
174
197
  def to_ron_list session
175
- ["{"]+map{|k,v|
176
- Array(k.to_ron_list2(session)).push "=>",
198
+ if self.class==Hash
199
+ leader="{"
200
+ trailer="}"
201
+ sep="=>"
202
+ else
203
+ leader=self.class.name+"["
204
+ trailer="]"
205
+ sep=","
206
+ end
207
+
208
+ [leader]+map{|k,v|
209
+ Array(k.to_ron_list2(session)).push sep,
177
210
  v.to_ron_list2(session)<<', '
178
- }.flatten<<"}"
211
+ }.flatten<<trailer
179
212
  end
180
213
  end
181
214
 
@@ -185,7 +218,7 @@ class Object
185
218
  #warning "additional modules not handled"
186
219
  #warning "prettified output not supported"
187
220
 
188
- to_ron_list2.to_s
221
+ to_ron_list2.join
189
222
  end
190
223
 
191
224
  def to_ron_list session
@@ -225,13 +258,21 @@ class Object
225
258
  result=to_ron_list(session).unshift str
226
259
  if result.last=="}#end object literal"
227
260
  result.last.replace "}"
261
+ was_obj_syntax=true
228
262
  else
229
263
  #append instance_eval
230
- ivars=instance_variables-::Ron::IGNORED_INSTANCE_VARIABLES[self.class.name]
264
+ ivars=instance_variables
265
+ ivars.map!{|iv| iv.to_s } if Symbol===ivars.first
266
+ ivars-=::Ron::IGNORED_INSTANCE_VARIABLES[self.class.name]
231
267
  ivars.empty? or result.push ".with_ivars(", *ivars.map{|iv|
232
268
  [":",iv.to_s,"=>",instance_variable_get(iv).to_ron_list2(session),', ']
233
269
  }.flatten[0...-1]<<")"
234
270
  end
271
+ extensions=Ron::extension_modules_of(self)
272
+ unless extensions.empty?
273
+ result=["(",result,")"] if was_obj_syntax
274
+ result.push ".extend(",extensions,")"
275
+ end
235
276
  result.push ")" if str[/^Recursive\(/]
236
277
  session.objects_seen[__id__]=[session.objects_in_progress.pop[1]]
237
278
  result
@@ -268,7 +309,7 @@ Ron::IGNORED_INSTANCE_VARIABLES["Sequence::WeakRefSet"]=%w[@ids]
268
309
  class Range
269
310
  def to_ron_list session
270
311
  # result=
271
- ["Range.new(",first.to_ron_list2(session), ", ",
312
+ [self.class.name, ".new(",first.to_ron_list2(session), ", ",
272
313
  last.to_ron_list2(session),
273
314
  (", true" if exclude_end?),
274
315
  ")"
@@ -301,7 +342,7 @@ class Binding
301
342
  l<<"self"
302
343
  h={}
303
344
  l.each{|i| h[i.to_sym]=Kernel::eval i, self }
304
- h[:yield]=Kernel::eval "block_given? and proc{|*a| #,&b\n yield(*a) #,&b\n}", self
345
+ h[:yield]=Kernel::eval "block_given? and proc{|*a__| #,&b__\n yield(*a__) #,&b__\n}", self
305
346
  h
306
347
  end
307
348
 
@@ -316,17 +357,17 @@ class Binding
316
357
  keys.empty? or
317
358
  code=keys.map{|k|
318
359
  k.to_s
319
- }.join(',')+'=*::Ron::CaptureCtx.last'
360
+ }.join(',')+',=*Thread.current[:$__Ron__CaptureCtx]'
320
361
  mname="Ron__capture_binding#{Thread.current.object_id}" #unlikely method name
321
- newmname=result=nil
362
+ oldmname=newmname=result=nil
322
363
 
323
364
  eval "
324
- Thread.critical=true
325
365
  newmname=class<<the_self;
326
366
  mname=oldmname='#{mname}'
327
- im=instance_methods(false)
367
+ im=instance_methods
368
+ im.map!{|sym| sym.to_s} if Symbol===im.first
328
369
  mname+='-' while im.include? mname
329
- alias_method mname, oldmname
370
+ alias_method mname, oldmname if im.include? oldmname
330
371
  def #{mname}
331
372
  #{code}
332
373
  binding
@@ -334,9 +375,9 @@ class Binding
334
375
  mname
335
376
  end
336
377
  "
337
- ::Ron::CaptureCtx.push h.values
378
+ Thread.current[:$__Ron__CaptureCtx]= h.values
338
379
  result=the_self.send mname, &the_block
339
- ::Ron::CaptureCtx.pop
380
+ Thread.current[:$__Ron__CaptureCtx]=nil
340
381
  class<<the_self;
341
382
  self
342
383
  end.send(*if newmname==mname
@@ -344,7 +385,6 @@ class Binding
344
385
  else
345
386
  [:alias_method, mname, oldmname]
346
387
  end)
347
- Thread.critical=false
348
388
  result
349
389
  end
350
390
  alias from_h -
@@ -378,22 +418,20 @@ UnboundMethod,
378
418
 
379
419
 
380
420
  #not sure about these:
381
- [
382
- Continuation,
383
- :Thread,
384
- :ThreadGroup,
385
-
386
- :Mutex, #??
387
421
  #and other interthead communication mechanisms, like
388
- :Queue,
389
- :SizedQueue,
390
- :RingBuffer,
391
- :ConditionVariable,
392
- :Semaphore,
393
- :CountingSemaphore,
394
- :Multiwait,
395
- ].each{|sym|
396
- k=(Object.const_get(sym) rescue nil) and
422
+ eval("["+%w[
423
+ Continuation
424
+ Thread
425
+ ThreadGroup
426
+ Mutex
427
+ Queue
428
+ SizedQueue
429
+ RingBuffer
430
+ ConditionVariable
431
+ Semaphore
432
+ CountingSemaphore
433
+ Multiwait
434
+ ].map{|x| "(#{x} if defined? #{x}), "}.join+"]").compact.each{|k|
397
435
  k.class_eval do
398
436
  def to_ron(x=nil); raise Ron:: NotYetMaybeNeverSerializeableError end
399
437
  alias to_ron_list to_ron
@@ -86,7 +86,7 @@ module ForModule
86
86
  #column_order defaults to attribute order if not given
87
87
  cols=(@COLUMN_ORDER if defined? @COLUMN_ORDER) || @REG_ATTRIBUTE_ORDER
88
88
 
89
- meths=instance_methods(false)
89
+ meths=instance_methods(false).map{|im| im.to_s}
90
90
 
91
91
  module_eval <<-"END"
92
92
  private #utility methods
@@ -0,0 +1,168 @@
1
+ begin #redparse/float_accurate_to_s overrides this one, if present
2
+ require 'redparse/float_accurate_to_s'
3
+ overridden=true
4
+ rescue Exception #and ignore
5
+ end
6
+
7
+ class Float
8
+ SIZE=[1.1].pack("d").size
9
+ BITSIZE=SIZE*8
10
+ BASE10_DIGITS=(2**BITSIZE-1).to_s.size
11
+ def accurate_to_s
12
+ return "#{'-' if self<0}Infinity" if infinite?
13
+ return "NaN" if nan?
14
+ return "0.0e0" if zero?
15
+
16
+ as_str=sprintf("%.#{BASE10_DIGITS+2}e",self)
17
+
18
+ #decompose self into sign, mantissa, and exponent (in string form)
19
+ all,sign,first,digits,exp=*as_str.match(/^([+-]?)(\d)\.(\d+)e(.*)$/)
20
+ digits=first<<digits
21
+ exp=exp.to_i+1
22
+ lead=sign<<"0."
23
+ return digits=digits if as_str.to_f.zero? #hopeless
24
+
25
+ #recompose back to a float
26
+ result=[lead,digits,"e",exp].join
27
+ result_f=result.to_f
28
+ delta=result_f - self
29
+ return digits=digits if delta.zero? #if representation is exact, return here
30
+
31
+ #figure out which direction to go to get toward the right answer
32
+ if delta<0
33
+ incr=1
34
+ else #delta>0
35
+ incr=-1
36
+ end
37
+
38
+ #keep adding increasing increments to mantissa
39
+ #until we get to a value on the other side of the correct answer
40
+ while true
41
+ while true
42
+ try_digits=digits.to_i.+(incr).to_s
43
+ if try_digits.size>digits.size
44
+ exp+=1
45
+ digits="0"+digits
46
+ end
47
+ fail if try_digits[0]==?- #can't happen... I think?
48
+ trying=[lead,try_digits,"e",exp].join
49
+ trying_f=trying.to_f
50
+ break unless trying_f.zero?
51
+ digits[-1,1]='' #workaround 1.8 bug
52
+ end
53
+ return digits=try_digits if trying_f==self
54
+ break if self.between?(*[trying_f,result_f].sort) #(trying_f-self)*delta<0
55
+ incr*=2
56
+ end
57
+
58
+ #we now have lower and upper bounds on the correct answer
59
+ lower,upper=*[digits.to_i, digits.to_i.+(incr)].sort
60
+
61
+ #maybe one of the bounds is already the correct answer?
62
+ result=[lead,lower,"e",exp].join
63
+ return digits=lower if result.to_f==self
64
+ result=[lead,upper,"e",exp].join
65
+ return digits=upper if result.to_f==self
66
+
67
+ #binary search between lower and upper bounds til we find a correct answer
68
+ digits=nil
69
+ while true
70
+ return as_str if upper-lower <= 1 #hopeless
71
+ mid=(lower+upper)/2
72
+ mid_s=[lead,mid,"e",exp].join
73
+ mid_f=mid_s.to_f
74
+ return digits=mid if mid_f==self
75
+ if mid_f<self
76
+ lower=mid
77
+ else #mid_f>self
78
+ upper=mid
79
+ end
80
+ end
81
+ ensure
82
+
83
+ #try to drop unneeded trailing digits
84
+ if digits
85
+ digits=digits.to_s
86
+ begin
87
+ last=digits.slice! -1
88
+ result=[lead,digits,"e",exp].join.to_f
89
+ end while result==self or result.zero? && digits.size.nonzero?
90
+ roundup=(digits.to_i+1).to_s
91
+ if roundup.size>digits.size
92
+ exp+=1
93
+ digits="0"+digits
94
+ end
95
+ roundup.slice! /0+\Z/
96
+ roundup=[lead,roundup,"e",exp].join
97
+ return roundup if roundup.to_f==self
98
+ return [lead,digits<<last,"e",exp].join
99
+ end
100
+ end
101
+ end unless overridden
102
+
103
+ =begin not quite accurate, tho
104
+ class String
105
+ def accurate_to_f
106
+ all,sign,int,frac,exp=*self.match(/\A([+-])?([0-9_]+)(?:\.([0-9_]+))?(?:[eE]([+-]?[0-9_]+))?/)
107
+ exp=exp.to_i||0
108
+ exp-=frac.size
109
+ mantissa=sign<<int<<frac
110
+ mantissa=mantissa.to_f
111
+ scale=10.0**exp
112
+ return mantissa*scale
113
+ end
114
+ end unless overridden
115
+ =end
116
+
117
+ eval DATA.read if __FILE__==$0
118
+ __END__
119
+
120
+ require 'test/unit'
121
+
122
+ class FloatRoundTripTest<Test::Unit::TestCase
123
+ def float_round_trip f
124
+ str=f.accurate_to_s
125
+ if str=="Infinity"
126
+ return 1.0/0
127
+ elsif str=="-Infinity"
128
+ return -1.0/0
129
+ elsif str=="NaN"
130
+ return 0.0/0.0
131
+ else
132
+ str.to_f
133
+ end
134
+ end
135
+ alias frt float_round_trip
136
+
137
+ def rand_float
138
+ base=rand
139
+ range=Float::MIN_10_EXP..Float::MAX_10_EXP
140
+ extent=range.last-range.first
141
+ offset=rand(extent+1)
142
+ exp=range.first+offset
143
+ base*=10**exp
144
+ end
145
+
146
+ def test_frt
147
+ data=[0.399891415240566, 0.198188037200931, 0.90302699802093,
148
+ 7.48121345153454520016e-01, 4.11408313999083175005e-02,
149
+ 2.68070684698467065488e-02, 3.85029229764812574999e-01,
150
+ "\327\237vY\343n\342?".unpack("d")[0],
151
+ 1.0/0, -1.0/0, 0.0/0.0,
152
+ "\333\211>4\253Atu".unpack("d")[0],
153
+ "\2461\220\253\214\267e\021".unpack("d")[0],
154
+ "\r\032N\f'\336\243\003".unpack("d")[0],
155
+ "B[\226\001\2175f\021".unpack("d")[0],
156
+ "%\217\336\362\326\272\026\001".unpack("d")[0],
157
+ "\312\342\344\206\237\024\"\003".unpack("d")[0],
158
+ ]
159
+ 1_000_000.times{|i|
160
+ f=data.shift||rand_float
161
+ p sprintf("%.#{Float::BASE10_DIGITS}e",f)
162
+ p [f].pack"d"
163
+ p f.accurate_to_s
164
+ f2=frt f
165
+ warn "failed with i=#{i} f=#{f}, #{[f].pack("d").inspect}; f2=#{f2}, #{[f2].pack("d").inspect}" unless [f].pack("d")==[f2].pack("d")
166
+ }
167
+ end
168
+ end
@@ -38,23 +38,69 @@ module Ron
38
38
  end
39
39
 
40
40
  #--------------------------------
41
- def graphcopy(obj)
42
- old2new={}
41
+ #
42
+ #NOTE: breadth_graphcopy has a problem; since graphwalk is not depth-first,
43
+ # if a node is replaced, the subnodes of the new node will not be walked.
44
+ # Instead, the old nodes will be walked.
45
+ # If this causes problems, use depth_graphcopy instead
46
+ def breadth_graphcopy(obj,old2new={})
43
47
  root=nil
44
- graphwalk(obj){|cntr,o,i,ty|
48
+ breadth_graphwalk(obj){|cntr,o,i,ty|
45
49
  newo= block_given? && (yield cntr,o,i,ty,useit=[false])
46
- useit.first or newo= old2new[o.__id__] ||= o.clone rescue o
50
+ if useit.first
51
+ #fail unless newo
52
+ old2new[o.__id__]=newo
53
+ else
54
+ #fail unless o
55
+ newo= old2new[o.__id__] ||= (o.clone rescue o)
56
+ end
47
57
  #IO objects really shouldn't be dup'd here
48
58
  if Ron::GraphEdge::TopLevel==ty
49
59
  root=newo
50
60
  else
61
+ #fail unless old2new.has_key? cntr.__id__
51
62
  ty.new(old2new[cntr.__id__],i,1){newo}.replace
52
63
  end
53
64
  }
54
65
  return root
55
66
  end
67
+
68
+ #--------------------------------
69
+ def depth_graphcopy(obj,old2new={})
70
+ root=nil
71
+ changes=[]
72
+ depth_graphwalk(obj){|cntr,o,i,ty|
73
+ newo=yield cntr,o,i,ty,useit=[false] if block_given?
74
+ if useit.first
75
+ #p [cntr.id,cntr,o,:>>,newo,newo.last.__id__]
76
+ old2new[o.__id__]=o #bad, bad... shouldn't be refs to old tree in old2new
77
+ else
78
+ begin
79
+ newo=o.clone
80
+ rescue Exception
81
+ newo=o
82
+ else
83
+ old2new[o.__id__]=newo
84
+ end
85
+ end
86
+ #IO objects really shouldn't be dup'd here
87
+ if Ron::GraphEdge::TopLevel==ty
88
+ root=newo
89
+ else
90
+ changes.push ty,cntr.__id__,i,newo
91
+ end
92
+ }
93
+ until changes.empty?
94
+ ty,cntr_id,i,newo=*changes.slice!(-4,4)
95
+ new_cntr=old2new[cntr_id]
96
+ ty.new(new_cntr,i,1){newo}.replace if old2new.has_key? cntr_id
97
+ end
98
+ return root
99
+ end
100
+ alias graphcopy depth_graphcopy
56
101
 
57
102
  #--------------------------------
103
+ #breadth-first walk of an object graph
58
104
  def graphwalk(obj)
59
105
  yield nil,obj,nil,GraphEdge::TopLevel
60
106
  todolist=[obj]
@@ -69,7 +115,18 @@ module Ron
69
115
  }
70
116
  }
71
117
  end
72
-
118
+ alias breadth_graphwalk graphwalk
119
+
120
+ #--------------------------------
121
+ #depth-first walk of an object graph
122
+ def depth_graphwalk(obj,container=nil,index=nil,type=GraphEdge::TopLevel,seen=Set[],&block)
123
+ seen<<[container.__id__,type,index]
124
+ traverse(obj){|cntr,o,i,ty|
125
+ depth_graphwalk(o,cntr,i,ty,seen,&block) unless seen.include? [cntr.__id__,ty,i]
126
+ }
127
+ block[ container,obj,index,type ]
128
+ end
129
+
73
130
  #--------------------------------
74
131
  def abortable_graphwalk(obj)
75
132
  return unless yield nil,obj,nil,GraphEdge::TopLevel
@@ -93,7 +150,7 @@ module Ron
93
150
  case obj
94
151
  when nil; #do nothing
95
152
  when (Set if defined? Set)
96
- obj.each{|elem|
153
+ obj.dup.each{|elem|
97
154
  yield(obj,elem, elem, GraphEdge::SetMember)
98
155
  }
99
156
  when Struct;
@@ -101,12 +158,12 @@ module Ron
101
158
  yield(obj,obj[mem],mem, GraphEdge::BracketsValue)
102
159
  }
103
160
  when Hash;
104
- obj.each{|(i,elem)|
161
+ obj.keys.each{|i| elem=obj[i]
105
162
  yield(obj,elem,i, GraphEdge::HashValue)
106
163
  yield(obj,i,i, GraphEdge::HashKey)
107
164
  }
108
165
  when Array;
109
- obj.each_with_index{|elem,i|
166
+ (obj.size-1).downto(0){|i| elem=obj[i]
110
167
  yield(obj,elem,i, GraphEdge::Array)
111
168
  }
112
169
  when Range;
@@ -120,7 +177,7 @@ module Ron
120
177
  end
121
178
  #traverse instance vars in any case
122
179
  obj.instance_variables.each{|var|
123
- yield obj, (obj.instance_variable_get var), var, GraphEdge::ObjectIvarValue
180
+ yield obj, (obj.instance_variable_get var), var.to_s, GraphEdge::ObjectIvarValue
124
181
  }
125
182
  end
126
183
 
@@ -158,7 +215,7 @@ module Ron
158
215
  def initialize(context,index,len=1,&newval_code)
159
216
  len>=0 or raise ArgumentError
160
217
  @context,@index,@len,@newval_code=context,index,len,newval_code
161
- ObjectSpace.define_finalizer(@context,(method :context_died))
218
+ fail unless @context
162
219
  end
163
220
  attr_reader :index,:len,:context
164
221
 
@@ -167,9 +224,9 @@ module Ron
167
224
 
168
225
  def call; replace; end
169
226
 
170
- def context_died
171
- @context=nil
172
- end
227
+ # def context_died
228
+ # @context=nil
229
+ # end
173
230
 
174
231
  def new_value
175
232
  @newval_code[self]
@@ -191,9 +248,22 @@ module Ron
191
248
  context[@index]
192
249
  end
193
250
 
194
- def replace(*newvals)
195
- newvals.empty? and newvals=[new_value]
196
- context[@index]=*newvals
251
+
252
+ a=[]
253
+ a[0]=*[1]
254
+ if a==[1]
255
+ def replace(*newvals)
256
+ newvals.empty? and newvals=[new_value]
257
+ context[@index]=*newvals
258
+ end
259
+ elsif a==[[1]] #goddamn 1.9 array multiple assignment semantics!
260
+ def replace(*newvals)
261
+ if newvals.empty? then newvals=new_value
262
+ elsif newvals.size==1 then newvals=newvals[0]
263
+ end
264
+ context[@index]=newvals
265
+ end
266
+ else fail
197
267
  end
198
268
  end
199
269
 
@@ -1,3 +1,3 @@
1
1
  module Ron
2
- VERSION="0.1.1"
2
+ VERSION="0.1.2"
3
3
  end
@@ -1,28 +1,29 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require './lib/ron/version'
3
+ require "#{File.dirname(__FILE__)}/lib/ron/version"
4
4
  Ron::Description=open("README.txt"){|f| f.read[/^==+ ?description[^\n]*?\n *\n?(.*?\n *\n.*?)\n *\n/im,1] }
5
5
  Ron::Latest_changes="###"+open("History.txt"){|f| f.read[/\A===(.*?)(?====)/m,1] }
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "Ron"
9
9
  s.version = Ron::VERSION
10
-
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
- s.authors = ["Caleb Clausen"]
13
10
  s.date = Time.now.strftime("%Y-%m-%d")
11
+ s.authors = ["Caleb Clausen"]
14
12
  s.email = %q{caleb (at) inforadical (dot) net}
15
- s.extra_rdoc_files = ["README.txt", "COPYING", "GPL"]
16
- s.files = `git ls-files`.split
17
- s.has_rdoc = true
13
+ s.summary = "Ruby Object Notation (Ron) is a ruby-based textual format for representing Ruby data."
14
+ s.description = Ron::Description
18
15
  s.homepage = %{http://github.com/coatl/ron}
19
- s.rdoc_options = %w[--inline-source --main README.txt]
20
- s.require_paths = ["lib"]
21
16
  s.rubyforge_project = %q{ron}
22
- s.rubygems_version = %q{1.3.0}
17
+
18
+ s.files = `git ls-files`.split
23
19
  s.test_files = %w[test/test_all.rb]
24
- s.summary = "Ruby Object Notation (Ron) is a ruby-based textual format for representing Ruby data."
25
- s.description = Ron::Description
20
+ s.require_paths = ["lib"]
21
+ s.extra_rdoc_files = ["README.txt", "COPYING", "GPL"]
22
+ s.has_rdoc = true
23
+ s.rdoc_options = %w[--main README.txt]
24
+
25
+ s.rubygems_version = %q{1.3.0}
26
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
26
27
 
27
28
  =begin
28
29
  if s.respond_to? :specification_version then
@@ -18,6 +18,8 @@ try_require 'rubygems'
18
18
  try_require("sequence/weakrefset")
19
19
  try_require 'facets/more/superstruct'
20
20
 
21
+ require "test/test_graphcopy"
22
+
21
23
  $Verbose=true
22
24
 
23
25
  =begin
@@ -67,10 +69,33 @@ def == bm
67
69
  end
68
70
  end
69
71
 
72
+ class MyString<String
73
+ attr_accessor :ivar
74
+ end
75
+
76
+ class MyRegexp<Regexp
77
+ attr_accessor :ivar
78
+ end
79
+
80
+ class MyArray<Array
81
+ attr_accessor :ivar
82
+ end
83
+
84
+ class MyHash<Hash
85
+ attr_accessor :ivar
86
+ end
87
+
88
+ class MyRange<Range
89
+ attr_accessor :ivar
90
+ end
91
+
92
+ module M
93
+ end
94
+
95
+
70
96
  class RonTest<Test::Unit::TestCase
71
97
 
72
98
  def test_ron
73
-
74
99
  s1=1.0
75
100
  s2=2.0
76
101
  range=s1..s2
@@ -83,13 +108,16 @@ s2.instance_variable_set(:@n, ss)
83
108
  s1=1.0
84
109
  s2=2.0
85
110
  sss=SortedSet[s1,s2]
86
- s1.instance_variable_set(:@n, sss)
87
- s2.instance_variable_set(:@n, sss)
111
+ s1.instance_variable_set(:@o, sss)
112
+ s2.instance_variable_set(:@o, sss)
88
113
 
89
114
  sss.inspect #disable this and tests fail... why?!?!?
115
+ strongrefs=%w[a b c]
90
116
  data=[
91
-
117
+ 34565.23458888888*0.5,
118
+ 45245.765735422567*0.5,
92
119
  3.14159,
120
+ 1.2345678901234567,
93
121
  2**2000,
94
122
 
95
123
  "string",
@@ -111,7 +139,7 @@ data=[
111
139
  [1,2,3],
112
140
  {1=>2,3=>4},
113
141
  Set[1,2,3],
114
- (Sequence::WeakRefSet[*%w[a b c]] rescue warn 'weakrefset test disabled'),
142
+ (Sequence::WeakRefSet[*strongrefs] rescue warn 'weakrefset test disabled'),
115
143
  A_Class.new,
116
144
  2,
117
145
  :symbol,
@@ -119,10 +147,18 @@ data=[
119
147
  true,
120
148
  false,
121
149
 
122
-
150
+ MyString.new,
151
+ MyRegexp.new(//),
152
+ MyArray.new,
153
+ MyHash.new,
154
+ MyRange.new(1,2),
155
+
156
+ "\\",
157
+ "'",
123
158
  ]
124
159
  data.each{|datum|
125
- # p datum
160
+ GC.disable
161
+ #p datum
126
162
  assert_equal datum, datum
127
163
  assert_equal datum, ( dup=eval datum.to_ron )
128
164
  assert_equal internal_state(datum), internal_state(dup)
@@ -138,17 +174,30 @@ data.each{|datum|
138
174
  datum.instance_eval{@c=self}
139
175
  assert_equal datum, ( dup=eval datum.to_ron )
140
176
  assert_equal internal_state(datum), internal_state(dup)
177
+
178
+ datum.extend(M)
179
+ assert_equal datum, ( dup=eval datum.to_ron )
180
+ assert_equal internal_state(datum), internal_state(dup)
181
+ assert M===dup
141
182
  end
183
+ GC.enable
142
184
  }
143
185
  data.each{|datum|
186
+ GC.disable
144
187
  if case datum
145
188
  when Fixnum,Symbol,true,false,nil; false
146
189
  else true
147
190
  end
148
191
  datum.instance_eval{@d=data}
149
- assert datum, ( dup=eval datum.to_ron )
150
- assert internal_state(datum), internal_state(dup)
192
+ assert_equal datum, ( dup=eval datum.to_ron )
193
+ assert_equal internal_state(datum), internal_state(dup)
194
+
195
+ datum.extend(M)
196
+ assert_equal datum, ( dup=eval datum.to_ron )
197
+ assert_equal internal_state(datum), internal_state(dup)
198
+ assert M===dup
151
199
  end
200
+ GC.enable
152
201
  }
153
202
 
154
203
  data2=[
@@ -160,35 +209,57 @@ data2=[
160
209
  (h={};h[h]=0;h),
161
210
  (h={};h[h]=h;h),
162
211
  (s=Set[];s<<s;s),
212
+ (o=MyString.new; o.ivar=o; o),
213
+ (o=MyArray.new; o.ivar=o; o),
214
+ (o=MyHash.new; o.ivar=o; o),
215
+ (o=MyArray.new; o<<o),
216
+ (o=MyHash.new; o[1]=o; o),
217
+ (o=MyRange.new(1,2); o.ivar=o; o),
163
218
  ]
164
- data2.each{|datum|
165
- #p datum
219
+ data2.each{|datum|
220
+ GC.disable
221
+ #p datum
166
222
  assert_equal datum.to_yaml, datum.to_yaml
167
- assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
168
- assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
223
+ dup=eval datum.to_ron
224
+ dup.inspect #shouldn't be needed
225
+ assert_equal datum.to_yaml, dup.to_yaml
226
+ assert_equal internal_state(datum)[0...2], internal_state(dup)[0...2]
227
+ assert_equal internal_state(datum)[2..-1].to_yaml, internal_state(dup)[2..-1].to_yaml
169
228
 
170
229
  if case datum
171
230
  when Fixnum,Symbol,true,false,nil; false
172
231
  else true
173
232
  end
174
233
  datum.instance_eval{@a,@b=1,2}
175
- assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
176
- assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
234
+ dup=eval datum.to_ron
235
+ dup.inspect #shouldn't be needed
236
+ assert_equal datum.to_yaml, dup.to_yaml
237
+ assert_equal internal_state(datum)[0...2], internal_state(dup)[0...2]
238
+ assert_equal internal_state(datum)[2..-1].to_yaml, internal_state(dup)[2..-1].to_yaml
177
239
 
178
240
  datum.instance_eval{@c=self}
179
- assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
180
- assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
241
+ dup=eval datum.to_ron
242
+ dup.inspect #shouldn't be needed
243
+ assert_equal datum.to_yaml, dup.to_yaml
244
+ assert_equal internal_state(datum)[0...2], internal_state(dup)[0...2]
245
+ assert_equal internal_state(datum)[2..-1].to_yaml, internal_state(dup)[2..-1].to_yaml
246
+
247
+ datum.extend(M)
248
+ dup=eval datum.to_ron
249
+ dup.inspect #shouldn't be needed
250
+ assert_equal datum.to_yaml, dup.to_yaml
251
+ assert_equal internal_state(datum)[0...2], internal_state(dup)[0...2]
252
+ assert_equal internal_state(datum)[2..-1].to_yaml, internal_state(dup)[2..-1].to_yaml
253
+ assert M===dup
181
254
  end
182
- }
183
- datum= ((w=Sequence::WeakRefSet[];w<<w;w) rescue warn 'weakrefset test disabled')
255
+ GC.enable
256
+ }
257
+ GC.disable
258
+ datum= ((w=Sequence::WeakRefSet[];w<<w;w) rescue warn 'weakrefset test disabled')
184
259
  assert_equal datum.inspect, datum.inspect
185
260
  assert_equal datum.inspect, ( dup=eval datum.to_ron ).inspect
186
261
  assert_equal internal_state(datum).inspect, internal_state(dup).inspect
187
262
 
188
- if case datum
189
- when Fixnum,Symbol,true,false,nil; false
190
- else true
191
- end
192
263
  datum.instance_eval{@a,@b=1,2}
193
264
  assert_equal datum.inspect, ( dup=eval datum.to_ron ).inspect
194
265
  assert_equal internal_state(datum).inspect, internal_state(dup).inspect
@@ -196,24 +267,26 @@ datum= ((w=Sequence::WeakRefSet[];w<<w;w) rescue warn 'weakrefset test disabled'
196
267
  datum.instance_eval{@c=self}
197
268
  assert_equal datum.inspect, ( dup=eval datum.to_ron ).inspect
198
269
  assert_equal internal_state(datum).inspect, internal_state(dup).inspect
199
- end
200
270
 
201
- data2.each{|datum|
202
- if case datum
203
- when Fixnum,Symbol,true,false,nil; false
204
- else true
205
- end
206
- datum.instance_eval{@d=data;@e=data2}
207
- #breaks yaml
208
- # assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
209
- # assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
210
- end
211
- }
271
+ datum= (o=MyRegexp.new(//); o.ivar=o; o)
272
+ ron=datum.to_ron
273
+ assert_match %r"Recursive\(v\d+_=\{\}, MyRegexp\.new\(\"\"\)\.with_ivars\(:@ivar=>v\d+_\)\)", ron
274
+ assert_equal datum,eval(ron)
275
+ datum.extend M
276
+ ron=datum.to_ron
277
+ assert M===eval(ron)
278
+ GC.enable
279
+ end
280
+
281
+ def nonrecursive_ancestors_of x
282
+ class<<x;ancestors end-[Recursive]
283
+ rescue TypeError
284
+ []
212
285
  end
213
286
 
214
287
  def internal_state x
215
- list=(x.instance_variables-::Ron::IGNORED_INSTANCE_VARIABLES[x.class.name]).sort
216
- [list]+list.map{|iv| x.instance_variable_get(iv)}
288
+ list=(x.instance_variables.map!{|iv| iv.to_s}-::Ron::IGNORED_INSTANCE_VARIABLES[x.class.name]).sort
289
+ [x.class,nonrecursive_ancestors_of(x),list]+list.map{|iv| x.instance_variable_get(iv)}
217
290
  end
218
291
 
219
292
  end
@@ -0,0 +1,42 @@
1
+ require 'test/unit'
2
+ require 'ron'
3
+ require 'ron/graphedge'
4
+ require 'pp'
5
+
6
+ class TestGraphCopy<Test::Unit::TestCase
7
+ inner=["foo",nil]
8
+ TESTDATA=[
9
+ {:one=>inner, :two=>inner, :three=>nil}
10
+ ]
11
+
12
+ def test_depth
13
+ TESTDATA.each{|td|
14
+ copy=Ron::GraphWalk::depth_graphcopy(td){|cntr,o,i,ty,useit|
15
+ if :one==i and Ron::GraphEdge::HashValue==ty
16
+ useit[0]=true
17
+ ["bar"]
18
+ elsif "foo"==o
19
+ useit[0]=true
20
+ "baz"
21
+ end
22
+ }
23
+ assert_equal({:three=>nil, :one=>["bar"], :two=>["baz", nil]}, copy)
24
+ }
25
+ end
26
+
27
+ def test_breadth
28
+ TESTDATA.each{|td|
29
+ copy=Ron::GraphWalk::breadth_graphcopy(td){|cntr,o,i,ty,useit|
30
+ if :one==i and Ron::GraphEdge::HashValue==ty
31
+ useit[0]=true
32
+ ["bar"]
33
+ elsif "foo"==o
34
+ useit[0]=true
35
+ "baz"
36
+ end
37
+ }
38
+ assert_equal({:three=>nil, :one=>["baz", nil], :two=>["baz", nil]}, copy)
39
+ #?? is that really the right output ??
40
+ }
41
+ end
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Ron
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Caleb Clausen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-05 00:00:00 -07:00
12
+ date: 2010-01-03 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -28,20 +28,19 @@ files:
28
28
  - GPL
29
29
  - History.txt
30
30
  - Makefile
31
- - Manifest.txt
32
31
  - README.txt
33
- - Rakefile
34
32
  - lib/ron.rb
35
33
  - lib/ron/column_order.rb
34
+ - lib/ron/float_accurate_to_s.rb
36
35
  - lib/ron/graphedge.rb
37
36
  - lib/ron/version.rb
38
37
  - ron.gemspec
39
38
  - test/test_all.rb
39
+ - test/test_graphcopy.rb
40
40
  has_rdoc: true
41
41
  homepage: http://github.com/coatl/ron
42
42
  post_install_message:
43
43
  rdoc_options:
44
- - --inline-source
45
44
  - --main
46
45
  - README.txt
47
46
  require_paths:
@@ -1,9 +0,0 @@
1
- History.txt
2
- Manifest.txt
3
- README.txt
4
- Rakefile
5
- lib/ron.rb
6
- lib/ron/version.rb
7
- lib/ron/graphedge.rb
8
- lib/ron/column_order.rb
9
- test/test_all.rb
data/Rakefile DELETED
@@ -1,37 +0,0 @@
1
- # Copyright (C) 2006 Caleb Clausen
2
- # Distributed under the terms of Ruby's license.
3
- require 'rubygems'
4
- require 'hoe'
5
- require 'lib/ron/version.rb'
6
-
7
- if $*==["test"]
8
- #hack to get 'rake test' to stay in one process
9
- #which keeps netbeans happy
10
- $:<<"lib"
11
- # require 'ron.rb'
12
- # require "test/unit"
13
- require "test/test_all.rb"
14
- Test::Unit::AutoRunner.run
15
- exit
16
- end
17
-
18
-
19
-
20
- Hoe.new("Ron", Ron::VERSION) do |_|
21
-
22
- _.author = "Caleb Clausen"
23
- _.email = "ron-owner @at@ inforadical .dot. net"
24
- _.url = "http://rubyforge.org/projects/ron"
25
- _.summary = "Ron is a ruby-based serialization format for representing any ruby
26
- object graph."
27
-
28
- _.description = <<'END'
29
- Ron is very much like JSON, but based around Ruby instead of JavaScript.
30
- Stardard Ruby literal notation is extended to include an object literal
31
- and a way to create self-referencing data structures.
32
- END
33
- _.changes='' #1st version, no changes
34
- end
35
-
36
- # add other tasks here
37
-