cgen 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/History.txt +199 -0
- data/README.txt +34 -0
- data/examples/bench.rb +14 -0
- data/examples/complex.rb +63 -0
- data/examples/complex2.rb +48 -0
- data/examples/cshadow-example.rb +55 -0
- data/examples/cshadow-point.rb +58 -0
- data/examples/ctest.rb +34 -0
- data/examples/ctest2.rb +32 -0
- data/examples/ctest3.rb +179 -0
- data/examples/ctest4.rb +18 -0
- data/examples/ctest5.rb +27 -0
- data/examples/example-ruby-talk-30April2004.rb +65 -0
- data/examples/fixed-array.rb +221 -0
- data/examples/inherit-example.rb +26 -0
- data/examples/inherit-example.txt +80 -0
- data/examples/instance-eval.rb +66 -0
- data/examples/ivset.rb +55 -0
- data/examples/marshal-test.rb +19 -0
- data/examples/matrix.rb +91 -0
- data/examples/modular-def.rb +87 -0
- data/examples/objattr.rb +46 -0
- data/examples/opaque-struct-test.rb +36 -0
- data/examples/sample.rb +184 -0
- data/examples/struct.rb +103 -0
- data/examples/test.rb +24 -0
- data/examples/yaml.rb +56 -0
- data/install.rb +1015 -0
- data/lib/cgen/attribute.rb +414 -0
- data/lib/cgen/cgen.rb +2041 -0
- data/lib/cgen/cshadow.rb +1037 -0
- data/lib/cgen/inherit.rb +46 -0
- data/rakefile +42 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test-attribute.rb +430 -0
- data/test/test-cgen.rb +127 -0
- data/test/test-cshadow.rb +289 -0
- data/test/test.rb +17 -0
- metadata +123 -0
data/lib/cgen/cshadow.rb
ADDED
@@ -0,0 +1,1037 @@
|
|
1
|
+
require "cgen/cgen"
|
2
|
+
require "cgen/attribute"
|
3
|
+
|
4
|
+
# == Overview
|
5
|
+
#
|
6
|
+
# CShadow is a way of creating objects which transparently mix C data with Ruby
|
7
|
+
# data.
|
8
|
+
#
|
9
|
+
# The CShadow module is a mix-in which adds a C struct to objects which derive
|
10
|
+
# from the class which includes it. The data members of the C struct, called
|
11
|
+
# shadow attributes, may vary in subclasses of this base class. Shadow attrs can
|
12
|
+
# be added to any class inheriting from the base class, and they will propagate
|
13
|
+
# along the ordinary ruby inheritance hierarchy. (For now, shadow attributes
|
14
|
+
# cannot be defined in modules.)
|
15
|
+
#
|
16
|
+
# The CShadow module uses CGenerator's structure templates to handle the code
|
17
|
+
# generation. CGenerator is also useful for inline C definitons of methods that
|
18
|
+
# operate on shadow attributes.
|
19
|
+
#
|
20
|
+
# CShadow cooperates with the CShadow::Attribute subclasses defined in
|
21
|
+
# attribute.rb to handle the mark and free functions, type checking and
|
22
|
+
# conversion, serialization, and so on. New attribute types can be added easily.
|
23
|
+
#
|
24
|
+
# ==Usage
|
25
|
+
#
|
26
|
+
# class MyClass
|
27
|
+
# include CShadow
|
28
|
+
#
|
29
|
+
# Include CShadow in the base class(es) that need to have shadow attributes. The
|
30
|
+
# base class is assigned a CGenerator::Library, which can be accessed using
|
31
|
+
# the base class's #shadow_library method. Each subclass of the base class will
|
32
|
+
# be associated with a struct defined in this library's .h files.
|
33
|
+
#
|
34
|
+
# As usual, the #initialize method of the class will be called when the object
|
35
|
+
# is created, and the arguments are whatever was passed to #new, including the
|
36
|
+
# block, if any. You can write your own #initialize method to assign initial
|
37
|
+
# values to the shadow attrs. (Typically, shadow attrs are initialized by
|
38
|
+
# default to nil or 0. See attribute.rb.)
|
39
|
+
#
|
40
|
+
# The file name of the library is the same as the name of the class which
|
41
|
+
# includes CShadow, by default, and the library directory is generated in the
|
42
|
+
# current dir when #commit is called. To change the name, or to use a different
|
43
|
+
# library:
|
44
|
+
#
|
45
|
+
# shadow_library <Library, Class, or String>
|
46
|
+
#
|
47
|
+
# The argument can be another library (instance of CShadow::Library), a class
|
48
|
+
# which includes CShadow, in which case the library of the class is used, or a
|
49
|
+
# string, in which case a new library is created. Using this feature, several
|
50
|
+
# base classes (and their descendants) can be kept in the same library. It is
|
51
|
+
# not possible to split a tree (a base class which includes CShadow and its
|
52
|
+
# descendants) into more than one library.
|
53
|
+
#
|
54
|
+
# Shadow classes that are placed in the same library can still be put in
|
55
|
+
# separate C source files, using #shadow_library_file:
|
56
|
+
#
|
57
|
+
# shadow_library_file <CFile or String>
|
58
|
+
#
|
59
|
+
# This setting is inherited (and can be overridden) by subclasses of the current
|
60
|
+
# class. If a class calls both #shadow_library and #shadow_library_file then
|
61
|
+
# they must be called in that order. Note that anything defined before the
|
62
|
+
# #shadow_library_file statement will not be placed in the specifed file.
|
63
|
+
#
|
64
|
+
# The C include and source files for the current class can be accessed with
|
65
|
+
# #shadow_library_include_file and #shadow_library_source_file. This is not
|
66
|
+
# required for normal use.
|
67
|
+
#
|
68
|
+
# Behavior at commit time can be controlled by scheduling #before_commit and
|
69
|
+
# #after_commit procs. These procs are called immediately before and after the
|
70
|
+
# actual commit, which allows, for example, removing instances that would
|
71
|
+
# otherwise cause an exception, or creating the instance of a singleton class.
|
72
|
+
# The #before_commit and #after_commit class methods of CShadow delegate to the
|
73
|
+
# shadow_library's methods. See CGenerator for details.
|
74
|
+
#
|
75
|
+
# ===Struct members
|
76
|
+
#
|
77
|
+
# All shadow structs have a +self+ member of type +VALUE+ which points to the
|
78
|
+
# Ruby object.
|
79
|
+
#
|
80
|
+
# The subclass struct inherits members of the base class struct.
|
81
|
+
#
|
82
|
+
# There are two types of shadow attributes, declared with #shadow_attr
|
83
|
+
# and friends:
|
84
|
+
#
|
85
|
+
# (1) C data::
|
86
|
+
#
|
87
|
+
# :d => "double d", :x => "char *foo"
|
88
|
+
#
|
89
|
+
# (2) Ruby value:
|
90
|
+
#
|
91
|
+
# :str => String, :obj => Object
|
92
|
+
#
|
93
|
+
# In addition to shadow attributes, you can declare other struct members using
|
94
|
+
#
|
95
|
+
# shadow_struct.declare ...
|
96
|
+
#
|
97
|
+
# as in cgen.
|
98
|
+
#
|
99
|
+
# For example, the ruby code:
|
100
|
+
#
|
101
|
+
####
|
102
|
+
#
|
103
|
+
# produces the following struct defs (as well as some functions):
|
104
|
+
#
|
105
|
+
# ===Type checking and conversion
|
106
|
+
#
|
107
|
+
# In case (1) (C data attribute), assignments to int and double struct members
|
108
|
+
# are done with +NUM2INT+ and +NUM2DBL+, which convert between numeric types,
|
109
|
+
# but raise exceptions for unconvertible types. The <tt>char *</tt> case uses
|
110
|
+
# StringValue (formerly +rb_str2cstr+), which behaves analogously.
|
111
|
+
#
|
112
|
+
# In case (2) (Ruby value attribute) the assigned value must always be a
|
113
|
+
# descendant of the specified type, except that +nil+ is always accepted. The
|
114
|
+
# purpose of specifying a class (should also allow type predicate!) is to allow
|
115
|
+
# C code to assume that certain struct members are present in the shadow struct,
|
116
|
+
# and so it can be safely cast to the "ancestor" struct.
|
117
|
+
#
|
118
|
+
# ===Adding methods
|
119
|
+
#
|
120
|
+
# CGenerator provides a general interface to the Ruby-C api. However, for
|
121
|
+
# simplicity, CShadow defines three methods in the client class for defining
|
122
|
+
# methods and class methods:
|
123
|
+
#
|
124
|
+
# CShadow.define_c_method CShadow.define_c_class_method
|
125
|
+
# CShadow.define_c_function
|
126
|
+
#
|
127
|
+
# ===Memory management
|
128
|
+
#
|
129
|
+
# Each type of attribute is responsible for managing any required marking of
|
130
|
+
# referenced Ruby objects or freeing of allocated bocks of memory. See Attribute
|
131
|
+
# and its subclasses for details.
|
132
|
+
#
|
133
|
+
# ===C attribute plug-ins
|
134
|
+
#
|
135
|
+
# CShadow's #shadow_attr methods check for one (no more, no less) matching
|
136
|
+
# attribute class. The details of the matching depend on the attribute class.
|
137
|
+
# See Attribute for details. Additional attribute classes can easily be added
|
138
|
+
# simply by subclassing CShadow::Attribute.
|
139
|
+
#
|
140
|
+
# ===Namespace usage
|
141
|
+
#
|
142
|
+
# TODO: prefix every attribute, method, constant name with "cshadow_"?
|
143
|
+
#
|
144
|
+
# ===Using CShadow classes with YAML
|
145
|
+
#
|
146
|
+
# CShadow classes can serialize via YAML. Both shadow attributes and ordinary
|
147
|
+
# ruby instance variables are serialized. No additional attribute code is
|
148
|
+
# needed, because of the generic load/dump mechanism used in cshadow (attributes
|
149
|
+
# are pushed into or shifted out of an array, which is passed to or from the
|
150
|
+
# serialization protocol, either Marshal or YAML). The ordering of shadow
|
151
|
+
# attributes is preserved when dumping to a YAML string.
|
152
|
+
#
|
153
|
+
# The only user requirement is that, before attempting to load shadow class
|
154
|
+
# instances from YAML string, the shadow class types must be registered with
|
155
|
+
# YAML. This is simple:
|
156
|
+
#
|
157
|
+
# CShadow.allow_yaml
|
158
|
+
#
|
159
|
+
# This method may be called before or after committing. Calling this method also
|
160
|
+
# loads the standard yaml library, if it has not already been loaded.
|
161
|
+
#
|
162
|
+
# See examples/yaml.rb
|
163
|
+
#
|
164
|
+
# ===Common problems and their solutions
|
165
|
+
#
|
166
|
+
# Do you get a NameError because accessor methods are not defined? Make sure you
|
167
|
+
# commit your class.
|
168
|
+
#
|
169
|
+
# You assign to an attribute but it doesn't change? Ruby assumes that, in an
|
170
|
+
# assignment like "x=1", the left hand side is a local variable. For all writer
|
171
|
+
# methods, whether Ruby or C attributes, you need to do "self.x=1".
|
172
|
+
#
|
173
|
+
# ==Notes
|
174
|
+
#
|
175
|
+
# * As with most modules, including CShadow more than once has no effect.
|
176
|
+
# However, CShadow cannot currently be included in another module.
|
177
|
+
#
|
178
|
+
# * In addition to the included examples, the RedShift project uses CShadow
|
179
|
+
# extensively. See http://rubyforge.org/projects/redshift.
|
180
|
+
#
|
181
|
+
# ==Limitations:
|
182
|
+
#
|
183
|
+
# * Hash args are ordered unpredictably, so if struct member order is
|
184
|
+
# significant (for example, because you want to pass the struct to C code that
|
185
|
+
# expects it that way), use a separate declare statement for each member. Also,
|
186
|
+
# take note of the self pointer at the beginning of the struct.
|
187
|
+
#
|
188
|
+
# * Creating a ruby+shadow object has a bit more time/space overhead than just a
|
189
|
+
# C object, so CShadow may not be the best mechansism for managing heap
|
190
|
+
# allocated data (for example, if you want lots of 2-element arrays). Also,
|
191
|
+
# CShadow objects are fixed in size (though their members can point to other
|
192
|
+
# structures).
|
193
|
+
#
|
194
|
+
# * CShadow attributes are, of course, not dynamic. They are fixed at the time
|
195
|
+
# of #commit. Otherwise, they behave essentially like Ruby attributes, except
|
196
|
+
# that they can be accessed only with methods or from C code; they cannot be
|
197
|
+
# accessed with the @ notation. Of course, the reader and writer for a shadow
|
198
|
+
# attribute can be flagged as protected or private. However, a private writer
|
199
|
+
# cannot be used, since by definition private methods can only be called in the
|
200
|
+
# receiverless form.
|
201
|
+
#
|
202
|
+
# * CShadow is designed for efficient in-memory structs, not packed,
|
203
|
+
# network-ordered data as for example in network protocols. See the
|
204
|
+
# bit-struct project for the latter.
|
205
|
+
#
|
206
|
+
# ==To do:
|
207
|
+
#
|
208
|
+
# * It should be easier to get a handle to entities. Below, shadow_attr has been
|
209
|
+
# hacked to return a list of pairs of functions. But it should be easier and
|
210
|
+
# more general.
|
211
|
+
#
|
212
|
+
# * Optimization: if class A<B, and their free func have the same content, use
|
213
|
+
# B's function in A, and don't generate a new function for A. Similarly for all
|
214
|
+
# the other kinds of functions.
|
215
|
+
#
|
216
|
+
# * Allow
|
217
|
+
#
|
218
|
+
# shadow_attr "int x", "double y"
|
219
|
+
#
|
220
|
+
# or even
|
221
|
+
#
|
222
|
+
# attr_accessor :a, :b, "int x", "double y"
|
223
|
+
#
|
224
|
+
# and (in cgen)
|
225
|
+
#
|
226
|
+
# declare "int x", "double y"
|
227
|
+
#
|
228
|
+
# The ruby name will be extracted from the string using the matching pattern.
|
229
|
+
#
|
230
|
+
# * Generate documentation for the shadow class.
|
231
|
+
#
|
232
|
+
# * Change name to CStruct? CStructure?
|
233
|
+
#
|
234
|
+
# * Find a way to propagate append_features so that CShadow can be included in
|
235
|
+
# modules and modules can contribute shadow_attrs.
|
236
|
+
#
|
237
|
+
# * option to omit the "self" pointer, or put it at the end of the struct
|
238
|
+
# automatically omit it in a class if no ShadowObjectAttributes point to it?
|
239
|
+
#
|
240
|
+
# * shadow_struct_constructor class method to use DATA_WRAP_STRUCT
|
241
|
+
#
|
242
|
+
# * Use CNativeAttribute as a default attribute? Or use the attr class hierarchy
|
243
|
+
# to supply defaults?
|
244
|
+
#
|
245
|
+
module CShadow
|
246
|
+
SHADOW_SUFFIX = "_Shadow"
|
247
|
+
|
248
|
+
class Library < CGenerator::Library
|
249
|
+
def initialize(*args) # :nodoc:
|
250
|
+
super
|
251
|
+
|
252
|
+
before_commit do
|
253
|
+
@classes_to_commit = []
|
254
|
+
ObjectSpace.each_object(CShadowClassMethods) do |cl|
|
255
|
+
if cl.shadow_library == self
|
256
|
+
@classes_to_commit << cl
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# This is done here, rather than in #inherited, to get around
|
261
|
+
# the Ruby bug with names of nested classes. It's ugly...
|
262
|
+
## this may be fixed in 1.7
|
263
|
+
# Better: register classes with libraries...
|
264
|
+
|
265
|
+
classes = Library.sort_class_tree(@classes_to_commit)
|
266
|
+
|
267
|
+
classes.each do |cl|
|
268
|
+
# make sure the following have been defined by now
|
269
|
+
cl.shadow_struct
|
270
|
+
cl.new_method; cl.mark_function; cl.free_function
|
271
|
+
cl._dump_data_method; cl._load_data_method; cl._alloc_method
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
after_commit do
|
276
|
+
for cl in @classes_to_commit
|
277
|
+
cl.protect_shadow_attrs
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Sort a list of classes. Sorting has the following properties:
|
283
|
+
#
|
284
|
+
# Deterministic -- you get the same output no matter what order the input
|
285
|
+
# is in, because we're using #sort_by.
|
286
|
+
#
|
287
|
+
# Compatible with original order on classes: superclass comes before
|
288
|
+
# subclass.
|
289
|
+
#
|
290
|
+
# Unrelated classes are ordered alphabetically by name
|
291
|
+
#
|
292
|
+
# Note that
|
293
|
+
#
|
294
|
+
# classes.sort {|c,d| (d <=> c) || (c.name <=> d.name)}
|
295
|
+
#
|
296
|
+
# is wrong.
|
297
|
+
#
|
298
|
+
def self.sort_class_tree(classes)
|
299
|
+
classes.sort_by {|c| c.ancestors.reverse!.map!{|d|d.to_s}}
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
module CShadowClassMethods
|
304
|
+
def new # :nodoc:
|
305
|
+
raise Library::CommitError,
|
306
|
+
"Cannot create shadow objects before committing library"
|
307
|
+
end
|
308
|
+
|
309
|
+
# Primarily for loading yaml data. The hash is of the form
|
310
|
+
#
|
311
|
+
# { 'attr' => value, ... }
|
312
|
+
#
|
313
|
+
# where <tt>attr</tt> is either the name of a shadow attr, or
|
314
|
+
# the name (without @) of an attribute.
|
315
|
+
#
|
316
|
+
# Warning: The hash +h+ is modified.
|
317
|
+
def new_from_hash(h)
|
318
|
+
obj = allocate
|
319
|
+
|
320
|
+
psa = shadow_attrs.select {|attr| attr.persists}
|
321
|
+
shadow_vars = psa.map{|attr|attr.var.to_s}
|
322
|
+
from_array = h.values_at(*shadow_vars)
|
323
|
+
obj._load_data(from_array)
|
324
|
+
shadow_vars.each {|v| h.delete(v) }
|
325
|
+
|
326
|
+
h.each do |ivar, value|
|
327
|
+
obj.instance_variable_set("@#{ivar}", value)
|
328
|
+
end
|
329
|
+
|
330
|
+
obj
|
331
|
+
end
|
332
|
+
|
333
|
+
# Return the base class, which is the ancestor which first included
|
334
|
+
# CShadow.
|
335
|
+
def base_class
|
336
|
+
@base_class ||= superclass.base_class
|
337
|
+
end
|
338
|
+
|
339
|
+
# If +flag+ is +true+, indicate that the class can persist
|
340
|
+
# with Marshal and YAML. This is the default. Otherwise
|
341
|
+
# if +flag+ is +false+, the entire object will be dumped as +nil+.
|
342
|
+
def persistent flag = true
|
343
|
+
unless self == base_class
|
344
|
+
raise "Persistence must be selected for the base class, " +
|
345
|
+
"#{base_class}, and it applies to the tree of subclasses."
|
346
|
+
end
|
347
|
+
@persistent = flag
|
348
|
+
end
|
349
|
+
private :persistent
|
350
|
+
|
351
|
+
def persistent?
|
352
|
+
bc = @base_class
|
353
|
+
if self == bc
|
354
|
+
@persistent
|
355
|
+
else
|
356
|
+
bc.persistent?
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Generate code and load the dynamically linked library. No further C attrs
|
361
|
+
# or methods can be defined after calling #commit.
|
362
|
+
def commit
|
363
|
+
shadow_library.commit
|
364
|
+
end
|
365
|
+
|
366
|
+
# Returns true if and only if the class haas been committed.
|
367
|
+
def committed?
|
368
|
+
shadow_library.committed?
|
369
|
+
end
|
370
|
+
|
371
|
+
# Register +block+ to be called before the #commit happens.
|
372
|
+
def before_commit(&block)
|
373
|
+
shadow_library.before_commit(&block)
|
374
|
+
end
|
375
|
+
|
376
|
+
# Register +block+ to be called after the #commit happens.
|
377
|
+
def after_commit(&block)
|
378
|
+
shadow_library.after_commit(&block)
|
379
|
+
end
|
380
|
+
|
381
|
+
# Each class which includes the CShadow module has this method to
|
382
|
+
# iterate over its shadow attributes.
|
383
|
+
#
|
384
|
+
# Note that the shadow attributes dynamically include inherited ones.
|
385
|
+
# (Dynamically in the sense that subsequent changes to superclasses are
|
386
|
+
# automatically reflected.) The order is from root to leaf of the
|
387
|
+
# inheritance chain, and within each class in order of definition. (TEST
|
388
|
+
# THIS)
|
389
|
+
def each_shadow_attr(&bl)
|
390
|
+
if superclass.respond_to? :each_shadow_attr
|
391
|
+
superclass.each_shadow_attr(&bl)
|
392
|
+
end
|
393
|
+
@shadow_attrs ||= []
|
394
|
+
@shadow_attrs.each(&bl)
|
395
|
+
end
|
396
|
+
|
397
|
+
# Returns a proxy Enumerable object referring to the same attributes as
|
398
|
+
# #each_shadow_attr. For example:
|
399
|
+
#
|
400
|
+
# sub_class.shadow_attrs.collect { |attr| attr.var }
|
401
|
+
#
|
402
|
+
# returns an array of variable names for all attributes of the class.
|
403
|
+
def shadow_attrs
|
404
|
+
proxy = Object.new.extend Enumerable
|
405
|
+
shadow_class = self
|
406
|
+
proxy.instance_eval {@target = shadow_class}
|
407
|
+
def proxy.each(&bl)
|
408
|
+
@target.each_shadow_attr(&bl)
|
409
|
+
end
|
410
|
+
proxy
|
411
|
+
end
|
412
|
+
|
413
|
+
# If +lib+ provided and this class doesn't have a library yet,
|
414
|
+
# set the library to +lib+..
|
415
|
+
#
|
416
|
+
# If +lib+ not proivided, and the class has a library, return it.
|
417
|
+
#
|
418
|
+
# If +lib+ not proivided, and the class doesn't have a library,
|
419
|
+
# construct a library with a reasonable name, and return it.
|
420
|
+
# The name is based on the full path of this class.
|
421
|
+
#
|
422
|
+
def shadow_library lib = nil
|
423
|
+
bc = base_class
|
424
|
+
if self == bc
|
425
|
+
if defined?(@shadow_library) and @shadow_library
|
426
|
+
if lib
|
427
|
+
raise RuntimeError,
|
428
|
+
"Class #{name} is already associated" +
|
429
|
+
" with library #{@shadow_library.name}."
|
430
|
+
end
|
431
|
+
else
|
432
|
+
case lib
|
433
|
+
when Library
|
434
|
+
@shadow_library = lib
|
435
|
+
when Class
|
436
|
+
begin
|
437
|
+
@shadow_library = lib.shadow_library
|
438
|
+
rescue NameError
|
439
|
+
raise ScriptError, "#{lib} does not include CShadow."
|
440
|
+
end
|
441
|
+
when String
|
442
|
+
@shadow_library = Library.new(lib)
|
443
|
+
when nil
|
444
|
+
n = name.dup
|
445
|
+
n.gsub!(/_/, '__')
|
446
|
+
n.gsub!(/::/, '_') # almost reversible
|
447
|
+
@shadow_library = Library.new(n)
|
448
|
+
else
|
449
|
+
raise ArgumentError,
|
450
|
+
"#{lib} is not a CShadow::Library, String, or Class. " +
|
451
|
+
"Its class is #{lib.class}"
|
452
|
+
end
|
453
|
+
end
|
454
|
+
@shadow_library
|
455
|
+
else
|
456
|
+
bc.shadow_library lib
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
# Set or return the shadow library file.
|
461
|
+
def shadow_library_file file = nil
|
462
|
+
if defined? @shadow_library_file
|
463
|
+
if file
|
464
|
+
raise RuntimeError,
|
465
|
+
"Class #{self} is already associated" +
|
466
|
+
" with file #{@shadow_library_file}."
|
467
|
+
end
|
468
|
+
@shadow_library_file
|
469
|
+
elsif file
|
470
|
+
case file
|
471
|
+
when CGenerator::CFile
|
472
|
+
file_name = file.name
|
473
|
+
when String
|
474
|
+
file_name = file
|
475
|
+
else
|
476
|
+
raise ArgumentError, "#{file} is not a String or CFile."
|
477
|
+
end
|
478
|
+
file_name = file_name.sub(/\.[ch]$/, "")
|
479
|
+
@shadow_library_file = shadow_library.add_file file_name
|
480
|
+
else
|
481
|
+
if superclass.respond_to? :shadow_library_file
|
482
|
+
superclass.shadow_library_file
|
483
|
+
else
|
484
|
+
[shadow_library.include_file, shadow_library.source_file]
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# Return the main C include file for the library.
|
490
|
+
def shadow_library_include_file
|
491
|
+
shadow_library_file[0]
|
492
|
+
end
|
493
|
+
|
494
|
+
# Return the main C source file for the library.
|
495
|
+
def shadow_library_source_file
|
496
|
+
shadow_library_file[1]
|
497
|
+
end
|
498
|
+
|
499
|
+
#------------------------
|
500
|
+
# :section: Defining methods
|
501
|
+
# CGenerator provides a general interface to the Ruby-C api. However, for
|
502
|
+
# simplicity, CShadow defines three methods in the client class for defining
|
503
|
+
# methods and class methods:
|
504
|
+
#------------------------
|
505
|
+
|
506
|
+
# The block is evaluated in a context that allows commands for listing
|
507
|
+
# arguments, declarations, C body code, etc. See CGenerator for details.
|
508
|
+
# See examples in examples/matrix.rb and examples/complex.rb. The +subclass+
|
509
|
+
# argument is optional and allows the template to belong to a subclass of
|
510
|
+
# the function template it would normally belong to.
|
511
|
+
#
|
512
|
+
# In the case of #define_c_method, a pointer to the object's shadow struct
|
513
|
+
# is available in the C variable +shadow+.
|
514
|
+
#
|
515
|
+
def define_c_method name, subclass = CGenerator::Method, &block
|
516
|
+
sf = shadow_library_source_file
|
517
|
+
m = sf.define_c_method self, name, subclass
|
518
|
+
m.scope :extern
|
519
|
+
m.declare :shadow => "#{shadow_struct.name} *shadow"
|
520
|
+
m.setup :shadow =>
|
521
|
+
"Data_Get_Struct(self, #{shadow_struct.name}, shadow)"
|
522
|
+
m.instance_eval(&block) if block
|
523
|
+
m
|
524
|
+
end
|
525
|
+
|
526
|
+
# Define a class method for this class.
|
527
|
+
def define_c_class_method name,
|
528
|
+
subclass = CGenerator::SingletonMethod, &block
|
529
|
+
sf = shadow_library_source_file
|
530
|
+
m = sf.define_c_singleton_method self, name, subclass
|
531
|
+
m.scope :extern
|
532
|
+
m.instance_eval(&block) if block
|
533
|
+
m
|
534
|
+
end
|
535
|
+
|
536
|
+
# Define a global function in the library of this class.
|
537
|
+
def define_c_function name, subclass = CGenerator::Function, &block
|
538
|
+
sf = shadow_library_source_file
|
539
|
+
m = sf.define_c_function name, subclass
|
540
|
+
m.scope :extern
|
541
|
+
m.instance_eval(&block) if block
|
542
|
+
m
|
543
|
+
end
|
544
|
+
|
545
|
+
#== Internal methods ==#
|
546
|
+
|
547
|
+
# Construct the name used for the shadow struct.
|
548
|
+
def shadow_struct_name
|
549
|
+
name.gsub(/_/, '__').gsub(/::/, '_o_') + CShadow::SHADOW_SUFFIX
|
550
|
+
# To preserve differences.
|
551
|
+
end
|
552
|
+
|
553
|
+
# Return the object for managing the shadow struct.
|
554
|
+
def shadow_struct
|
555
|
+
unless defined?(@shadow_struct) and @shadow_struct
|
556
|
+
sf = shadow_library_source_file
|
557
|
+
ssn = shadow_struct_name
|
558
|
+
@shadow_struct = sf.declare_extern_struct(ssn)
|
559
|
+
if self == base_class
|
560
|
+
@shadow_struct.declare :self => "VALUE self"
|
561
|
+
else
|
562
|
+
sss = superclass.shadow_struct
|
563
|
+
shadow_struct.inherit\
|
564
|
+
sss.inherit!,
|
565
|
+
"/* #{superclass.shadow_struct.name} members */",
|
566
|
+
sss.declare!, " "
|
567
|
+
|
568
|
+
unless superclass.shadow_library_source_file ==
|
569
|
+
shadow_library_source_file
|
570
|
+
shadow_library_include_file.include(
|
571
|
+
superclass.shadow_library_include_file)
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
@shadow_struct
|
576
|
+
end
|
577
|
+
|
578
|
+
# Return the object for managing the +new+ method of the class.
|
579
|
+
def new_method
|
580
|
+
unless defined?(@new_method) and @new_method
|
581
|
+
sf = shadow_library_source_file
|
582
|
+
ssn = shadow_struct.name
|
583
|
+
@new_method = sf.define_c_singleton_method self,
|
584
|
+
:new, AttrClassMethod
|
585
|
+
@new_method.instance_eval {
|
586
|
+
scope :extern
|
587
|
+
c_array_args
|
588
|
+
declare :object => "VALUE object"
|
589
|
+
declare :shadow => "#{ssn} *shadow"
|
590
|
+
setup :shadow_struct => %{
|
591
|
+
object = Data_Make_Struct(self,
|
592
|
+
#{ssn},
|
593
|
+
mark_#{ssn},
|
594
|
+
free_#{ssn},
|
595
|
+
shadow);
|
596
|
+
shadow->self = object;
|
597
|
+
}.tabto(0)
|
598
|
+
body attr_code!
|
599
|
+
body %{
|
600
|
+
rb_obj_call_init(object, argc, argv);
|
601
|
+
}
|
602
|
+
returns "object"
|
603
|
+
}
|
604
|
+
if superclass.respond_to? :shadow_struct
|
605
|
+
@new_method.attr_code superclass.new_method.attr_code!
|
606
|
+
end
|
607
|
+
end
|
608
|
+
@new_method
|
609
|
+
end
|
610
|
+
|
611
|
+
# Return the object for managing the mark function of the class.
|
612
|
+
def mark_function
|
613
|
+
unless defined?(@mark_function) and @mark_function
|
614
|
+
sf = shadow_library_source_file
|
615
|
+
ssn = shadow_struct.name
|
616
|
+
@mark_function = sf.define_c_function("mark_#{ssn}", MarkFunction)
|
617
|
+
@mark_function.instance_eval {
|
618
|
+
scope :static
|
619
|
+
arguments "#{ssn} *shadow"
|
620
|
+
return_type "void"
|
621
|
+
}
|
622
|
+
if superclass.respond_to? :shadow_struct
|
623
|
+
@mark_function.mark superclass.mark_function.mark!
|
624
|
+
end
|
625
|
+
end
|
626
|
+
@mark_function
|
627
|
+
end
|
628
|
+
|
629
|
+
# Return the object for managing the free function of the class.
|
630
|
+
def free_function
|
631
|
+
unless defined?(@free_function) and @free_function
|
632
|
+
sf = shadow_library_source_file
|
633
|
+
ssn = shadow_struct.name
|
634
|
+
@free_function = sf.define_c_function("free_#{ssn}", FreeFunction)
|
635
|
+
@free_function.instance_eval {
|
636
|
+
scope :static
|
637
|
+
arguments "#{ssn} *shadow"
|
638
|
+
return_type "void"
|
639
|
+
}
|
640
|
+
if superclass.respond_to? :shadow_struct
|
641
|
+
@free_function.free superclass.free_function.free!
|
642
|
+
end
|
643
|
+
end
|
644
|
+
@free_function
|
645
|
+
end
|
646
|
+
|
647
|
+
# Return the object for managing the +_dump_data+ method of the class.
|
648
|
+
# See ruby's marshal.c.
|
649
|
+
def _dump_data_method
|
650
|
+
return nil unless persistent?
|
651
|
+
unless defined?(@_dump_data_method) and @_dump_data_method
|
652
|
+
@_dump_data_method = define_c_method(:_dump_data, AttrMethod) {
|
653
|
+
declare :result => "VALUE result"
|
654
|
+
setup :result => "result = rb_ary_new()"
|
655
|
+
body pre_code!, attr_code!, post_code!
|
656
|
+
returns "result"
|
657
|
+
}
|
658
|
+
if superclass.respond_to? :shadow_struct
|
659
|
+
@_dump_data_method.attr_code superclass._dump_data_method.body!
|
660
|
+
end
|
661
|
+
end
|
662
|
+
@_dump_data_method
|
663
|
+
end
|
664
|
+
|
665
|
+
# Return the object for managing the +_load_data+ method of the class.
|
666
|
+
# See ruby's marshal.c.
|
667
|
+
def _load_data_method
|
668
|
+
return nil unless persistent?
|
669
|
+
unless defined?(@_load_data_method) and @_load_data_method
|
670
|
+
@_load_data_method = define_c_method(:_load_data, AttrMethod) {
|
671
|
+
arguments :from_array
|
672
|
+
declare :tmp => "VALUE tmp"
|
673
|
+
body pre_code!, attr_code!, post_code!
|
674
|
+
returns "from_array" ## needed?
|
675
|
+
}
|
676
|
+
if superclass.respond_to? :shadow_struct
|
677
|
+
@_load_data_method.attr_code superclass._load_data_method.body!
|
678
|
+
end
|
679
|
+
end
|
680
|
+
@_load_data_method
|
681
|
+
end
|
682
|
+
|
683
|
+
# Return the object for managing the +alloc+ method of the class.
|
684
|
+
def _alloc_method
|
685
|
+
## same as new_method, but no initialize -- factor this
|
686
|
+
## can we use define_c_class_method?
|
687
|
+
return nil unless persistent?
|
688
|
+
unless defined?(@_alloc_method) and @_alloc_method
|
689
|
+
sf = shadow_library_source_file
|
690
|
+
ssn = shadow_struct.name
|
691
|
+
@_alloc_method = sf.define_alloc_func(self)
|
692
|
+
@_alloc_method.instance_eval {
|
693
|
+
scope :extern
|
694
|
+
arguments 'VALUE klass'
|
695
|
+
return_type 'VALUE'
|
696
|
+
klass_c_name = "klass"
|
697
|
+
declare :object => "VALUE object"
|
698
|
+
declare :shadow => "#{ssn} *shadow"
|
699
|
+
body %{
|
700
|
+
object = Data_Make_Struct(#{klass_c_name},
|
701
|
+
#{ssn},
|
702
|
+
mark_#{ssn},
|
703
|
+
free_#{ssn},
|
704
|
+
shadow);
|
705
|
+
shadow->self = object;
|
706
|
+
}
|
707
|
+
returns "object"
|
708
|
+
}
|
709
|
+
end
|
710
|
+
@_alloc_method
|
711
|
+
end
|
712
|
+
|
713
|
+
private
|
714
|
+
|
715
|
+
# :stopdoc:
|
716
|
+
def check_overwrite_shadow_attrs(*symbols)
|
717
|
+
for attr in shadow_attrs
|
718
|
+
for sym in symbols
|
719
|
+
if sym == attr.var
|
720
|
+
raise NameError, "#{sym} is a shadow attr."
|
721
|
+
end
|
722
|
+
end
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
def attr_accessor(*args)
|
727
|
+
check_overwrite_shadow_attrs(*args)
|
728
|
+
super
|
729
|
+
end
|
730
|
+
|
731
|
+
def attr_reader(*args)
|
732
|
+
check_overwrite_shadow_attrs(*args)
|
733
|
+
super
|
734
|
+
end
|
735
|
+
|
736
|
+
def attr_writer(*args)
|
737
|
+
check_overwrite_shadow_attrs(*args)
|
738
|
+
super
|
739
|
+
end
|
740
|
+
# :startdoc:
|
741
|
+
|
742
|
+
# Same as #shadow_attr with the +:reader+ and +:writer+ options.
|
743
|
+
def shadow_attr_accessor(*args)
|
744
|
+
shadow_attr :reader, :writer, *args
|
745
|
+
end
|
746
|
+
|
747
|
+
# Same as #shadow_attr with the +:reader+ option.
|
748
|
+
def shadow_attr_reader(*args)
|
749
|
+
shadow_attr :reader, *args
|
750
|
+
end
|
751
|
+
|
752
|
+
# Same as #shadow_attr with the +:writer+ option.
|
753
|
+
def shadow_attr_writer(*args)
|
754
|
+
shadow_attr :writer, *args
|
755
|
+
end
|
756
|
+
|
757
|
+
# call-seq:
|
758
|
+
# shadow_attr [options,] :var => decl, ...
|
759
|
+
#
|
760
|
+
# Adds the specified declarations to the shadow struct of the current class
|
761
|
+
# without defining any accessors. The data can be accessed only in C code.
|
762
|
+
#
|
763
|
+
# Each <tt>:var => decl</tt> pair generates one C struct member (<tt>:x =>
|
764
|
+
# "int x,y"</tt> will generate an exception).
|
765
|
+
#
|
766
|
+
# The same symbol :var cannot be used in both a +shadow_attr_*+ definition
|
767
|
+
# and an ordinary +attr_*+ definition. (You can always get around this by
|
768
|
+
# manually defining accessor methods.)
|
769
|
+
#
|
770
|
+
# The available options are:
|
771
|
+
#
|
772
|
+
# +:persistent+, +:nonpersistent+::
|
773
|
+
# Serialize or do not serialize this attribute when dumping with Marshal,
|
774
|
+
# YAML, etc. When loading, it is initialized using the attribute's #init
|
775
|
+
# code (which, typically, sets it to zero or +nil+). Default is
|
776
|
+
# +:persistent+.
|
777
|
+
#
|
778
|
+
# +:reader+, +:writer+::
|
779
|
+
# Creates a reader or writer method, just like #shadow_attr_reader and
|
780
|
+
# #shadow_attr_writer. (This is mostly for internal use.)
|
781
|
+
#
|
782
|
+
# Returns list of handles to the attr funcs: [ [r0,w0], [r1,w1], ... ]
|
783
|
+
#
|
784
|
+
# Typically, #shadow_attr_accessor and so on are called instead.
|
785
|
+
#
|
786
|
+
def shadow_attr(*args)
|
787
|
+
attr_persists = true
|
788
|
+
for arg in args
|
789
|
+
case arg
|
790
|
+
when Hash; var_map = arg.sort_by {|var, decl| var.to_s}
|
791
|
+
when :reader; reader = true
|
792
|
+
when :writer; writer = true
|
793
|
+
when :nonpersistent; attr_persists = false
|
794
|
+
when :persistent; attr_persists = true
|
795
|
+
else
|
796
|
+
raise SyntaxError,
|
797
|
+
"Unrecognized shadow_attr argument: #{arg.inspect}"
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
source_file = shadow_library_source_file
|
802
|
+
ssn = shadow_struct.name
|
803
|
+
@shadow_attrs ||= []
|
804
|
+
|
805
|
+
var_map.map do |var, decl|
|
806
|
+
var = var.intern if var.is_a? String
|
807
|
+
|
808
|
+
if instance_methods(false).include?(var.to_s) or
|
809
|
+
instance_methods(false).include?(var.to_s + '=')
|
810
|
+
raise NameError, "#{var} already has a Ruby attribute."
|
811
|
+
end
|
812
|
+
|
813
|
+
matches = AttributeTypes.collect { |t|
|
814
|
+
[t, t.match(decl)]
|
815
|
+
}.select {|t, match| match}
|
816
|
+
|
817
|
+
if matches.size > 1
|
818
|
+
raise StandardError, %{
|
819
|
+
No unique AttributeType for '#{decl}':
|
820
|
+
each of #{matches.map{|t|t[0]}.join ", "} match.
|
821
|
+
}.tabto(0)
|
822
|
+
end
|
823
|
+
|
824
|
+
if matches.size < 1
|
825
|
+
raise StandardError, "No Attribute type matches '#{decl}'."
|
826
|
+
end
|
827
|
+
|
828
|
+
attr_type, match = matches[0]
|
829
|
+
attr = attr_type.new self, var, match, attr_persists
|
830
|
+
each_shadow_attr { |a|
|
831
|
+
if a.var == attr.var or a.cvar == attr.cvar
|
832
|
+
unless a.var == attr.var and a.cdecl == attr.cdecl
|
833
|
+
raise NameError, "Attribute #{a.inspect} already exists."
|
834
|
+
end
|
835
|
+
end
|
836
|
+
}
|
837
|
+
@shadow_attrs << attr
|
838
|
+
|
839
|
+
shadow_struct.declare attr.cvar => attr.cdecl
|
840
|
+
|
841
|
+
new_method.attr_code attr.init if attr.init
|
842
|
+
|
843
|
+
mark_function.mark attr.mark
|
844
|
+
free_function.free attr.free
|
845
|
+
|
846
|
+
if persistent?
|
847
|
+
if attr_persists
|
848
|
+
_dump_data_method.attr_code attr.dump
|
849
|
+
_load_data_method.attr_code attr.load
|
850
|
+
else
|
851
|
+
_load_data_method.attr_code attr.init if attr.init
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
if reader
|
856
|
+
unless attr.reader
|
857
|
+
raise ScriptError, "Can't have a reader method for #{attr}."
|
858
|
+
end
|
859
|
+
r_meth = source_file.define_c_method(self, var)
|
860
|
+
r_meth.instance_eval {
|
861
|
+
scope :extern
|
862
|
+
declare :shadow => "#{ssn} *shadow"
|
863
|
+
declare :result => "VALUE result"
|
864
|
+
body "Data_Get_Struct(self, #{ssn}, shadow)", attr.reader
|
865
|
+
returns "result"
|
866
|
+
}
|
867
|
+
end
|
868
|
+
|
869
|
+
if writer
|
870
|
+
unless attr.writer
|
871
|
+
raise ScriptError, "Can't have a writer method for #{attr}."
|
872
|
+
end
|
873
|
+
w_meth = source_file.define_c_method(self, "#{var}=")
|
874
|
+
w_meth.instance_eval {
|
875
|
+
scope :extern
|
876
|
+
c_array_args {
|
877
|
+
required :arg
|
878
|
+
typecheck :arg => attr.check
|
879
|
+
}
|
880
|
+
declare :shadow => "#{ssn} *shadow"
|
881
|
+
body "Data_Get_Struct(self, #{ssn}, shadow)", attr.writer
|
882
|
+
returns "arg"
|
883
|
+
}
|
884
|
+
end
|
885
|
+
|
886
|
+
[r_meth, w_meth]
|
887
|
+
end
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
def self.append_features base_class # :nodoc:
|
892
|
+
unless base_class.is_a? Class
|
893
|
+
raise TypeError, "CShadow can be included only in a Class"
|
894
|
+
end
|
895
|
+
|
896
|
+
unless base_class.ancestors.include? self
|
897
|
+
|
898
|
+
base_class.class_eval {@base_class = self; @persistent = true}
|
899
|
+
base_class.extend CShadowClassMethods
|
900
|
+
|
901
|
+
class << base_class
|
902
|
+
## why can't these be in CShadowClassMethods?
|
903
|
+
|
904
|
+
alias really_protected protected
|
905
|
+
def protected(*args)
|
906
|
+
(@to_be_protected ||= []).concat args
|
907
|
+
end
|
908
|
+
|
909
|
+
alias really_private private
|
910
|
+
def private(*args)
|
911
|
+
(@to_be_private ||= []).concat args
|
912
|
+
end
|
913
|
+
|
914
|
+
def protect_shadow_attrs
|
915
|
+
if defined?(@to_be_protected) and @to_be_protected
|
916
|
+
really_protected(*@to_be_protected)
|
917
|
+
end
|
918
|
+
if defined?(@to_be_private) and @to_be_private
|
919
|
+
really_private(*@to_be_private)
|
920
|
+
end
|
921
|
+
## should undo the aliasing
|
922
|
+
end
|
923
|
+
public :protect_shadow_attrs
|
924
|
+
|
925
|
+
end
|
926
|
+
|
927
|
+
end
|
928
|
+
|
929
|
+
super
|
930
|
+
end
|
931
|
+
|
932
|
+
class AttrCodeAccumulator < CGenerator::CFragment::StatementAccumulator
|
933
|
+
include CGenerator::SetAccumulator
|
934
|
+
end
|
935
|
+
|
936
|
+
class AttrMethod < CGenerator::Method
|
937
|
+
accumulator(:attr_code) {AttrCodeAccumulator}
|
938
|
+
accumulator(:pre_code) {CGenerator::CFragment::StatementAccumulator}
|
939
|
+
accumulator(:post_code) {CGenerator::CFragment::StatementAccumulator}
|
940
|
+
end
|
941
|
+
|
942
|
+
class AttrClassMethod < CGenerator::SingletonMethod
|
943
|
+
accumulator(:attr_code) {AttrCodeAccumulator}
|
944
|
+
end
|
945
|
+
|
946
|
+
class MarkFunction < CGenerator::Function
|
947
|
+
accumulator(:mark) {AttrCodeAccumulator}
|
948
|
+
|
949
|
+
def initialize(*args)
|
950
|
+
super
|
951
|
+
body "rb_gc_mark(shadow->self)", mark!
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
class FreeFunction < CGenerator::Function
|
956
|
+
accumulator(:free) {AttrCodeAccumulator}
|
957
|
+
|
958
|
+
def initialize(*args)
|
959
|
+
super
|
960
|
+
body free!, "free(shadow)" # free the struct last!
|
961
|
+
end
|
962
|
+
end
|
963
|
+
|
964
|
+
def inspect # :nodoc:
|
965
|
+
attrs = []
|
966
|
+
seen = {self => true}
|
967
|
+
each_attr_value do |attr, value|
|
968
|
+
if seen[value]
|
969
|
+
attrs << "#{attr}=#{value}"
|
970
|
+
else
|
971
|
+
attrs << "#{attr}=#{value.inspect}"
|
972
|
+
end
|
973
|
+
seen[value] = true
|
974
|
+
end
|
975
|
+
super.sub(/(?=>\z)/, " " + attrs.join(", "))
|
976
|
+
end
|
977
|
+
|
978
|
+
# Iterate over each shadow attr and instance var of +self+, yielding the attr
|
979
|
+
# name and value in this instance to the block. Differs in three ways from
|
980
|
+
# CShadow.each_shadow_attr: it is an instance method of shadow objects, it
|
981
|
+
# iterates over both shadow attrs and instance vars, and it yields both the
|
982
|
+
# name and the value. (In the case of instance vars, the name does _not_
|
983
|
+
# include the "@".)
|
984
|
+
def each_attr_value # :yields: attr_name, attr_value
|
985
|
+
values = _dump_data
|
986
|
+
self.class.shadow_attrs.each_with_index do |attr, i|
|
987
|
+
yield attr.var.to_s, values[i]
|
988
|
+
end
|
989
|
+
instance_variables.each do |ivar|
|
990
|
+
yield ivar[1..-1], instance_variable_get(ivar)
|
991
|
+
end
|
992
|
+
end
|
993
|
+
|
994
|
+
# Like #each_attr_value, but limited to attr declared as +persistent+.
|
995
|
+
def each_persistent_attr_value # :yields: attr_name, attr_value
|
996
|
+
values = _dump_data
|
997
|
+
psa = self.class.shadow_attrs.select {|attr| attr.persists}
|
998
|
+
psa.each_with_index do |attr, i|
|
999
|
+
yield attr.var.to_s, values[i]
|
1000
|
+
end
|
1001
|
+
instance_variables.each do |ivar|
|
1002
|
+
yield ivar[1..-1], instance_variable_get(ivar)
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
# Define YAML methods for CShadow classes. Can be called before
|
1007
|
+
# or after #commit. Loads the yaml library.
|
1008
|
+
def self.allow_yaml(your_tag = "path.berkeley.edu,2006")
|
1009
|
+
return if defined?(@cshadow_allow_yaml) and @cshadow_allow_yaml
|
1010
|
+
@cshadow_allow_yaml = true
|
1011
|
+
|
1012
|
+
require 'yaml'
|
1013
|
+
|
1014
|
+
yaml_as "tag:#{your_tag}:cshadow"
|
1015
|
+
|
1016
|
+
def self.yaml_new( klass, tag, val )
|
1017
|
+
subtype, subclass = YAML.read_type_class(tag, Object)
|
1018
|
+
subclass.new_from_hash(val)
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
module_eval do
|
1022
|
+
def add_yaml_map_contents(map)
|
1023
|
+
each_persistent_attr_value do |attr, value|
|
1024
|
+
map.add(attr, value)
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
def to_yaml( opts = {} )
|
1029
|
+
YAML.quick_emit(object_id, opts) do |out|
|
1030
|
+
out.map( taguri, to_yaml_style ) do |map|
|
1031
|
+
add_yaml_map_contents(map)
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
end
|