Ron 0.1.1 → 0.1.2

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,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
-