utilrb 2.0.0 → 2.0.1
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/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
|