Ron 0.1.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.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2006-10-07
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
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/README.txt ADDED
@@ -0,0 +1,84 @@
1
+ Well, who needed another serialization format? Not me, but that's
2
+ what I've made. Ruby Object Graph (Ron) is a textual format for
3
+ the representation of Ruby data structures. It's somewhat like
4
+ YAML, XML, or (most of all) JSON. However, since it is defined
5
+ entirely within and as a subset of Ruby, it has the slick
6
+ property that Ron expressions are legal Ruby. Thus it is very
7
+ like JSON, except that it's Ruby-centered instead of being
8
+ JavaScript-centered.
9
+
10
+ Another way to look at Ron is as a purely declarative language
11
+ for creating (almost) any type of Ruby data structure.
12
+
13
+ Ruby already has formats for representing a literal Array, Hash,
14
+ Set, String, Symbol, Integer, etc. Ron simply reuses the same
15
+ notations for those data types. The major new feature of Ron is
16
+ that it provides a literal syntax for representing arbitrary
17
+ Ruby Objects.
18
+
19
+ Say you have a class that looks like this:
20
+
21
+ class K
22
+ attr :a
23
+ attr :b
24
+ end
25
+
26
+ A Ron literal that represents a K instance might look like this:
27
+
28
+ K-{:@a=>1, :@b=>2}
29
+
30
+ Better yet, if you provide setters as well as getters, you can
31
+ leave off the @ signs:
32
+
33
+ class K
34
+ attr_accessor :a,:b
35
+ end
36
+
37
+ K-{:a=>1, :b=>2}
38
+
39
+
40
+ This construct breaks encapsulation, so use the capability with
41
+ moderation and prudence, please.
42
+
43
+ An existing object can be rendered into Ron form with a call to
44
+ Ron.dump:
45
+
46
+ Ron.dump([1,2,3]) #=>"[1, 2, 3]"
47
+
48
+ Ron.load is the inverse operation; it takes a Ron-formatted
49
+ string and converts it back to a live Ruby object. (This is
50
+ just an alias of eval.):
51
+
52
+ Ron.load("K-{:@a=>1, :@b=>2}") #=> #<K:@a=2, @b=2>
53
+
54
+
55
+ Most objects also have a #to_ron method which works just like
56
+ Ron.dump; however, Ron.dump is the preferred form.
57
+
58
+ In order to facilitate the dumping of bindings, two methods are
59
+ provided that rip out the guts of a Binding and stuff them back
60
+ in again: Binding#to_h and Binding.from_h.
61
+
62
+ bugs and limitations
63
+ Currently, some types of ruby object cannot be dumped. In some
64
+ cases, this is because it is impossible to (correctly) dump those
65
+ objects. In others, it's merely difficult. Here are the ones I
66
+ think I should eventually be doing, mostly with ParseTree help:
67
+
68
+ (anonymous) Class and Module
69
+ Method
70
+ UnboundMethod
71
+ Proc
72
+
73
+ These are very difficult, and I don't see how to approach them
74
+ correctly (but it would be very nice...):
75
+
76
+ Thread
77
+ Continuation
78
+
79
+ These are more or less impossible:
80
+
81
+ File
82
+ Dir
83
+ Process
84
+ IO
data/Rakefile ADDED
@@ -0,0 +1,27 @@
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
+
8
+
9
+
10
+ Hoe.new("Ron", Ron::VERSION) do |_|
11
+
12
+ _.author = "Caleb Clausen"
13
+ _.email = "ron-owner @at@ inforadical .dot. net"
14
+ _.url = "http://rubyforge.org/projects/ron"
15
+ _.summary = "Ron is a ruby-based serialization format for representing any ruby
16
+ object graph."
17
+
18
+ _.description = <<'END'
19
+ Ron is very much like JSON, but based around Ruby instead of JavaScript.
20
+ Stardard Ruby literal notation is extended to include an object literal
21
+ and a way to create self-referencing data structures.
22
+ END
23
+ _.changes='' #1st version, no changes
24
+ end
25
+
26
+ # add other tasks here
27
+
data/lib/ron.rb ADDED
@@ -0,0 +1,466 @@
1
+ #require 'warning'
2
+ require 'ron/graphedge'
3
+
4
+
5
+
6
+ #to_ron
7
+
8
+ class Object
9
+ #forward decls
10
+ def to_ron; end
11
+ def to_ron_list(x=nil); end
12
+ end
13
+
14
+ module Ron
15
+ class NotSerializeableError<RuntimeError; end
16
+ class NotYetSerializeableError<NotSerializeableError; end
17
+ class NotYetMaybeNeverSerializeableError<NotSerializeableError; end
18
+
19
+ class Session
20
+ def initialize
21
+ @objects_seen={} #hash of object id to reference to string in output
22
+ @objects_in_progress=[]
23
+ #@output=[] #array of strings of data to be output
24
+ end
25
+ attr_reader :objects_in_progress, :objects_seen
26
+ end
27
+ IGNORED_INSTANCE_VARIABLES=Hash.new{[]}
28
+ DefaultMarker={}.freeze
29
+
30
+ @@local_names_generated=0
31
+ def Ron.gen_local_name
32
+ "v#{@@local_names_generated+=1}_"
33
+ end
34
+
35
+ def self.dump obj
36
+ obj.to_ron
37
+ end
38
+
39
+ def self.load str
40
+ eval str
41
+ end
42
+
43
+ CaptureCtx=[]
44
+ =begin
45
+ def self.recurse_safe_objects_equal?(o1,o2,session={})
46
+ pair=[o1.__id__,o2.__id__]
47
+ return true if session[pair]
48
+ session[pair]=1
49
+
50
+ o1.class==o2.class and
51
+ case o1
52
+ when Array:
53
+ o1.size==o2.size and
54
+ o1.each_with_index{|i1,idx|
55
+ recurse_safe_objects_equal?(i1,o2[idx],session) or return
56
+ }
57
+
58
+ when Hash:
59
+ #oops, this depends on #== and #hash working right for recursive structures, which they don't.
60
+ o1.size==o2.size or return
61
+ recurse_safe_objects_equal? o1.default,o2.default,session or return
62
+ o1.each_with_index{|(idx,i1),bogus|
63
+ return unless (o2.key? idx and recurse_safe_objects_equal? i1, o2[idx],session)
64
+ }
65
+
66
+ when Range:
67
+ o1.exclude_end?()==o2.exclude_end?() and
68
+ recurse_safe_objects_equal? o1.begin, o2.begin,session and
69
+ recurse_safe_objects_equal? o1.end, o2.end,session
70
+
71
+ when Struct:
72
+ (mems=o1.members).size==o2.members.size and
73
+ mems.each{|i|
74
+ recurse_safe_objects_equal? o1[i], (o2[i] rescue return),session or return
75
+ }
76
+ when Binding:
77
+ recurse_safe_objects_equal? o1.to_h, o2.to_h, session
78
+ when Proc,Integer,Float,String:
79
+ o1==o2
80
+ when Thread,ThreadGroup,Process,IO,Symbol,
81
+ Continuation,Class,Module:
82
+ return o1.equal?(o2)
83
+ when Exception:
84
+ o1.message==o2.message
85
+ when MatchData:
86
+ o1.to_a==o2.to_a
87
+ when Time:
88
+ o1.eql? o2
89
+ else true
90
+ end and
91
+ (iv1=o1.instance_variables).size==o2.instance_variables.size and
92
+ iv1.each{|name|
93
+ recurse_safe_objects_equal? \
94
+ o1.instance_variable_get(name),
95
+ (o2.instance_variable_get(name) rescue return),session or return
96
+ }
97
+ end
98
+ =end
99
+ end
100
+
101
+ SR=Ron::DefaultMarker
102
+ #Recursive([SR]) #recursive array
103
+ #Recursive({SR=>SR}) #doubly recursive hash
104
+ #Recursive(Set[SR]) #recursive Set
105
+
106
+ module Recursive; end
107
+ SelfReferencing=Recursive #old name alias
108
+ SelfReferential=SelfReferencing #old name alias
109
+
110
+ def Recursive *args #marker='foo',data
111
+ marker,data=*case args.size
112
+ when 2: args
113
+ when 1: [::Ron::DefaultMarker,args.last]
114
+ else raise ArgumentError
115
+ end
116
+
117
+ ::Ron::GraphWalk.graphwalk(data){|cntr,o,i,ty|
118
+ if o.equal? marker
119
+ ty.new(cntr,i,1){data}.replace
120
+ data.extend Recursive
121
+ end
122
+ }
123
+ data
124
+ end
125
+ def SelfReferencing #old name alias
126
+ Recursive(v=Object.new, yield(v))
127
+ end
128
+ alias SelfReferential SelfReferencing #old name alias
129
+
130
+ class Object
131
+ def with_ivars(hash)
132
+ hash.each_pair{|k,v|
133
+ instance_variable_set(k,v)
134
+ }
135
+ self
136
+ end
137
+ end
138
+
139
+ [Fixnum,NilClass,FalseClass,TrueClass,Symbol].each{|k|
140
+ k.class_eval{ alias to_ron inspect; undef to_ron_list }
141
+ }
142
+ [Bignum,Float,].each{|k|
143
+ k.class_eval{ def to_ron_list(session) [inspect] end }
144
+ }
145
+
146
+ class String
147
+ def to_ron_list session
148
+
149
+ [ "'", gsub(/['\\]/){ '\\\\'+$&}, "'" ]
150
+ end
151
+ end
152
+
153
+ class Regexp
154
+ def to_ron_list session
155
+
156
+ [ inspect ]
157
+ end
158
+ end
159
+
160
+ class Array
161
+ def to_ron_list session
162
+ ["["] +
163
+ map{|i|
164
+ i.to_ron_list2(session)<<', '
165
+ }.flatten<<
166
+ "]"
167
+ end
168
+ end
169
+
170
+ class Hash
171
+ def to_ron_list session
172
+ ["{"]+map{|k,v|
173
+ Array(k.to_ron_list2(session)).push "=>",
174
+ v.to_ron_list2(session)<<', '
175
+ }.flatten<<"}"
176
+ end
177
+ end
178
+
179
+ class Object
180
+ undef to_ron, to_ron_list #avoid warnings
181
+ def to_ron
182
+ #warning "additional modules not handled"
183
+ #warning "prettified output not supported"
184
+
185
+ to_ron_list2.to_s
186
+ end
187
+
188
+ def to_ron_list session
189
+ self.class.name or raise NotSerializableError
190
+ [self.class.name,"-{",
191
+ *instance_variables.map{|ivar|
192
+ [ivar.to_ron,"=>",
193
+ instance_variable_get(ivar).to_ron_list2(session),', ']
194
+ }.flatten<<
195
+ "}#end object literal"]
196
+ #end
197
+ end
198
+
199
+ def to_ron_list2(session=Ron::Session.new)
200
+ respond_to? :to_ron_list or return [to_ron]
201
+ if pair=(session.objects_in_progress.assoc __id__)
202
+ str=pair[1]
203
+ if str[/^Recursive\(/]
204
+ result=pair.last #put var name already generated into result
205
+ else
206
+ pair.push result=Ron.gen_local_name
207
+ str[0,0]="Recursive(#{result}={}, "
208
+ end
209
+ result=[result]
210
+ elsif pair=session.objects_seen[ __id__ ]
211
+ str=pair.first
212
+ if str[/^[a-z_0-9]+=[^=]/i]
213
+ result=pair.last #put var name already generated into result
214
+ else
215
+ pair.push result=Ron.gen_local_name
216
+ str[0,0]=result+"="
217
+ end
218
+ result=[result]
219
+ else
220
+ str=''
221
+ session.objects_in_progress.push [__id__,str]
222
+ result=to_ron_list(session).unshift str
223
+ if result.last=="}#end object literal"
224
+ result.last.replace "}"
225
+ else
226
+ #append instance_eval
227
+ ivars=instance_variables-::Ron::IGNORED_INSTANCE_VARIABLES[self.class]
228
+ ivars.empty? or result.push ".with_ivars(", *ivars.map{|iv|
229
+ [":",iv.to_s,"=>",instance_variable_get(iv).to_ron_list2(session),', ']
230
+ }.flatten[0...-1]<<")"
231
+ end
232
+ result.push ")" if str[/^Recursive\(/]
233
+ session.objects_seen[__id__]=[session.objects_in_progress.pop[1]]
234
+ result
235
+ end
236
+ result
237
+ end
238
+ end
239
+
240
+ class Struct
241
+ def to_ron_list session
242
+ self.class.name or raise NotSerializableError
243
+ result=[self.class.name,"-{"]+
244
+ members.map{|memb|
245
+ [memb.to_ron_list2(session) , "=>" , self[memb] , ', ']
246
+ }.flatten<<
247
+ "}"
248
+ result=["(",result,")"].flatten unless instance_variables.empty?
249
+ result
250
+ end
251
+ end
252
+
253
+ sets=[:Set,:SortedSet,:WeakRefSet]
254
+ eval sets.map{|k| <<END }.to_s
255
+ class #{k} #{'< Set' if k==:SortedSet}
256
+ def to_ron_list session
257
+ ['#{k}[']+map{|i| i.to_ron_list2(session)<<', ' }.flatten<<"]"
258
+ end
259
+ end
260
+ END
261
+ Ron::IGNORED_INSTANCE_VARIABLES[Set]=%w[@hash]
262
+ Ron::IGNORED_INSTANCE_VARIABLES[SortedSet]=%w[@hash @keys]
263
+ Ron::IGNORED_INSTANCE_VARIABLES[WeakRefSet]=%w[@ids]
264
+
265
+ class Range
266
+ def to_ron_list session
267
+ # result=
268
+ ["(",*first.to_ron_list2(session).
269
+ push( "..#{'.' if exclude_end?}" )+
270
+ last.to_ron_list2(session)<<
271
+ ")"
272
+ ]
273
+ # result.flatten!
274
+ # result
275
+ end
276
+ end
277
+
278
+
279
+ class Module
280
+ def to_ron
281
+ name.empty? and raise ::Ron::NotSerializeableError
282
+ name
283
+ end
284
+ undef to_ron_list
285
+ end
286
+
287
+ class Class
288
+ def to_ron
289
+ name.empty? and raise ::Ron::NotSerializeableError
290
+ name
291
+ end
292
+ undef to_ron_list if methods.include? "to_ron_list"
293
+ end
294
+
295
+ class Binding
296
+ def to_h
297
+ l=Kernel::eval "local_variables", self
298
+ l<<"self"
299
+ h={}
300
+ l.each{|i| h[i.to_sym]=Kernel::eval i, self }
301
+ h[:yield]=Kernel::eval "block_given? and proc{|*a| #,&b\n yield(*a) #,&b\n}", self
302
+ h
303
+ end
304
+
305
+ class<<self
306
+ def - h
307
+ h=h.dup
308
+ the_self=h.delete :self
309
+ the_block=(h.delete :yield) || nil
310
+ keys=h.keys
311
+ keys.empty? or
312
+ code=keys.map{|k|
313
+ k.to_s
314
+ }.join(',')+'=*::Ron::CaptureCtx.last'
315
+ mname="Ron__capture_binding#{Thread.current.object_id}" #unlikely method name
316
+ newmname=result=nil
317
+
318
+ eval "
319
+ Thread.critical=true
320
+ newmname=class<<the_self;
321
+ mname=oldmname='#{mname}'
322
+ im=instance_methods(false)
323
+ mname+='-' while im.include? mname
324
+ alias_method mname, oldmname
325
+ def #{mname}
326
+ #{code}
327
+ binding
328
+ end
329
+ mname
330
+ end
331
+ "
332
+ ::Ron::CaptureCtx.push h.values
333
+ result=the_self.send mname, &the_block
334
+ ::Ron::CaptureCtx.pop
335
+ class<<the_self;
336
+ self
337
+ end.send(*if newmname==mname
338
+ [:remove_method, mname]
339
+ else
340
+ [:alias_method, mname, oldmname]
341
+ end)
342
+ Thread.critical=false
343
+ result
344
+ end
345
+ alias from_h -
346
+ end
347
+
348
+ def to_ron_list session
349
+ result=to_h.to_ron_list2(session).unshift("Binding-")
350
+ result=["(",result,")"].flatten unless instance_variables.empty?
351
+ result
352
+ end
353
+ end
354
+
355
+
356
+
357
+
358
+
359
+ #I might be able to implement these eventually....
360
+ [
361
+ Proc,
362
+ Method,
363
+ UnboundMethod,
364
+ #Binding, #??
365
+ ].each{|k|
366
+ k.class_eval do
367
+ def to_ron(x=nil); raise Ron::NotYetSerializeableError end
368
+ alias to_ron_list to_ron
369
+ end
370
+ }
371
+
372
+ #what about unnamed class and module?
373
+
374
+
375
+ #not sure about these:
376
+ [
377
+ Continuation,
378
+ :Thread,
379
+ :ThreadGroup,
380
+
381
+ :Mutex, #??
382
+ #and other interthead communication mechanisms, like
383
+ :Queue,
384
+ :SizedQueue,
385
+ :RingBuffer,
386
+ :ConditionVariable,
387
+ :Semaphore,
388
+ :CountingSemaphore,
389
+ :Multiwait,
390
+ ].each{|sym|
391
+ k=(Object.const_get(sym) rescue nil) and
392
+ k.class_eval do
393
+ def to_ron(x=nil); raise Ron:: NotYetMaybeNeverSerializeableError end
394
+ alias to_ron_list to_ron
395
+ end
396
+ }
397
+
398
+
399
+ #not a chance in hell:
400
+ [
401
+ File,
402
+ IO,
403
+ Dir,
404
+ Process,
405
+
406
+
407
+
408
+ ].each{|k|
409
+ k.class_eval do
410
+ def to_ron(x=nil); raise Ron::NotSerializeableError end
411
+ alias to_ron_list to_ron
412
+ end
413
+ }
414
+
415
+ =begin Kernel#self_referencing test case
416
+ exp=Reg::const # or Reg::var
417
+ stmt=Reg::const # or Reg::var
418
+
419
+ exp=exp.set! -[exp, '+', exp]|-['(', stmt, ')']|Integer
420
+ stmt=stmt.set! (-[stmt, ';', stmt]|-[exp, "\n"])-1
421
+ -- or --
422
+ stmt=Recursive(stmt={},
423
+ (-[stmt, ';', stmt]|
424
+ -[exp=Recursive(exp={},
425
+ -[exp, '+', exp]|
426
+ -['(', stmt, ')']|
427
+ Integer
428
+ ), "\n"])-1
429
+ )
430
+ =end
431
+
432
+ #Class#-
433
+ class Class
434
+ #construct an instance of a class from the data in hash
435
+ def - hash
436
+ #name.empty? and huh
437
+ Array===hash and return make( *hash )
438
+ allocate.instance_eval{
439
+ hash.each{|(k,v)|
440
+ if ?@==k.to_s[0]
441
+ instance_variable_set(k,v)
442
+ else
443
+ send(k+"=",v)
444
+ end
445
+ }
446
+ return self
447
+ }
448
+ end
449
+ end
450
+
451
+ #Struct#-
452
+ class<<Struct
453
+ alias new__no_minus_op new
454
+ def new *args
455
+ result=new__no_minus_op(*args)
456
+ class<<result
457
+ def - hash
458
+ name.empty? and huh
459
+ result=allocate
460
+ hash.each{|(k,v)| result[k]=v }
461
+ result
462
+ end
463
+ end
464
+ result
465
+ end
466
+ end
@@ -0,0 +1,170 @@
1
+ module AttributeOrder
2
+ def self.included(othermod)
3
+ othermod.extend case othermod
4
+ when Class: ForClass
5
+ when Module: ForModule
6
+ else raise ArgumentError
7
+ end
8
+ end
9
+
10
+ class NoMoreSuperMethods<RuntimeError; end
11
+
12
+ def <=>(other)
13
+ raise NoMoreSuperMethods
14
+ end
15
+
16
+ def ==(other)
17
+ raise NoMoreSuperMethods
18
+ end
19
+
20
+ def eql?(other)
21
+ raise NoMoreSuperMethods
22
+ end
23
+
24
+ def hash
25
+ raise NoMoreSuperMethods
26
+ end
27
+
28
+ module ForModule
29
+
30
+
31
+ #writes a bunch of comparison/equality/construction methods for you,
32
+ #all you have to do is define an order for the set of 'columns' or
33
+ #attributes of the object for use in sorting. the attributes are
34
+ #allowed to be instance variables (with @) or properties (pairs of
35
+ #setter/getter methods)(without @).
36
+ #column_order and subclassing:
37
+ #The columns that were declared in any a superclass (or module)
38
+ #are considered before the columns declared in this class. any
39
+ #column from a superclass need not be repeated in this class.
40
+ #the instance methods create by column_order are:
41
+ # #==, #<=>, #eql?, and #hash.
42
+ #none of these are written if a version already exists in this
43
+ #subclass (not a module or superclass).
44
+
45
+ #in addition, there are a couple of private utility methods
46
+ #created: #attributes and #same_kind?.
47
+ #also, a new constructor method, #make, is written in the class.
48
+
49
+ def attribute_order(*ivars)
50
+ #get trailing option list, if any (:column_order is only 1 known)
51
+ @COLUMN_ORDER=ivars.pop[:column_order] if Hash===ivars.last
52
+
53
+ #normalize/type check parameters
54
+ @REG_ATTRIBUTE_ORDER= ivars.map{|ivar|
55
+ ivar=ivar.to_s
56
+ raise ArgumentError unless #check for malformed names
57
+ ivar[/^(@|self\.)?[A-Za-z_][A-Za-z0-9_]*$/]
58
+
59
+ #prepend 'self.' if ivar doesn't start with @ or 'self.'
60
+ ivar[/^(@|self\.)/] or ivar[0,0]="self."
61
+ ivar
62
+ }
63
+
64
+ #normalize column_order
65
+ @COLUMN_ORDER and
66
+ @COLUMN_ORDER.map!{|ivar|
67
+ ivar=case ivar
68
+ when Integer: @REG_ATTRIBUTE_ORDER[ivar]
69
+ when Symbol: ivar.to_s
70
+ when String: ivar
71
+ else fail ArgumentError
72
+ end
73
+
74
+ raise ArgumentError unless #check for malformed names
75
+ ivar[/^(@|self\.)?[A-Za-z_][A-Za-z0-9_]*$/]
76
+
77
+ #prepend 'self.' if ivar doesn't start with @ or 'self.'
78
+ ivar[/^(@|self\.)/] or ivar[0,0]="self."
79
+ ivar
80
+ }
81
+
82
+
83
+ #column_order defaults to attribute order if not given
84
+ cols=(@COLUMN_ORDER if defined? @COLUMN_ORDER) || @REG_ATTRIBUTE_ORDER
85
+
86
+ meths=instance_methods(false)
87
+
88
+ module_eval <<-"END"
89
+ private #utility methods
90
+ huh #name collision... these names need to be unique for each module/class
91
+ def column_list
92
+ result=[#{cols.join ','}]
93
+ result.pop while !result.empty? and result.last.nil?
94
+ return result
95
+ end
96
+ def same_kind?(other) huh #name collision... these names need to be unique for each module/class
97
+ other.kind_of?(#{self}) || self.kind_of? other.class
98
+ end
99
+
100
+ public
101
+ #comparison method
102
+ def <=>(other)
103
+ (result=super rescue 0).nonzero? and return result
104
+ same_kind?(other) and
105
+ column_list<=>other.column_list
106
+ end unless meths.include?("<=>")
107
+
108
+ #equality/hash methods
109
+ def ==(other)
110
+ (super rescue true) and
111
+ same_kind?(other) and
112
+ column_list==other.column_list
113
+ end unless meths.include?("==")
114
+ def eql?(other)
115
+ (super rescue true) and
116
+ same_kind?(other) and
117
+ column_list.eql? other.column_list
118
+ end unless meths.include?("eql?")
119
+ def hash
120
+ #{ops=[:^,:+,:^,:-];i=0
121
+ column_list.inject("(super rescue 0).^ "){|s,o|
122
+ s+"#{o}.hash.#{ops[(i+=1)&3]} "
123
+ }.sub(/\.[+^-] $/,'')
124
+ }
125
+ end unless meths.include?("hash")
126
+ END
127
+ end
128
+ end
129
+
130
+ module ForClass
131
+ include ForModule
132
+ def attribute_order(*stuff)
133
+ super
134
+ attrs=ancestors.reverse.map{|anc|
135
+ anc.instance_variable_get :@REG_ATTRIBUTE_ORDER
136
+ }.compact
137
+
138
+ class_eval <<-"END"
139
+ #raw constructor
140
+ def self.make(*args)
141
+ allocate.instance_eval do
142
+ named_args=if block_given?
143
+ yield
144
+ elsif args.size==#{attrs.size+1}
145
+ args.pop
146
+ end
147
+
148
+ #array args
149
+ #{attrs.join(",")+"=*args" unless attrs.empty?}
150
+
151
+ #hash args
152
+ named_args and
153
+ eval named_args.to_a.map{|(name,val)|
154
+ name=name.to_s
155
+ if name[/^@[a-z_][a-z_0-9]*$/i]
156
+ instance_variable_set(name,val)
157
+ nil
158
+ else
159
+ #name is allowed to be a complex l-value expression: eg 'foo.bar' or '@baz[44]', etc
160
+ ["self." unless /^@[^@]/===name,name,"=",val,"\n"]
161
+ end
162
+ }
163
+ end
164
+ end
165
+ END
166
+ end
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,320 @@
1
+ require 'forwardable'
2
+ require 'set'
3
+ #require 'assert'
4
+
5
+ module Ron
6
+ #--------------------------------
7
+ #represents a path through the object graph
8
+ class GraphTrace
9
+ extend Forwardable
10
+ def initialize(points=[])
11
+ @points=points
12
+ end
13
+
14
+ attr_reader :points
15
+ def_delegators :@points, :<<
16
+ end
17
+
18
+
19
+ #--------------------------------
20
+ module GraphWalk
21
+ class<<self
22
+ #--------------------------------
23
+ def graphcopy(obj)
24
+ old2new={}
25
+ root=nil
26
+ graphwalk(obj){|cntr,o,i,ty|
27
+ newo= block_given? && (yield cntr,o,i,ty,useit=[false])
28
+ useit.first or newo ||=
29
+ old2new[o.__id__] ||= root ||= o.dup
30
+ ty.new(old2new[cntr.__id__],i,1){newo}.replace
31
+ }
32
+ return root
33
+ end
34
+
35
+ #--------------------------------
36
+ def graphwalk(obj)
37
+ yield nil,obj,nil,GraphEdge::TopLevel
38
+ todolist=[obj]
39
+ donelist=Set[]
40
+ todolist.each{|o|
41
+ traverse(o){|cntr,o2,i,ty|
42
+ unless donelist.include? [cntr.__id__,ty,i]
43
+ todolist<<o2
44
+ donelist<<[cntr.__id__,ty,i]
45
+ yield cntr,o2,i,ty
46
+ end
47
+ }
48
+ }
49
+ end
50
+
51
+ #--------------------------------
52
+ def traverse(obj)
53
+ #some other container types should be explicitly
54
+ #supported here: Set, Struct, OpenStruct, SuperStruct, Tree
55
+ #maybe others i don't know? sparse array/sparse matrix?
56
+ #ordered hashes?
57
+ case obj
58
+ when nil: #do nothing
59
+ when (Set if defined? Set),(WeakRefSet if defined? WeakRefSet):
60
+ obj.each{|elem|
61
+ yield(obj,elem, elem, GraphEdge::SetMember)
62
+ }
63
+ when Struct:
64
+ obj.members.each{|mem|
65
+ yield(obj,obj[mem],mem, GraphEdge::BracketsValue)
66
+ }
67
+ when Hash:
68
+ obj.each{|(i,elem)|
69
+ yield(obj,elem,i, GraphEdge::HashValue)
70
+ yield(obj,i,i, GraphEdge::HashKey)
71
+ }
72
+ when Array:
73
+ obj.each_with_index{|elem,i|
74
+ yield(obj,elem,i, GraphEdge::Array)
75
+ }
76
+ when Range:
77
+ yield(obj,obj.first, :first, GraphEdge::ObjectMethValue)
78
+ yield(obj,obj.last, :last, GraphEdge::ObjectMethValue)
79
+ #when RBTree: huh
80
+ when (ActiveRecord::Base if defined? ActiveRecord::Base):
81
+ obj.columns.each{|mem|
82
+ yield(obj,obj[mem],mem, GraphEdge::BracketsValue)
83
+ }
84
+ end
85
+ #traverse instance vars in any case
86
+ obj.instance_variables.each{|var|
87
+ yield obj, (obj.instance_variable_get var), var, GraphEdge::ObjectIvarValue
88
+ }
89
+ end
90
+
91
+
92
+ #---------------------------
93
+ #wannabe in class ::Array
94
+ def recursive_each arr,&block
95
+ arr.each {|item|
96
+ if item.respond_to? :to_a
97
+ recursive_each item.to_a, &block
98
+ else
99
+ block[item]
100
+ end
101
+ }
102
+ end
103
+
104
+ #---------------------------
105
+ def recursive_reverse_each arr,&block
106
+ arr.reverse_each {|item|
107
+ if item.respond_to? :to_ary
108
+ recursive_reverse_each item.to_ary, &block
109
+ else
110
+ block[item]
111
+ end
112
+ }
113
+ end
114
+
115
+ end
116
+ end
117
+
118
+ #--------------------------------
119
+ #represents an edge along the ruby object graph. (Container and position within it.)
120
+ class GraphEdge
121
+ class ContextWasRecycled < Exception; end
122
+ def initialize(context,index,len=1,&newval_code)
123
+ len>=0 or raise ArgumentError
124
+ @context,@index,@len,@newval_code=context,index,len,newval_code
125
+ ObjectSpace.define_finalizer(@context,(method :context_died))
126
+ end
127
+ attr_reader :index,:len,:context
128
+
129
+ # def new_value_set!(&nv) @newval_code=nv end
130
+ def context_type; self.class end
131
+
132
+ def call; replace; end
133
+
134
+ def context_died
135
+ @context=nil
136
+ end
137
+
138
+ def new_value
139
+ @newval_code[self]
140
+ end
141
+
142
+ #--------------------------------
143
+ class Array<GraphEdge
144
+ def initialize(context,index,len=1)
145
+ super
146
+
147
+ if Range===@index
148
+ @len=@index.last-@index.first
149
+ @len-=1 if @len.exclude_end?
150
+ @index=@index.first
151
+ end
152
+ end
153
+
154
+ def old_value
155
+ context[@index]
156
+ end
157
+
158
+ def replace(*newvals)
159
+ newvals.empty? and newvals=[new_value]
160
+ context[@index]=*newvals
161
+ end
162
+ end
163
+
164
+ #--------------------------------
165
+ class BracketsKey < GraphEdge
166
+ def old_value
167
+ @index
168
+ end
169
+ #@index is actually a list... so is newkey
170
+ def replace(*newkey)
171
+ newkey.empty? and newkey=[new_value]
172
+ context[*newkey]=context.delete(*@index)
173
+ end
174
+ end
175
+
176
+ #--------------------------------
177
+ class BracketsValue < GraphEdge
178
+ def old_value
179
+ context[*@index]
180
+ end
181
+ #@index is actually a list
182
+ def replace(newval=new_value)
183
+ context[*@index]=newval
184
+ end
185
+ end
186
+
187
+ #--------------------------------
188
+ class HashKey<GraphEdge
189
+ def old_value
190
+ @index
191
+ end
192
+
193
+ def replace(newkey=new_value)
194
+ context[newkey]=context.delete @index
195
+ end
196
+ end
197
+
198
+ #--------------------------------
199
+ class HashValue<GraphEdge
200
+ def old_value
201
+ context[@index]
202
+ end
203
+
204
+ def replace(newval=new_value)
205
+ context[@index]=newval
206
+ end
207
+ end
208
+
209
+ #--------------------------------
210
+ class SetMember<GraphEdge
211
+ def old_value
212
+ @index
213
+ end
214
+
215
+ def replace(newval=new_value)
216
+ context.delete @index
217
+ context<<newval
218
+ end
219
+ end
220
+
221
+ #--------------------------------
222
+ class HashDefaultValue<GraphEdge
223
+ def initialize(context,index=nil,len=1)
224
+ super
225
+ end
226
+ def old_value
227
+ context.default @index
228
+ end
229
+
230
+ def replace(newval=nil)
231
+ raise TypeError
232
+ end
233
+ # def new_value_set!; nil end
234
+ # remove_method :new_value_set!
235
+ end
236
+
237
+ #--------------------------------
238
+ class HashDefaultKey<GraphEdge
239
+ def initialize(context,index=nil,len=1)
240
+ super
241
+ end
242
+ def old_value
243
+ nil
244
+ end
245
+
246
+ def replace(newval=nil)
247
+ raise TypeError
248
+ end
249
+ # def new_value_set!; nil end
250
+ # remove_method :new_value_set!
251
+ end
252
+
253
+ #--------------------------------
254
+ class ObjectName<GraphEdge
255
+ def initialize(context,index,len=1)
256
+ super
257
+ end
258
+ def old_value
259
+ @index
260
+ end
261
+
262
+ def replace(newval=nil)
263
+ raise TypeError
264
+ end
265
+ # def new_value_set!; nil end
266
+ # remove_method :new_value_set!
267
+ end
268
+
269
+ #--------------------------------
270
+ class ObjectIvarValue<GraphEdge
271
+ def old_value
272
+ context.instance_variable_get @index
273
+ end
274
+
275
+ def replace(newval=new_value)
276
+ context.instance_variable_set(@index, newval)
277
+ end
278
+ end
279
+
280
+ #--------------------------------
281
+ class ObjectMethValue<GraphEdge
282
+ def old_value
283
+ if ::Array===@index
284
+ if Proc===@index.last then
285
+ block=context.pop
286
+ elsif Literal===@index.last then
287
+ @index[-1]=@index[-1].unlit
288
+ end
289
+ context.send(*@index, &block)
290
+ else
291
+ context.send @index
292
+ end
293
+ end
294
+
295
+ def replace(newval=new_value)
296
+ raise TypeError if ::Array===@index
297
+ context.send "#{@index}=", newval
298
+ end
299
+ end
300
+
301
+ #--------------------------------
302
+ class TopLevel<GraphEdge
303
+ def initialize(context,index=nil,len=1,&newval_code)
304
+ super
305
+ end
306
+
307
+ def old_value
308
+ context
309
+ end
310
+
311
+ def replace(newval=new_value)
312
+ huh #can't really replace values in toplevel context...???
313
+ end
314
+
315
+ end
316
+
317
+
318
+ end
319
+
320
+ end
@@ -0,0 +1,3 @@
1
+ module Ron
2
+ VERSION="0.1.0"
3
+ end
data/test/test_all.rb ADDED
@@ -0,0 +1,227 @@
1
+ $VERBOSE=1
2
+ $Debug=1
3
+ require 'test/unit'
4
+ require 'set'
5
+ require 'ostruct'
6
+ require 'yaml'
7
+
8
+ require 'ron'
9
+ #require 'warning'
10
+
11
+ def try_require name
12
+ require name
13
+ rescue Exception
14
+ nil
15
+ end
16
+
17
+ try_require 'rubygems'
18
+ try_require("sequence")
19
+ try_require("weakrefset")
20
+ try_require 'facets/more/superstruct'
21
+
22
+ $Verbose=true
23
+
24
+ =begin
25
+ Object
26
+ Array
27
+ Hash
28
+ Struct
29
+ :SuperStruct #
30
+ :OpenStruct
31
+ :Set
32
+ :SortedSet
33
+ :WeakRefSet
34
+ Binding
35
+ (Object)
36
+ #+self-referencing versions of above
37
+
38
+ #also need a christmas tree, that incorporates at least one of each
39
+ #datum, as many of them as possible self-referencing.
40
+ #
41
+ #and don't forget repeated data
42
+ =end
43
+
44
+ class A_Class
45
+ def initialize
46
+ @a,@b=1,2
47
+ end
48
+ attr_reader :a,:b
49
+ def ==(other)
50
+ [@a,@b]==[other.a,other.b]
51
+ end
52
+ end
53
+
54
+ class A_Struct < Struct.new(:a,:b)
55
+ def initialize
56
+ self.a=1
57
+ self.b=2
58
+ end
59
+ end
60
+
61
+ class BindingMaker
62
+ def get_a_binding
63
+ a=12
64
+ binding
65
+ end
66
+ def == bm
67
+ BindingMaker===bm
68
+ end
69
+ end
70
+
71
+ class RonTest<Test::Unit::TestCase
72
+
73
+ def test_ron
74
+
75
+ s1=1.0
76
+ s2=2.0
77
+ range=s1..s2
78
+ s1.instance_variable_set(:@m, range)
79
+ s2.instance_variable_set(:@m, range)
80
+ ss=Set[s1,s2]
81
+ s1.instance_variable_set(:@n, ss)
82
+ s2.instance_variable_set(:@n, ss)
83
+
84
+ s1=1.0
85
+ s2=2.0
86
+ sss=SortedSet[s1,s2]
87
+ s1.instance_variable_set(:@n, sss)
88
+ s2.instance_variable_set(:@n, sss)
89
+
90
+ sss.inspect #disable this and tests fail... why?!?!?
91
+ data=[
92
+
93
+ 3.14159,
94
+ 2**2000,
95
+
96
+ "string",
97
+ /regexp/,
98
+ Enumerable,
99
+ Class,
100
+ BindingMaker.new.get_a_binding,
101
+ A_Struct.new,
102
+
103
+ (record = OpenStruct.new
104
+ record.name = "John Smith"
105
+ record.age = 70
106
+ record.pension = 300
107
+ record),
108
+
109
+ SortedSet[1,2,3],
110
+ 1..10,
111
+
112
+ [1,2,3],
113
+ {1=>2,3=>4},
114
+ Set[1,2,3],
115
+ (WeakRefSet[*%w[a b c]] rescue warn 'weakrefset test disabled'),
116
+ A_Class.new,
117
+ 2,
118
+ :symbol,
119
+ nil,
120
+ true,
121
+ false,
122
+
123
+
124
+ ]
125
+ data.each{|datum|
126
+ # p datum
127
+ assert_equal datum, datum
128
+ assert_equal datum, ( dup=eval datum.to_ron )
129
+ assert_equal internal_state(datum), internal_state(dup)
130
+
131
+ if case datum
132
+ when Fixnum,Symbol,true,false,nil: false
133
+ else true
134
+ end
135
+ datum.instance_eval{@a,@b=1,2}
136
+ assert_equal datum, ( dup=eval datum.to_ron )
137
+ assert_equal internal_state(datum), internal_state(dup)
138
+
139
+ datum.instance_eval{@c=self}
140
+ assert_equal datum, ( dup=eval datum.to_ron )
141
+ assert_equal internal_state(datum), internal_state(dup)
142
+ end
143
+ }
144
+ data.each{|datum|
145
+ if case datum
146
+ when Fixnum,Symbol,true,false,nil: false
147
+ else true
148
+ end
149
+ datum.instance_eval{@d=data}
150
+ assert datum, ( dup=eval datum.to_ron )
151
+ assert internal_state(datum), internal_state(dup)
152
+ end
153
+ }
154
+
155
+ data2=[
156
+ range,
157
+ sss,
158
+ (a=[];a<<a;a),
159
+ (a=[];a<<a;a<<a;a),
160
+ (h={};h[0]=h;h),
161
+ (h={};h[h]=0;h),
162
+ (h={};h[h]=h;h),
163
+ (s=Set[];s<<s;s),
164
+ ]
165
+ data2.each{|datum|
166
+ #p datum
167
+ assert_equal datum.to_yaml, datum.to_yaml
168
+ assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
169
+ assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
170
+
171
+ if case datum
172
+ when Fixnum,Symbol,true,false,nil: false
173
+ else true
174
+ end
175
+ datum.instance_eval{@a,@b=1,2}
176
+ assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
177
+ assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
178
+
179
+ datum.instance_eval{@c=self}
180
+ assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
181
+ assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
182
+ end
183
+ }
184
+ datum= ((w=WeakRefSet[];w<<w;w) rescue warn 'weakrefset test disabled')
185
+ assert_equal datum.inspect, datum.inspect
186
+ assert_equal datum.inspect, ( dup=eval datum.to_ron ).inspect
187
+ assert_equal internal_state(datum).inspect, internal_state(dup).inspect
188
+
189
+ if case datum
190
+ when Fixnum,Symbol,true,false,nil: false
191
+ else true
192
+ end
193
+ datum.instance_eval{@a,@b=1,2}
194
+ assert_equal datum.inspect, ( dup=eval datum.to_ron ).inspect
195
+ assert_equal internal_state(datum).inspect, internal_state(dup).inspect
196
+
197
+ datum.instance_eval{@c=self}
198
+ assert_equal datum.inspect, ( dup=eval datum.to_ron ).inspect
199
+ assert_equal internal_state(datum).inspect, internal_state(dup).inspect
200
+ end
201
+
202
+ data2.each{|datum|
203
+ if case datum
204
+ when Fixnum,Symbol,true,false,nil: false
205
+ else true
206
+ end
207
+ datum.instance_eval{@d=data;@e=data2}
208
+ #breaks yaml
209
+ # assert_equal datum.to_yaml, ( dup=eval datum.to_ron ).to_yaml
210
+ # assert_equal internal_state(datum).to_yaml, internal_state(dup).to_yaml
211
+ end
212
+ }
213
+ end
214
+
215
+ def internal_state x
216
+ list=(x.instance_variables-::Ron::IGNORED_INSTANCE_VARIABLES[x.class]).sort
217
+ [list]+list.map{|iv| x.instance_variable_get(iv)}
218
+ end
219
+
220
+ end
221
+
222
+ class Binding
223
+ def ==(other)
224
+ Binding===other or return
225
+ to_h==other.to_h
226
+ end
227
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: Ron
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2006-10-09 00:00:00 -07:00
8
+ summary: Ron is a ruby-based serialization format for representing any ruby object graph.
9
+ require_paths:
10
+ - lib
11
+ - test
12
+ email: ron-owner @at@ inforadical .dot. net
13
+ homepage: http://rubyforge.org/projects/ron
14
+ rubyforge_project: ron
15
+ description: Ron is very much like JSON, but based around Ruby instead of JavaScript. Stardard Ruby literal notation is extended to include an object literal and a way to create self-referencing data structures.
16
+ autorequire:
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ - - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ signing_key:
28
+ cert_chain:
29
+ post_install_message:
30
+ authors:
31
+ - Caleb Clausen
32
+ files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.txt
36
+ - Rakefile
37
+ - lib/ron.rb
38
+ - lib/ron/version.rb
39
+ - lib/ron/graphedge.rb
40
+ - lib/ron/column_order.rb
41
+ - test/test_all.rb
42
+ test_files:
43
+ - test/test_all.rb
44
+ rdoc_options: []
45
+
46
+ extra_rdoc_files: []
47
+
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ requirements: []
53
+
54
+ dependencies:
55
+ - !ruby/object:Gem::Dependency
56
+ name: hoe
57
+ version_requirement:
58
+ version_requirements: !ruby/object:Gem::Version::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.1.0
63
+ version: