Ron 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: