utilrb 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +17 -0
- data/Manifest.txt +11 -0
- data/README.txt +9 -5
- data/Rakefile +30 -50
- data/ext/extconf.rb +14 -1
- data/ext/value_set.cc +43 -1
- data/lib/utilrb/array/to_s.rb +1 -0
- data/lib/utilrb/common.rb +3 -2
- data/lib/utilrb/configsearch/configuration_finder.rb +78 -0
- data/lib/utilrb/configsearch.rb +2 -0
- data/lib/utilrb/hash/recursive_merge.rb +26 -0
- data/lib/utilrb/hash/to_s.rb +1 -0
- data/lib/utilrb/kernel/load_dsl_file.rb +187 -0
- data/lib/utilrb/kernel/options.rb +10 -1
- data/lib/utilrb/kernel/with_module.rb +53 -0
- data/lib/utilrb/logger/hierarchy.rb +21 -5
- data/lib/utilrb/logger/io.rb +25 -0
- data/lib/utilrb/marshal/load_with_missing_constants.rb +63 -0
- data/lib/utilrb/module/ancestor_p.rb +12 -18
- data/lib/utilrb/module/cached_enum.rb +15 -0
- data/lib/utilrb/module/const_defined_here_p.rb +11 -0
- data/lib/utilrb/module/define_or_reuse.rb +15 -22
- data/lib/utilrb/module/include.rb +12 -0
- data/lib/utilrb/module/inherited_enumerable.rb +15 -0
- data/lib/utilrb/object/scoped_eval.rb +17 -0
- data/lib/utilrb/object/singleton_class.rb +3 -44
- data/lib/utilrb/pkgconfig.rb +311 -27
- data/lib/utilrb/symbol/to_str.rb +5 -0
- data/lib/utilrb/value_set.rb +12 -0
- data/test/data/test_pkgconfig_empty.pc +10 -0
- data/test/test_kernel.rb +158 -0
- data/test/test_module.rb +11 -10
- data/test/test_object.rb +0 -28
- data/test/test_pkgconfig.rb +35 -1
- metadata +88 -44
data/lib/utilrb/pkgconfig.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'shellwords'
|
2
3
|
|
3
4
|
module Utilrb
|
@@ -36,36 +37,258 @@ module Utilrb
|
|
36
37
|
# pkg.libdir
|
37
38
|
#
|
38
39
|
class PkgConfig
|
40
|
+
PACKAGE_NAME_RX = /[\w\-\.]+/
|
41
|
+
VAR_NAME_RX = /\w+/
|
42
|
+
FIELD_NAME_RX = /[\w\.\-]+/
|
43
|
+
|
44
|
+
class << self
|
45
|
+
attr_reader :loaded_packages
|
46
|
+
|
47
|
+
def clear_cache
|
48
|
+
cache.clear
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@loaded_packages = Hash.new
|
52
|
+
|
53
|
+
def self.load(path)
|
54
|
+
pkg_name = File.basename(path, ".pc")
|
55
|
+
pkg = Class.instance_method(:new).bind(PkgConfig).call(pkg_name)
|
56
|
+
pkg.load(path)
|
57
|
+
pkg
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the pkg-config object that matches the given name, and
|
61
|
+
# optionally a version string
|
62
|
+
def self.get(name, version_spec = nil)
|
63
|
+
if !(candidates = loaded_packages[name])
|
64
|
+
paths = find_all_package_files(name)
|
65
|
+
if paths.empty?
|
66
|
+
raise NotFound.new(name), "cannot find the pkg-config specification for #{name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
candidates = loaded_packages[name] = Array.new
|
70
|
+
paths.each do |p|
|
71
|
+
candidates << PkgConfig.load(p)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Now try to find a matching spec
|
76
|
+
find_matching_version(candidates, version_spec)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.new(name, version_spec = nil)
|
80
|
+
get(name, version_spec)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the first package in +candidates+ that match the given version
|
84
|
+
# spec
|
85
|
+
def self.find_matching_version(candidates, version_spec)
|
86
|
+
if version_spec
|
87
|
+
version_spec =~ /([<>=]+)\s*([\d\.]+)/
|
88
|
+
op, requested_version = $1, $2
|
89
|
+
|
90
|
+
requested_op =
|
91
|
+
if op == "=" then [0]
|
92
|
+
elsif op == ">" then [1]
|
93
|
+
elsif op == "<" then [-1]
|
94
|
+
elsif op == "<=" then [-1, 0]
|
95
|
+
elsif op == ">=" then [1, 0]
|
96
|
+
end
|
97
|
+
|
98
|
+
requested_version = requested_version.split('.').map { |v| Integer(v) }
|
99
|
+
|
100
|
+
result = candidates.find do |pkg|
|
101
|
+
requested_op.include?(pkg.version <=> requested_version)
|
102
|
+
end
|
103
|
+
if !result
|
104
|
+
raise NotFound.new(name), "no version of #{name} match #{version_spect}. Available versions are: #{candidates.map(&:raw_version).join(", ")}"
|
105
|
+
end
|
106
|
+
result
|
107
|
+
else
|
108
|
+
candidates.first
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
39
112
|
class NotFound < RuntimeError
|
40
113
|
attr_reader :name
|
41
114
|
def initialize(name); @name = name end
|
42
115
|
end
|
116
|
+
|
117
|
+
attr_reader :file
|
118
|
+
attr_reader :path
|
43
119
|
|
44
120
|
# The module name
|
45
121
|
attr_reader :name
|
46
|
-
|
47
|
-
|
122
|
+
attr_reader :description
|
123
|
+
# The module version as a string
|
124
|
+
attr_reader :raw_version
|
125
|
+
# The module version, as an array of integers
|
126
|
+
attr_reader :version
|
127
|
+
|
128
|
+
# Information extracted from the file
|
129
|
+
attr_reader :variables
|
130
|
+
attr_reader :fields
|
48
131
|
|
49
132
|
# Create a PkgConfig object for the package +name+
|
50
133
|
# Raises PkgConfig::NotFound if the module does not exist
|
51
134
|
def initialize(name)
|
52
|
-
if !system("pkg-config --exists #{name}")
|
53
|
-
raise NotFound.new(name), "#{name} is not available to pkg-config"
|
54
|
-
end
|
55
|
-
|
56
135
|
@name = name
|
57
|
-
@
|
58
|
-
@actions = Hash.new
|
136
|
+
@fields = Hash.new
|
59
137
|
@variables = Hash.new
|
60
138
|
end
|
61
139
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
140
|
+
# Helper method that expands ${word} in +value+ using the name to value
|
141
|
+
# map +variables+
|
142
|
+
#
|
143
|
+
# +current+ is a string that describes what we are expanding. It is used
|
144
|
+
# to detect recursion in expansion of variables, and to give meaningful
|
145
|
+
# errors to the user
|
146
|
+
def expand_variables(value, variables, current)
|
147
|
+
value = value.gsub(/\$\{(\w+)\}/) do |rx|
|
148
|
+
expand_name = $1
|
149
|
+
if expand_name == current
|
150
|
+
raise "error in pkg-config file #{path}: #{current} contains a reference to itself"
|
151
|
+
elsif !(expanded = variables[expand_name])
|
152
|
+
raise "error in pkg-config file #{path}: #{current} contains a reference to #{expand_name} but there is no such variable"
|
153
|
+
end
|
154
|
+
expanded
|
155
|
+
end
|
156
|
+
value
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.parse_dependencies(string)
|
160
|
+
if string =~ /,/
|
161
|
+
packages = string.split(',')
|
162
|
+
else
|
163
|
+
packages = []
|
164
|
+
words = string.split(' ')
|
165
|
+
while !words.empty?
|
166
|
+
w = words.shift
|
167
|
+
if w =~ /[<>=]/
|
168
|
+
packages[-1] += " #{w} #{words.shift}"
|
169
|
+
else
|
170
|
+
packages << w
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
result = packages.map do |dep|
|
176
|
+
dep = dep.strip
|
177
|
+
if dep =~ /^(#{PACKAGE_NAME_RX})\s*([=<>]+.*)/
|
178
|
+
PkgConfig.get($1, $2.strip)
|
179
|
+
else
|
180
|
+
PkgConfig.get(dep)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
186
|
+
SHELL_VARS = %w{Cflags Libs Libs.private}
|
187
|
+
|
188
|
+
# Loads the information contained in +path+
|
189
|
+
def load(path)
|
190
|
+
@path = path
|
191
|
+
@file = File.readlines(path).map(&:strip)
|
192
|
+
|
193
|
+
raw_variables = Hash.new
|
194
|
+
raw_fields = Hash.new
|
195
|
+
file.each do |line|
|
196
|
+
line.gsub! /\s*#.*$/, ''
|
197
|
+
next if line.empty?
|
198
|
+
|
199
|
+
case line
|
200
|
+
when /^(#{VAR_NAME_RX})\s*=(.*)/
|
201
|
+
raw_variables[$1] = $2.strip
|
202
|
+
when /^(#{FIELD_NAME_RX}):\s*(.*)/
|
203
|
+
raw_fields[$1] = $2.strip
|
204
|
+
else
|
205
|
+
raise NotImplementedError, "cannot parse pkg-config line #{line.inspect}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Resolve the variables
|
210
|
+
while variables.size != raw_variables.size
|
211
|
+
raw_variables.each do |name, value|
|
212
|
+
value = expand_variables(value, raw_variables, name)
|
213
|
+
raw_variables[name] = value
|
214
|
+
if value !~ /\$\{#{VAR_NAME_RX}\}/
|
215
|
+
variables[name] = value
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Shell-split the fields, and expand the variables in them
|
221
|
+
raw_fields.each do |name, value|
|
222
|
+
if SHELL_VARS.include?(name)
|
223
|
+
value = Shellwords.shellsplit(value)
|
224
|
+
value.map! do |v|
|
225
|
+
expand_variables(v, variables, name)
|
226
|
+
end
|
227
|
+
else
|
228
|
+
value = expand_variables(value, variables, name)
|
229
|
+
end
|
230
|
+
|
231
|
+
fields[name] = value
|
232
|
+
end
|
233
|
+
|
234
|
+
# Initialize the main flags
|
235
|
+
@raw_version = (fields['Version'] || '')
|
236
|
+
@version = raw_version.split('.').map { |v| Integer(v) if v =~ /^\d+$/ }.compact
|
237
|
+
@description = (fields['Description'] || '')
|
238
|
+
|
239
|
+
# Get the requires/conflicts
|
240
|
+
@requires = PkgConfig.parse_dependencies(fields['Requires'] || '')
|
241
|
+
@requires_private = PkgConfig.parse_dependencies(fields['Requires.private'] || '')
|
242
|
+
@conflicts = PkgConfig.parse_dependencies(fields['Conflicts'] || '')
|
243
|
+
|
244
|
+
# And finally resolve the compilation flags
|
245
|
+
@cflags = fields['Cflags'] || []
|
246
|
+
@requires.each do |pkg|
|
247
|
+
@cflags.concat(pkg.raw_cflags)
|
248
|
+
end
|
249
|
+
@requires_private.each do |pkg|
|
250
|
+
@cflags.concat(pkg.raw_cflags)
|
251
|
+
end
|
252
|
+
@cflags.uniq!
|
253
|
+
@cflags.delete('-I/usr/include')
|
254
|
+
@ldflags = Hash.new
|
255
|
+
@ldflags[false] = fields['Libs'] || []
|
256
|
+
@ldflags[false].delete('-L/usr/lib')
|
257
|
+
@ldflags[false].uniq!
|
258
|
+
@ldflags[true] = @ldflags[false] + (fields['Libs.private'] || [])
|
259
|
+
@ldflags[true].delete('-L/usr/lib')
|
260
|
+
@ldflags[true].uniq!
|
261
|
+
|
262
|
+
@ldflags_with_requires = {
|
263
|
+
true => @ldflags[true].dup,
|
264
|
+
false => @ldflags[false].dup
|
265
|
+
}
|
266
|
+
@requires.each do |pkg|
|
267
|
+
@ldflags_with_requires[true].concat(pkg.raw_ldflags_with_requires[true])
|
268
|
+
@ldflags_with_requires[false].concat(pkg.raw_ldflags_with_requires[false])
|
269
|
+
end
|
270
|
+
@requires_private.each do |pkg|
|
271
|
+
@ldflags_with_requires[true].concat(pkg.raw_ldflags_with_requires[true])
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.define_pkgconfig_action(action) # :nodoc:
|
276
|
+
class_eval <<-EOD
|
277
|
+
def pkgconfig_#{action.gsub(/-/, '_')}(static = false)
|
278
|
+
if static
|
279
|
+
`pkg-config --#{action} --static \#{name}`.strip
|
280
|
+
else
|
281
|
+
`pkg-config --#{action} \#{name}`.strip
|
282
|
+
end
|
283
|
+
end
|
284
|
+
EOD
|
66
285
|
nil
|
67
286
|
end
|
68
287
|
|
288
|
+
def pkgconfig_variable(varname)
|
289
|
+
`pkg-config --variable=#{varname}`.strip
|
290
|
+
end
|
291
|
+
|
69
292
|
# Returns the list of include directories listed in the Cflags: section
|
70
293
|
# of the pkgconfig file
|
71
294
|
def include_dirs
|
@@ -79,37 +302,98 @@ module Utilrb
|
|
79
302
|
end
|
80
303
|
|
81
304
|
ACTIONS = %w{cflags cflags-only-I cflags-only-other
|
82
|
-
libs libs-only-L libs-only-l libs-only-other
|
83
|
-
ACTIONS.each { |action|
|
305
|
+
libs libs-only-L libs-only-l libs-only-other}
|
306
|
+
ACTIONS.each { |action| define_pkgconfig_action(action) }
|
307
|
+
|
308
|
+
def raw_cflags
|
309
|
+
@cflags
|
310
|
+
end
|
311
|
+
|
312
|
+
def cflags
|
313
|
+
@cflags.join(" ")
|
314
|
+
end
|
315
|
+
|
316
|
+
def cflags_only_I
|
317
|
+
@cflags.grep(/^-I/).join(" ")
|
318
|
+
end
|
319
|
+
|
320
|
+
def cflags_only_other
|
321
|
+
@cflags.find_all { |s| s !~ /^-I/ }.join(" ")
|
322
|
+
end
|
323
|
+
|
324
|
+
def raw_ldflags
|
325
|
+
@ldflags
|
326
|
+
end
|
327
|
+
|
328
|
+
def raw_ldflags_with_requires
|
329
|
+
@ldflags_with_requires
|
330
|
+
end
|
331
|
+
|
332
|
+
def libs(static = false)
|
333
|
+
@ldflags_with_requires[static].join(" ")
|
334
|
+
end
|
335
|
+
|
336
|
+
def libs_only_L(static = false)
|
337
|
+
@ldflags_with_requires[static].grep(/^-L/).join(" ")
|
338
|
+
end
|
339
|
+
|
340
|
+
def libs_only_l(static = false)
|
341
|
+
@ldflags_with_requires[static].grep(/^-l/).join(" ")
|
342
|
+
end
|
343
|
+
|
344
|
+
def libs_only_other(static = false)
|
345
|
+
@ldflags[static].find_all { |s| s !~ /^-[lL]/ }.join(" ")
|
346
|
+
end
|
84
347
|
|
85
348
|
def method_missing(varname, *args, &proc) # :nodoc:
|
86
349
|
if args.empty?
|
87
|
-
|
350
|
+
variables[varname.to_s]
|
88
351
|
else
|
89
352
|
super(varname, *args, &proc)
|
90
353
|
end
|
91
354
|
end
|
92
355
|
|
356
|
+
def self.each_pkgconfig_directory(&block)
|
357
|
+
if path = ENV['PKG_CONFIG_PATH']
|
358
|
+
path.split(':').each(&block)
|
359
|
+
end
|
360
|
+
yield('/usr/local/lib/pkgconfig')
|
361
|
+
yield('/usr/lib/pkgconfig')
|
362
|
+
yield('/usr/share/pkgconfig')
|
363
|
+
end
|
364
|
+
|
365
|
+
# Returns true if there is a package with this name
|
366
|
+
def self.find_all_package_files(name)
|
367
|
+
result = []
|
368
|
+
each_pkgconfig_directory do |dir|
|
369
|
+
path = File.join(dir, "#{name}.pc")
|
370
|
+
if File.exists?(path)
|
371
|
+
result << path
|
372
|
+
end
|
373
|
+
end
|
374
|
+
result
|
375
|
+
end
|
376
|
+
|
93
377
|
# Returns true if there is a package with this name
|
94
378
|
def self.has_package?(name)
|
95
|
-
|
379
|
+
!find_all_package_files(name).empty?
|
96
380
|
end
|
97
381
|
|
98
382
|
# Yields the package names of available packages. If +regex+ is given,
|
99
383
|
# lists only the names that match the regular expression.
|
100
384
|
def self.each_package(regex = nil)
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
385
|
+
seen = Set.new
|
386
|
+
each_pkgconfig_directory do |dir|
|
387
|
+
Dir.glob(File.join(dir, '*.pc')) do |file|
|
388
|
+
puts file
|
389
|
+
pkg_name = File.basename(file, ".pc")
|
390
|
+
next if seen.include?(pkg_name)
|
391
|
+
next if regex && pkg_name !~ regex
|
392
|
+
|
393
|
+
seen << pkg_name
|
394
|
+
yield(pkg_name)
|
112
395
|
end
|
396
|
+
end
|
113
397
|
end
|
114
398
|
end
|
115
399
|
end
|
data/lib/utilrb/value_set.rb
CHANGED
@@ -24,5 +24,17 @@ Utilrb.require_ext("ValueSet") do
|
|
24
24
|
def self._load(str)
|
25
25
|
Marshal.load(str).to_value_set
|
26
26
|
end
|
27
|
+
|
28
|
+
def eql?(obj)
|
29
|
+
self == obj
|
30
|
+
end
|
31
|
+
|
32
|
+
def hash
|
33
|
+
result = ValueSet.hash / size
|
34
|
+
for obj in self
|
35
|
+
result = result ^ obj.hash
|
36
|
+
end
|
37
|
+
result
|
38
|
+
end
|
27
39
|
end
|
28
40
|
end
|
data/test/test_kernel.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'test_config'
|
2
|
+
require 'flexmock'
|
3
|
+
require 'tempfile'
|
2
4
|
|
3
5
|
require 'utilrb/kernel/options'
|
4
6
|
require 'utilrb/kernel/arity'
|
5
7
|
require 'utilrb/kernel/swap'
|
8
|
+
require 'utilrb/kernel/with_module'
|
9
|
+
require 'utilrb/kernel/load_dsl_file'
|
6
10
|
|
7
11
|
class TC_Kernel < Test::Unit::TestCase
|
8
12
|
def test_validate_options
|
@@ -45,6 +49,160 @@ class TC_Kernel < Test::Unit::TestCase
|
|
45
49
|
assert_nothing_raised { check_arity(object.method(:arity_1_more), 2) }
|
46
50
|
end
|
47
51
|
|
52
|
+
def test_with_module
|
53
|
+
obj = Object.new
|
54
|
+
c0, c1 = nil
|
55
|
+
mod0 = Module.new do
|
56
|
+
const_set(:Const, c0 = Object.new)
|
57
|
+
end
|
58
|
+
mod1 = Module.new do
|
59
|
+
const_set(:Const, c1 = Object.new)
|
60
|
+
end
|
61
|
+
|
62
|
+
eval_string = "Const"
|
63
|
+
const_val = obj.with_module(mod0, mod1, eval_string)
|
64
|
+
assert_equal(c0, const_val)
|
65
|
+
const_val = obj.with_module(mod1, mod0, eval_string)
|
66
|
+
assert_equal(c1, const_val)
|
67
|
+
|
68
|
+
const_val = obj.with_module(mod0, mod1) { Const }
|
69
|
+
assert_equal(c0, const_val)
|
70
|
+
const_val = obj.with_module(mod1, mod0) { Const }
|
71
|
+
assert_equal(c1, const_val)
|
72
|
+
|
73
|
+
assert_raises(NameError) { Const }
|
74
|
+
end
|
75
|
+
|
76
|
+
module Mod
|
77
|
+
module Submod
|
78
|
+
class Klass
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
const_set(:KnownConstant, 10)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_eval_dsl_file
|
86
|
+
obj = Class.new do
|
87
|
+
def real_method_called?; !!@real_method_called end
|
88
|
+
def name(value)
|
89
|
+
end
|
90
|
+
def real_method
|
91
|
+
@real_method_called = true
|
92
|
+
end
|
93
|
+
end.new
|
94
|
+
|
95
|
+
Tempfile.open('test_eval_dsl_file') do |io|
|
96
|
+
io.puts <<-EOD
|
97
|
+
real_method
|
98
|
+
if KnownConstant != 10
|
99
|
+
raise ArgumentError, "invalid constant value"
|
100
|
+
end
|
101
|
+
class Submod::Klass
|
102
|
+
def my_method
|
103
|
+
end
|
104
|
+
end
|
105
|
+
name('test')
|
106
|
+
unknown_method
|
107
|
+
EOD
|
108
|
+
io.flush
|
109
|
+
|
110
|
+
begin
|
111
|
+
eval_dsl_file(io.path, obj, [], false)
|
112
|
+
assert(obj.real_method_called?, "the block has not been evaluated")
|
113
|
+
flunk("did not raise NameError for KnownConstant")
|
114
|
+
rescue NameError => e
|
115
|
+
assert e.message =~ /KnownConstant/, e.message
|
116
|
+
assert e.backtrace.first =~ /#{io.path}:2/, "wrong backtrace when checking constant resolution: #{e.backtrace.join("\n")}"
|
117
|
+
end
|
118
|
+
|
119
|
+
begin
|
120
|
+
eval_dsl_file(io.path, obj, [Mod], false)
|
121
|
+
flunk("did not raise NoMethodError for unknown_method")
|
122
|
+
rescue NoMethodError => e
|
123
|
+
assert e.message =~ /unknown_method/
|
124
|
+
assert e.backtrace.first =~ /#{io.path}:10/, "wrong backtrace when checking method resolution: #{e.backtrace.join("\n")}"
|
125
|
+
end
|
126
|
+
|
127
|
+
# instance_methods returns strings on 1.8 and symbols on 1.9. Conver
|
128
|
+
# to strings to have the right assertion on both
|
129
|
+
methods = Mod::Submod::Klass.instance_methods(false).map(&:to_s)
|
130
|
+
assert(methods.include?('my_method'), "the 'class K' statement did not refer to the already defined class")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_eval_dsl_file_does_not_allow_class_definition
|
135
|
+
obj = Class.new do
|
136
|
+
def real_method
|
137
|
+
@real_method_called = true
|
138
|
+
end
|
139
|
+
end.new
|
140
|
+
|
141
|
+
Tempfile.open('test_eval_dsl_file') do |io|
|
142
|
+
io.puts <<-EOD
|
143
|
+
class NewClass
|
144
|
+
end
|
145
|
+
EOD
|
146
|
+
io.flush
|
147
|
+
|
148
|
+
begin
|
149
|
+
eval_dsl_file(io.path, obj, [], false)
|
150
|
+
flunk("NewClass has been defined")
|
151
|
+
rescue NameError => e
|
152
|
+
assert e.message =~ /NewClass/, e.message
|
153
|
+
assert e.backtrace.first =~ /#{io.path}:1/, "wrong backtrace when checking constant definition detection: #{e.backtrace[0]}, expected #{io.path}:1"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
DSL_EXEC_BLOCK = Proc.new do
|
159
|
+
real_method
|
160
|
+
if KnownConstant != 10
|
161
|
+
raise ArgumentError, "invalid constant value"
|
162
|
+
end
|
163
|
+
class Submod::Klass
|
164
|
+
def my_method
|
165
|
+
end
|
166
|
+
end
|
167
|
+
name('test')
|
168
|
+
unknown_method
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_dsl_exec
|
172
|
+
obj = Class.new do
|
173
|
+
def real_method_called?; !!@real_method_called end
|
174
|
+
def name(value)
|
175
|
+
end
|
176
|
+
def real_method
|
177
|
+
@real_method_called = true
|
178
|
+
end
|
179
|
+
end.new
|
180
|
+
|
181
|
+
begin
|
182
|
+
dsl_exec(obj, [], false, &DSL_EXEC_BLOCK)
|
183
|
+
assert(obj.real_method_called?, "the block has not been evaluated")
|
184
|
+
flunk("did not raise NameError for KnownConstant")
|
185
|
+
rescue NameError => e
|
186
|
+
assert e.message =~ /KnownConstant/, e.message
|
187
|
+
expected = "test_kernel.rb:160"
|
188
|
+
assert e.backtrace.first =~ /#{expected}/, "wrong backtrace when checking constant resolution: #{e.backtrace[0]}, expected #{expected}"
|
189
|
+
end
|
190
|
+
|
191
|
+
begin
|
192
|
+
dsl_exec(obj, [Mod], false, &DSL_EXEC_BLOCK)
|
193
|
+
flunk("did not raise NoMethodError for unknown_method")
|
194
|
+
rescue NoMethodError => e
|
195
|
+
assert e.message =~ /unknown_method/
|
196
|
+
expected = "test_kernel.rb:168"
|
197
|
+
assert e.backtrace.first =~ /#{expected}/, "wrong backtrace when checking method resolution: #{e.backtrace[0]}, expected #{expected}"
|
198
|
+
end
|
199
|
+
|
200
|
+
# instance_methods returns strings on 1.8 and symbols on 1.9. Conver
|
201
|
+
# to strings to have the right assertion on both
|
202
|
+
methods = Mod::Submod::Klass.instance_methods(false).map(&:to_s)
|
203
|
+
assert(methods.include?('my_method'), "the 'class K' statement did not refer to the already defined class")
|
204
|
+
end
|
205
|
+
|
48
206
|
Utilrb.require_ext('is_singleton?') do
|
49
207
|
def test_is_singleton
|
50
208
|
klass = Class.new
|
data/test/test_module.rb
CHANGED
@@ -23,10 +23,20 @@ class TC_Module < Test::Unit::TestCase
|
|
23
23
|
assert(k.respond_to?(:tag))
|
24
24
|
end
|
25
25
|
|
26
|
+
Foo = 42
|
27
|
+
|
26
28
|
def test_define_or_reuse
|
27
29
|
mod = Module.new
|
28
|
-
|
30
|
+
klass = Class.new
|
31
|
+
|
32
|
+
new_mod = mod.define_or_reuse(:Foo) { klass.new }
|
33
|
+
assert_kind_of(klass, new_mod)
|
29
34
|
assert_equal(new_mod, mod.define_or_reuse(:Foo) { flunk("block called in #define_under") })
|
35
|
+
|
36
|
+
# Now try with a constant that is widely available
|
37
|
+
new_mod = mod.define_or_reuse('Signal') { klass.new }
|
38
|
+
assert_kind_of(klass, new_mod)
|
39
|
+
assert_equal(new_mod, mod.define_or_reuse('Signal') { flunk("block called in #define_under") })
|
30
40
|
end
|
31
41
|
|
32
42
|
def test_define_method_with_block
|
@@ -93,9 +103,6 @@ class TC_Module < Test::Unit::TestCase
|
|
93
103
|
assert(object.singleton_class.respond_to?(:signatures))
|
94
104
|
object.singleton_class.signatures << :in_singleton
|
95
105
|
assert_equal([:in_singleton], object.singleton_class.signatures)
|
96
|
-
Utilrb.if_ext do
|
97
|
-
assert_equal([:in_singleton, :in_derived, :in_base], object.singleton_class.enum_for(:each_signature).to_a)
|
98
|
-
end
|
99
106
|
end
|
100
107
|
|
101
108
|
def check_inherited_enumerable(base, derived)
|
@@ -138,18 +145,12 @@ class TC_Module < Test::Unit::TestCase
|
|
138
145
|
include mod
|
139
146
|
end
|
140
147
|
child = Class.new(parent)
|
141
|
-
singleton = child.new.singleton_class
|
142
148
|
|
143
149
|
assert(child.has_ancestor?(parent))
|
144
150
|
assert(child.has_ancestor?(mod))
|
145
151
|
assert(parent.has_ancestor?(mod))
|
146
152
|
|
147
|
-
assert(singleton.has_ancestor?(parent), singleton.superclass)
|
148
|
-
assert(singleton.has_ancestor?(mod))
|
149
|
-
assert(singleton.has_ancestor?(child))
|
150
|
-
|
151
153
|
assert(!parent.has_ancestor?(child))
|
152
|
-
assert(!parent.has_ancestor?(singleton))
|
153
154
|
end
|
154
155
|
end
|
155
156
|
|
data/test/test_object.rb
CHANGED
@@ -55,34 +55,6 @@ class TC_Object < Test::Unit::TestCase
|
|
55
55
|
assert_same(obj1.block, obj2.block)
|
56
56
|
end
|
57
57
|
|
58
|
-
def test_singleton_class
|
59
|
-
klass = Class.new do
|
60
|
-
def bla; 0 end
|
61
|
-
end
|
62
|
-
|
63
|
-
object = klass.new
|
64
|
-
|
65
|
-
singleton = class << object
|
66
|
-
class << self
|
67
|
-
def bla
|
68
|
-
"a"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
self
|
72
|
-
end
|
73
|
-
|
74
|
-
assert(! object.has_singleton?)
|
75
|
-
assert_equal(object, object.singleton_class.singleton_instance)
|
76
|
-
assert(object.has_singleton?)
|
77
|
-
assert_equal(klass, object.singleton_class.superclass)
|
78
|
-
|
79
|
-
|
80
|
-
Utilrb.if_ext do
|
81
|
-
assert_equal([object.singleton_class, klass, Object], object.singleton_class.ancestors[0, 3])
|
82
|
-
assert_equal([klass, Object], klass.ancestors[0, 2])
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
58
|
def test_attr_predicate
|
87
59
|
klass = Class.new do
|
88
60
|
attr_predicate :working
|