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 +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:
|