utilrb 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +12 -4
- data/ext/utilrb/extconf.rb +5 -5
- data/ext/utilrb/proc.c +3 -3
- data/ext/utilrb/utilrb.cc +0 -2
- data/ext/utilrb/weakref.cc +3 -3
- data/lib/utilrb/common.rb +1 -1
- data/lib/utilrb/event_loop.rb +969 -0
- data/lib/utilrb/logger/silent.rb +10 -0
- data/lib/utilrb/models/inherited_enumerable.rb +341 -0
- data/lib/utilrb/models/registration.rb +115 -0
- data/lib/utilrb/pathname/find_matching_parent.rb +20 -0
- data/lib/utilrb/pathname.rb +3 -0
- data/lib/utilrb/qt/mime_data/mime_data.rb +18 -0
- data/lib/utilrb/qt/variant/from_ruby.rb +30 -0
- data/lib/utilrb/thread_pool.rb +592 -0
- data/test/test_array.rb +1 -1
- data/test/test_dir.rb +1 -1
- data/test/test_enumerable.rb +1 -1
- data/test/test_event_loop.rb +1 -1
- data/test/test_exception.rb +1 -1
- data/test/test_gc.rb +1 -1
- data/test/test_hash.rb +1 -1
- data/test/test_kernel.rb +3 -13
- data/test/test_misc.rb +1 -1
- data/test/test_models.rb +1 -1
- data/test/test_module.rb +1 -1
- data/test/test_object.rb +1 -1
- data/test/test_objectstats.rb +1 -1
- data/test/test_proc.rb +1 -1
- data/test/test_set.rb +1 -1
- data/test/test_thread_pool.rb +1 -1
- data/test/test_time.rb +1 -1
- data/test/test_unbound_method.rb +1 -1
- metadata +30 -25
- data/ext/utilrb/ruby_internals-1.8.h +0 -72
- data/ext/utilrb/ruby_internals-1.9.h +0 -71
- data/ext/utilrb/swap.cc +0 -31
- data/lib/utilrb/kernel/swap.rb +0 -2
@@ -0,0 +1,341 @@
|
|
1
|
+
require 'utilrb/object/attribute'
|
2
|
+
require 'utilrb/object/singleton_class'
|
3
|
+
require 'utilrb/enumerable/uniq'
|
4
|
+
require 'utilrb/module/include'
|
5
|
+
|
6
|
+
module Utilrb
|
7
|
+
module Models
|
8
|
+
# Helper method for inherited_enumerable
|
9
|
+
#
|
10
|
+
# It is called in the context of the singleton class of the module/class on
|
11
|
+
# which inherited_enumerable is called
|
12
|
+
def define_inherited_enumerable(name, attribute_name = name, options = Hash.new, &init) # :nodoc:
|
13
|
+
# Set up the attribute accessor
|
14
|
+
attribute(attribute_name, &init)
|
15
|
+
class_eval { private "#{attribute_name}=" }
|
16
|
+
|
17
|
+
promote = method_defined?("promote_#{name}")
|
18
|
+
options[:enum_with] ||= :each
|
19
|
+
|
20
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
21
|
+
def all_#{name}; each_#{name}.to_a end
|
22
|
+
def self_#{name}; @#{attribute_name} end
|
23
|
+
EOF
|
24
|
+
|
25
|
+
if options[:map]
|
26
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
27
|
+
def find_#{name}(key)
|
28
|
+
raise ArgumentError, "nil cannot be used as a key in find_#{name}" if !key
|
29
|
+
each_#{name}(key, true) do |value|
|
30
|
+
return value
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
def has_#{name}?(key)
|
35
|
+
ancestors = self.ancestors
|
36
|
+
if ancestors.first != self
|
37
|
+
ancestors.unshift self
|
38
|
+
end
|
39
|
+
for klass in ancestors
|
40
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
41
|
+
return true if klass.#{attribute_name}.has_key?(key)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
false
|
45
|
+
end
|
46
|
+
EOF
|
47
|
+
end
|
48
|
+
|
49
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
50
|
+
def clear_#{attribute_name}
|
51
|
+
#{attribute_name}.clear
|
52
|
+
for klass in ancestors
|
53
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
54
|
+
klass.#{attribute_name}.clear
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
EOF
|
59
|
+
|
60
|
+
if !promote
|
61
|
+
if options[:map]
|
62
|
+
define_inherited_enumerable_map_without_promotion(name, attribute_name, options)
|
63
|
+
else
|
64
|
+
define_inherited_enumerable_nomap_without_promotion(name, attribute_name, options)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
if options[:map]
|
68
|
+
define_inherited_enumerable_map_with_promotion(name, attribute_name, options)
|
69
|
+
else
|
70
|
+
define_inherited_enumerable_nomap_with_promotion(name, attribute_name, options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def define_inherited_enumerable_map_without_promotion(name, attribute_name, options)
|
76
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
77
|
+
def each_#{name}(key = nil, uniq = true)
|
78
|
+
if !block_given?
|
79
|
+
return enum_for(:each_#{name}, key, uniq)
|
80
|
+
end
|
81
|
+
|
82
|
+
ancestors = self.ancestors
|
83
|
+
if ancestors.first != self
|
84
|
+
ancestors.unshift self
|
85
|
+
end
|
86
|
+
if key
|
87
|
+
for klass in ancestors
|
88
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
89
|
+
if klass.#{attribute_name}.has_key?(key)
|
90
|
+
yield(klass.#{attribute_name}[key])
|
91
|
+
return self if uniq
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
elsif !uniq
|
96
|
+
for klass in ancestors
|
97
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
98
|
+
klass.#{attribute_name}.#{options[:enum_with]} do |el|
|
99
|
+
yield(el)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
else
|
104
|
+
seen = Set.new
|
105
|
+
for klass in ancestors
|
106
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
107
|
+
klass.#{attribute_name}.#{options[:enum_with]} do |el|
|
108
|
+
unless seen.include?(el.first)
|
109
|
+
seen << el.first
|
110
|
+
yield(el)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
self
|
118
|
+
end
|
119
|
+
EOF
|
120
|
+
end
|
121
|
+
|
122
|
+
def define_inherited_enumerable_nomap_without_promotion(name, attribute_name, options)
|
123
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
124
|
+
def each_#{name}
|
125
|
+
if !block_given?
|
126
|
+
return enum_for(:each_#{name})
|
127
|
+
end
|
128
|
+
|
129
|
+
ancestors = self.ancestors
|
130
|
+
if ancestors.first != self
|
131
|
+
ancestors.unshift self
|
132
|
+
end
|
133
|
+
for klass in ancestors
|
134
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
135
|
+
klass.#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
self
|
139
|
+
end
|
140
|
+
EOF
|
141
|
+
end
|
142
|
+
|
143
|
+
def define_inherited_enumerable_map_with_promotion(name, attribute_name, options)
|
144
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
145
|
+
def each_#{name}(key = nil, uniq = true)
|
146
|
+
if !block_given?
|
147
|
+
return enum_for(:each_#{name}, key, uniq)
|
148
|
+
end
|
149
|
+
|
150
|
+
ancestors = self.ancestors
|
151
|
+
if ancestors.first != self
|
152
|
+
ancestors.unshift self
|
153
|
+
end
|
154
|
+
if key
|
155
|
+
promotions = []
|
156
|
+
for klass in ancestors
|
157
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
158
|
+
if klass.#{attribute_name}.has_key?(key)
|
159
|
+
value = klass.#{attribute_name}[key]
|
160
|
+
for p in promotions
|
161
|
+
value = p.promote_#{name}(key, value)
|
162
|
+
end
|
163
|
+
yield(value)
|
164
|
+
return self if uniq
|
165
|
+
end
|
166
|
+
end
|
167
|
+
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
168
|
+
end
|
169
|
+
elsif !uniq
|
170
|
+
promotions = []
|
171
|
+
for klass in ancestors
|
172
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
173
|
+
klass.#{attribute_name}.#{options[:enum_with]} do |key, value|
|
174
|
+
for p in promotions
|
175
|
+
value = p.promote_#{name}(key, value)
|
176
|
+
end
|
177
|
+
yield(key, value)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
181
|
+
end
|
182
|
+
else
|
183
|
+
seen = Set.new
|
184
|
+
promotions = []
|
185
|
+
for klass in ancestors
|
186
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
187
|
+
klass.#{attribute_name}.#{options[:enum_with]} do |key, value|
|
188
|
+
unless seen.include?(key)
|
189
|
+
for p in promotions
|
190
|
+
value = p.promote_#{name}(key, value)
|
191
|
+
end
|
192
|
+
seen << key
|
193
|
+
yield(key, value)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
self
|
201
|
+
end
|
202
|
+
EOF
|
203
|
+
end
|
204
|
+
|
205
|
+
def define_inherited_enumerable_nomap_with_promotion(name, attribute_name, options)
|
206
|
+
class_eval <<-EOF, __FILE__, __LINE__+1
|
207
|
+
def each_#{name}
|
208
|
+
if !block_given?
|
209
|
+
return enum_for(:each_#{name})
|
210
|
+
end
|
211
|
+
|
212
|
+
ancestors = self.ancestors
|
213
|
+
if ancestors.first != self
|
214
|
+
ancestors.unshift self
|
215
|
+
end
|
216
|
+
promotions = []
|
217
|
+
for klass in ancestors
|
218
|
+
if klass.instance_variable_defined?(:@#{attribute_name})
|
219
|
+
klass.#{attribute_name}.#{options[:enum_with]} do |value|
|
220
|
+
for p in promotions
|
221
|
+
value = p.promote_#{name}(value)
|
222
|
+
end
|
223
|
+
yield(value)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
|
227
|
+
end
|
228
|
+
self
|
229
|
+
end
|
230
|
+
EOF
|
231
|
+
end
|
232
|
+
|
233
|
+
# Defines an attribute as being enumerable in the class instance and in the
|
234
|
+
# whole class inheritance hierarchy. More specifically, it defines a
|
235
|
+
# <tt>each_#{name}(&iterator)</tt> instance method and a <tt>each_#{name}(&iterator)</tt>
|
236
|
+
# class method which iterates (in order) on
|
237
|
+
# - the instance #{name} attribute
|
238
|
+
# - the singleton class #{name} attribute
|
239
|
+
# - the class #{name} attribute
|
240
|
+
# - the superclass #{name} attribute
|
241
|
+
# - the superclass' superclass #{name} attribute
|
242
|
+
# ...
|
243
|
+
#
|
244
|
+
# This method can be used on modules, in which case the module is used as if
|
245
|
+
# it was part of the inheritance hierarchy.
|
246
|
+
#
|
247
|
+
# The +name+ option defines the enumeration method name (+value+ will
|
248
|
+
# define a +each_value+ method). +attribute_name+ defines the attribute
|
249
|
+
# name. +init+ is a block called to initialize the attribute.
|
250
|
+
# Valid options in +options+ are:
|
251
|
+
# map::
|
252
|
+
# If true, the attribute should respond to +[]+. In that case, the
|
253
|
+
# enumeration method is each_value(key = nil, uniq = false) If +key+ is
|
254
|
+
# given, we iterate on the values given by <tt>attribute[key]</tt>. If
|
255
|
+
# +uniq+ is true, the enumeration will yield at most one value for each
|
256
|
+
# +key+ found (so, if both +key+ and +uniq+ are given, the enumeration
|
257
|
+
# yields at most one value). See the examples below
|
258
|
+
# enum_with:: the enumeration method of the enumerable, if it is not +each+
|
259
|
+
#
|
260
|
+
# === Example
|
261
|
+
# Let's define some classes and look at the ancestor chain
|
262
|
+
#
|
263
|
+
# class A; end
|
264
|
+
# module M; end
|
265
|
+
# class B < A; include M end
|
266
|
+
# A.ancestors # => [A, Object, Kernel]
|
267
|
+
# B.ancestors # => [B, M, A, Object, Kernel]
|
268
|
+
#
|
269
|
+
# ==== Attributes for which 'map' is not set
|
270
|
+
#
|
271
|
+
# class A
|
272
|
+
# inherited_enumerable("value", "values") do
|
273
|
+
# Array.new
|
274
|
+
# end
|
275
|
+
# end
|
276
|
+
# module M
|
277
|
+
# inherited_enumerable("mod") do
|
278
|
+
# Array.new
|
279
|
+
# end
|
280
|
+
# end
|
281
|
+
#
|
282
|
+
# A.values << 1 # => [1]
|
283
|
+
# B.values << 2 # => [2]
|
284
|
+
# M.mod << 1 # => [1]
|
285
|
+
# b = B.new
|
286
|
+
# class << b
|
287
|
+
# self.values << 3 # => [3]
|
288
|
+
# self.mod << 4 # => [4]
|
289
|
+
# end
|
290
|
+
# M.mod << 2 # => [1, 2]
|
291
|
+
#
|
292
|
+
# A.enum_for(:each_value).to_a # => [1]
|
293
|
+
# B.enum_for(:each_value).to_a # => [2, 1]
|
294
|
+
# b.singleton_class.enum_for(:each_value).to_a # => [3, 2, 1]
|
295
|
+
# b.singleton_class.enum_for(:each_mod).to_a # => [4, 1, 2]
|
296
|
+
#
|
297
|
+
# ==== Attributes for which 'map' is set
|
298
|
+
#
|
299
|
+
# class A
|
300
|
+
# inherited_enumerable("mapped", "map", :map => true) do
|
301
|
+
# Hash.new { |h, k| h[k] = Array.new }
|
302
|
+
# end
|
303
|
+
# end
|
304
|
+
#
|
305
|
+
# A.map['name'] = 'A' # => "A"
|
306
|
+
# A.map['universe'] = 42
|
307
|
+
# B.map['name'] = 'B' # => "B"
|
308
|
+
# B.map['half_of_it'] = 21
|
309
|
+
#
|
310
|
+
# Let's see what happens if we don't specify the key option.
|
311
|
+
# A.enum_for(:each_mapped).to_a # => [["name", "A"], ["universe", 42]]
|
312
|
+
# If the +uniq+ option is set (the default), we see only B's value for 'name'
|
313
|
+
# B.enum_for(:each_mapped).to_a # => [["half_of_it", 21], ["name", "B"], ["universe", 42]]
|
314
|
+
# If the +uniq+ option is not set, we see both values for 'name'. Note that
|
315
|
+
# since 'map' is a Hash, the order of keys in one class is not guaranteed.
|
316
|
+
# Nonetheless, we have the guarantee that values from B appear before
|
317
|
+
# those from A
|
318
|
+
# B.enum_for(:each_mapped, nil, false).to_a # => [["half_of_it", 21], ["name", "B"], ["name", "A"], ["universe", 42]]
|
319
|
+
#
|
320
|
+
#
|
321
|
+
# Now, let's see how 'key' behaves
|
322
|
+
# A.enum_for(:each_mapped, 'name').to_a # => ["A"]
|
323
|
+
# B.enum_for(:each_mapped, 'name').to_a # => ["B"]
|
324
|
+
# B.enum_for(:each_mapped, 'name', false).to_a # => ["B", "A"]
|
325
|
+
#
|
326
|
+
def inherited_enumerable(name, attribute_name = name, options = Hash.new, &init)
|
327
|
+
singleton_class.class_eval { define_inherited_enumerable(name, attribute_name, options, &init) }
|
328
|
+
|
329
|
+
if is_a?(Module) && !is_a?(Class)
|
330
|
+
unless const_defined_here?(:ClassExtension)
|
331
|
+
const_set(:ClassExtension, Module.new)
|
332
|
+
end
|
333
|
+
class_extension = const_get(:ClassExtension)
|
334
|
+
class_extension.class_eval do
|
335
|
+
define_inherited_enumerable(name, attribute_name, options, &init)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'facets/kernel/call_stack'
|
2
|
+
require 'utilrb/object/attribute'
|
3
|
+
require 'utilrb/module/attr_predicate'
|
4
|
+
module Utilrb
|
5
|
+
module Models
|
6
|
+
|
7
|
+
# Handling of registration of model hierarchies
|
8
|
+
#
|
9
|
+
# It depends on the mixed-in object to provide a #supermodel method that
|
10
|
+
# returns the model that is parent of +self+
|
11
|
+
module Registration
|
12
|
+
# The place where this model got defined in the source code
|
13
|
+
# The tuple is (file,lineno,method), and can be obtained with
|
14
|
+
# facet's #call_stack
|
15
|
+
# @return [Array<(String,Integer,Symbol)>]
|
16
|
+
attr_accessor :definition_location
|
17
|
+
|
18
|
+
# Tells {#clear_submodels} whether this model should be removed from
|
19
|
+
# the model set or not. The default is false (it should be removed)
|
20
|
+
#
|
21
|
+
# @return [Boolean]
|
22
|
+
attr_predicate :permanent_model?, true
|
23
|
+
|
24
|
+
# [ValueSet] the set of models that are children of this one
|
25
|
+
attribute(:submodels) { ValueSet.new }
|
26
|
+
|
27
|
+
# Returns the model that is parent of this one
|
28
|
+
#
|
29
|
+
# The default implementation returns superclass if it is extended by
|
30
|
+
# this Registration module, and nil otherwise
|
31
|
+
def supermodel
|
32
|
+
if superclass.respond_to?(:register_submodel)
|
33
|
+
superclass
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Call to register a model that is a submodel of +self+
|
38
|
+
def register_submodel(klass)
|
39
|
+
if !klass.definition_location
|
40
|
+
klass.definition_location = call_stack
|
41
|
+
end
|
42
|
+
|
43
|
+
if klass.name && !klass.permanent_model?
|
44
|
+
begin
|
45
|
+
if constant("::#{klass.name}") == klass
|
46
|
+
klass.permanent_model = true
|
47
|
+
end
|
48
|
+
rescue NameError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
submodels << klass
|
53
|
+
if m = supermodel
|
54
|
+
m.register_submodel(klass)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Enumerates all models that are submodels of this class
|
59
|
+
def each_submodel
|
60
|
+
return enum_for(:each_submodel) if !block_given?
|
61
|
+
submodels.each do |obj|
|
62
|
+
yield(obj)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Clears all registered submodels
|
67
|
+
def clear_submodels
|
68
|
+
children = self.submodels.find_all { |m| !m.permanent_model? }
|
69
|
+
if !deregister_submodels(children)
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
# This contains the permanent submodels
|
74
|
+
#
|
75
|
+
# We can call #clear_submodels while iterating here as it is a
|
76
|
+
# constraint that all models in #submodels are permanent (and
|
77
|
+
# will therefore not be removed)
|
78
|
+
submodels.each { |m| m.clear_submodels }
|
79
|
+
# And this the non-permanent ones
|
80
|
+
children.each { |m| m.clear_submodels }
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
# Deregisters a set of submodels on this model and all its
|
85
|
+
# supermodels
|
86
|
+
#
|
87
|
+
# This is usually not called directly. Use #clear_submodels instead
|
88
|
+
#
|
89
|
+
# @param [ValueSet] set the set of submodels to remove
|
90
|
+
def deregister_submodels(set)
|
91
|
+
current_size = submodels.size
|
92
|
+
submodels.difference!(set.to_value_set)
|
93
|
+
if (submodels.size != current_size)
|
94
|
+
if m = supermodel
|
95
|
+
m.deregister_submodels(set)
|
96
|
+
end
|
97
|
+
true
|
98
|
+
else false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Registers submodels when a subclass is created (when models are
|
103
|
+
# represented as classes)
|
104
|
+
def inherited(subclass)
|
105
|
+
subclass.definition_location = call_stack
|
106
|
+
super
|
107
|
+
register_submodel(subclass)
|
108
|
+
subclass.permanent_model = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
class Pathname
|
3
|
+
# Returns the path object that is the first parent of self matching the
|
4
|
+
# given predicate
|
5
|
+
#
|
6
|
+
# @yieldparam [Pathname] path the path object that should be tested
|
7
|
+
# @yieldreturn [Boolean] true if this is the path you are looking for, and
|
8
|
+
# false otherwise
|
9
|
+
# @return [Pathname,nil] the matching path or nil if none could be found
|
10
|
+
def find_matching_parent
|
11
|
+
# Look for a bundle in the parents of Dir.pwd
|
12
|
+
curdir = self
|
13
|
+
while !curdir.root? && !yield(curdir)
|
14
|
+
curdir = curdir.parent
|
15
|
+
end
|
16
|
+
if !curdir.root?
|
17
|
+
curdir
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'set'
|
2
|
+
module Qt
|
3
|
+
class MimeData
|
4
|
+
# prevents deleting the object until it get finalized by c++
|
5
|
+
@@saved_values = Hash.new
|
6
|
+
def initialize
|
7
|
+
super
|
8
|
+
ObjectSpace.define_finalizer self, MimeData::ruby_finalizer
|
9
|
+
@@saved_values[self.object_id] ||= Set.new
|
10
|
+
@@saved_values[self.object_id] << self
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.ruby_finalizer
|
14
|
+
lambda { |id| @@saved_values.delete(id) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'set'
|
2
|
+
module Qt
|
3
|
+
class Variant
|
4
|
+
# This is a mapping from a QVariant object_id to the object it is
|
5
|
+
# supposed to hold. An entry gets removed as soon as the QVariant is
|
6
|
+
# finalized
|
7
|
+
@@saved_values = Hash.new
|
8
|
+
def self.from_ruby(obj, lifetime_object = nil)
|
9
|
+
variant = Qt::Variant.new("__##{obj.object_id}#__")
|
10
|
+
lifetime_object ||= variant
|
11
|
+
ObjectSpace.define_finalizer lifetime_object, from_ruby_finalizer
|
12
|
+
@@saved_values[lifetime_object.object_id] ||= Set.new
|
13
|
+
@@saved_values[lifetime_object.object_id] << obj
|
14
|
+
variant
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_ruby_finalizer
|
18
|
+
lambda { |variant_id| @@saved_values.delete(variant_id) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_ruby
|
22
|
+
raise "QVariant is not storing an Object ID"if (value =~ /__#(\d*)#__/) != 0
|
23
|
+
ObjectSpace._id2ref(Integer($1))
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_ruby?
|
27
|
+
(value =~ /__#(\d*)#__/) == 0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|