cgen 0.16.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/.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
|