Ron 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +9 -0
- data/README.txt +84 -0
- data/Rakefile +27 -0
- data/lib/ron.rb +466 -0
- data/lib/ron/column_order.rb +170 -0
- data/lib/ron/graphedge.rb +320 -0
- data/lib/ron/version.rb +3 -0
- data/test/test_all.rb +227 -0
- metadata +63 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
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
|
data/lib/ron/version.rb
ADDED
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:
|