utilrb 1.3.3 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|