Module: Kernel

Defined in:
lib/utilrb/kernel/poll.rb,
lib/utilrb/kernel/arity.rb,
lib/utilrb/kernel/options.rb,
lib/utilrb/kernel/require.rb,
lib/utilrb/enumerable/null.rb,
lib/utilrb/kernel/with_module.rb,
lib/utilrb/kernel/load_dsl_file.rb

Defined Under Namespace

Modules: WithModuleConstResolutionExtension

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.backtrace_remove_first_occurence_of(e, rx) ⇒ Object

Raises:

  • (e)

5
6
7
8
9
10
11
12
13
14
15
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 5

def self.backtrace_remove_first_occurence_of(e, rx)
    # Remove the first occurence of eval_dsl_file_content in the backtrace
    backtrace = e.backtrace.dup
    found = false
    backtrace.delete_if do |line|
        break if found
        line =~ rx
        found = true
    end
    raise e, e.message, e.backtrace
end

Instance Method Details

#check_arity(object, arity, strict: nil) ⇒ Object

Raises if object can accept calls with exactly arity arguments. object should respond to #arity


4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/utilrb/kernel/arity.rb', line 4

def check_arity(object, arity, strict: nil)
    if strict.nil?
        if object.respond_to?(:lambda?)
            strict = object.lambda?
        else strict = true
        end
    end

    if strict
        if object.arity >= 0 && object.arity != arity
            raise ArgumentError, "#{object} requests #{object.arity} arguments, but #{arity} was requested"
        elsif -object.arity-1 > arity
            raise ArgumentError, "#{object} requests at least #{object.arity} arguments, but #{arity} was requested"
        end
    end
end

#dsl_exec(proxied_object, context, full_backtrace, *exceptions, &code) ⇒ Object


145
146
147
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 145

def dsl_exec(proxied_object, context, full_backtrace, *exceptions, &code)
    dsl_exec_common(nil, proxied_object, context, full_backtrace, *exceptions, &code)
end

#dsl_exec_common(file, proxied_object, context, full_backtrace, *exceptions, &code) ⇒ Object


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 149

def dsl_exec_common(file, proxied_object, context, full_backtrace, *exceptions, &code)
    load_dsl_filter_backtrace(file, full_backtrace, *exceptions) do
        sandbox = with_module(*context) do
            Class.new(BasicObject) do
                def self.name; "" end
                attr_accessor :main_object
                def initialize(obj); @main_object = obj end
                def method_missing(*m, &block)
                    main_object.__send__(*m, &block)
                end
            end
        end

        old_constants, new_constants = Kernel.constants, nil

        sandbox = sandbox.new(proxied_object)
        sandbox.with_module(*context) do
            old_constants =
                if respond_to?(:constants)
                    constants
                else  self.class.constants
                end

            instance_eval(&code)

            new_constants =
                if respond_to?(:constants)
                    constants
                else  self.class.constants
                end
        end

        # Check if the user defined new constants by using class K and/or
        # mod Mod
        if !new_constants
            new_constants = Kernel.constants
        end

        new_constants -= old_constants
        new_constants.delete_if { |n| n.to_s == 'WithModuleConstResolutionExtension' }
        if !new_constants.empty?
            msg = "#{new_constants.first} does not exist. You cannot define new constants in this context"
            raise NameError.new(msg, new_constants.first)
        end
        true
    end
end

#eval_dsl(text, proxied_object, context, full_backtrace, *exceptions) ⇒ Object


93
94
95
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 93

def eval_dsl(text, proxied_object, context, full_backtrace, *exceptions)
    eval_dsl_file_content(nil, text, proxied_object, context, full_backtrace, *exceptions)
end

#eval_dsl_block(block, proxied_object, context, full_backtrace, *exceptions) ⇒ Object


86
87
88
89
90
91
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 86

def eval_dsl_block(block, proxied_object, context, full_backtrace, *exceptions)
    load_dsl_filter_backtrace(nil, full_backtrace, *exceptions) do
        proxied_object.with_module(*context, &block)
        true
    end
end

#eval_dsl_file(file, proxied_object, context, full_backtrace, *exceptions, &block) ⇒ Object

Load the given file by eval-ing it in the provided binding. The originality of this method is to translate errors that are detected in the eval'ed code into errors that refer to the provided file

The caller of this method should call it at the end of its definition file, or the translation method may not be robust at all


118
119
120
121
122
123
124
125
126
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 118

def eval_dsl_file(file, proxied_object, context, full_backtrace, *exceptions, &block)
    file = File.expand_path(file)
    if !File.readable?(file)
        raise ArgumentError, "#{file} does not exist"
    end

    file_content = File.read(file)
    eval_dsl_file_content(file, file_content, proxied_object, context, full_backtrace, *exceptions, &block)
end

#eval_dsl_file_content(file, file_content, proxied_object, context, full_backtrace, *exceptions) ⇒ Object


97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 97

def eval_dsl_file_content(file, file_content, proxied_object, context, full_backtrace, *exceptions)
    code = with_module(*context) do
        code =  "Proc.new { \#{file_content} }\n"
        if file
            eval code, binding, file, 1
        else
            eval code, binding
        end
    end

    dsl_exec_common(file, proxied_object, context, full_backtrace, *exceptions, &code)
end

#filter_options(options, *user_option_spec) ⇒ Object

Partitions an option hash between known arguments and unknown arguments, with default value support. All option keys are converted to symbols for consistency.

The following rules apply:

* if a hash is given, non-nil values are treated as default values.
* an array is equivalent to a hash where all values are 'nil'

See #validate_options and #filter_and_validate_options

call-seq:

filter_options(option, hash)       -> known, unknown
filter_options(option, array)    -> known, unknown
filter_options(nil, known_options) -> default_options, {}

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/utilrb/kernel/options.rb', line 32

def filter_options(options, *user_option_spec)
    options = options.dup
    known_options = Hash.new
    user_option_spec.each do |opt|
        if opt.respond_to?(:to_hash)
            opt.each do |key, value|
                if filter_options_handle_single_entry(known_options, options, key)
                    if !value.nil?
                        known_options[key.to_sym] = value
                    end
                end
            end
        elsif opt.respond_to?(:to_ary)
            opt.each do |key|
                filter_options_handle_single_entry(known_options, options, key)
            end
        else
            filter_options_handle_single_entry(known_options, options, opt)
        end
    end

    return *[known_options, options]
end

#filter_options_handle_single_entry(known_options, options, key) ⇒ Object


4
5
6
7
8
9
10
11
12
13
14
15
# File 'lib/utilrb/kernel/options.rb', line 4

def filter_options_handle_single_entry(known_options, options, key)
    key = key.to_sym
    if options.has_key?(key)
        known_options[key] = options.delete(key)
        false
    elsif options.has_key?(key_s = key.to_s)
        known_options[key] = options.delete(key_s)
        false
    else
        true
    end
end

#load_dsl_file(file, *args, &block) ⇒ Object

Same than eval_dsl_file, but will not load the same file twice


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 129

def load_dsl_file(file, *args, &block)
    file = File.expand_path(file)
    if $LOADED_FEATURES.include?(file)
        return false
    end

    $LOADED_FEATURES << file
    begin
        eval_dsl_file(file, *args, &block)
    rescue Exception
        $LOADED_FEATURES.delete(file)
        raise
    end
    true
end

#load_dsl_filter_backtrace(file, full_backtrace = false, *exceptions) ⇒ Object


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/utilrb/kernel/load_dsl_file.rb', line 17

def load_dsl_filter_backtrace(file, full_backtrace = false, *exceptions)
    # Compute the position of the dsl-loading method that called us, so that
    # we don't touch anything below that while we are filtering the
    # backtrace
    if !full_backtrace
        callers = caller
        our_frame_pos = caller.size
        callers.each do |line|
            if line != /load_dsl_file\.rb/
                our_frame_pos -= 1
            else
                break
            end
        end
    end

    yield

rescue Exception => e
    raise e if full_backtrace
    if exceptions.any? { |e_class| e.kind_of?(e_class) }
        raise e
    end

    backtrace = e.backtrace.dup
    message   = e.message.dup

    # Filter out the message ... it can contain backtrace information as
    # well (!)
    message = message.split("\n").map do |line|
        if line =~ /^.*:\d+(:.*)$/
            backtrace.unshift line
            nil
        else
            line
        end
    end.compact.join("\n")


    if message.empty?
        message = backtrace.shift
        if message =~ /^(\s*[^\s]+:\d+:)\s*(.*)/
            location = $1
            message  = $2
            backtrace.unshift location
        else
            backtrace.unshift message
        end
    end

    filtered_backtrace = backtrace[0, backtrace.size - our_frame_pos].
        map do |line|
            line = line.gsub(/:in `.*dsl.*'/, '')
            if line =~ /load_dsl_file.*(method_missing|send)/
                next
            end

            if line =~ /(load_dsl_file\.rb|with_module\.rb):\d+/
                next
            else
                line
            end
        end.compact


    backtrace = (filtered_backtrace[0, 1] + filtered_backtrace + backtrace[(backtrace.size - our_frame_pos)..-1])
    raise e, message, backtrace
end

#normalize_options(options) ⇒ Object

Normalizes all keys in the given option hash to symbol and returns the modified hash


58
59
60
# File 'lib/utilrb/kernel/options.rb', line 58

def normalize_options(options)
    options.to_sym_keys
end

#null_enumObject

returns always the same null enumerator, to avoid creating objects. It can be used as a seed to #inject:

enumerators.inject(null_enum) { |a, b| a + b }.each do |element|
end

16
17
18
# File 'lib/utilrb/enumerable/null.rb', line 16

def null_enum
  @@null_enumerator ||= NullEnumerator.new.freeze
end

#poll(cycle) ⇒ Object

Yields every cycle seconds


3
4
5
6
7
8
# File 'lib/utilrb/kernel/poll.rb', line 3

def poll(cycle)
  loop do
 yield
 sleep(cycle)
  end
end

#require_dir(filename, exclude = nil) ⇒ Object

Require all .rb files in the filename directory


3
4
5
6
7
8
9
10
11
# File 'lib/utilrb/kernel/require.rb', line 3

def require_dir(filename, exclude = nil)
  dirname = filename.gsub(/.rb$/, '')
  Dir.new(dirname).each do |file|
        next if exclude && exclude === file
 if file =~ /\.rb$/
    require File.join(dirname, file)
 end
  end
end

#validate_option(options, name, required, message = nil) ⇒ Object

call-seq: validate_option(options, name, required, message) { |v| … } validate_option(options, name, required, message)

Validates option name in the options hash. If required is true, raises ArgumentError if the option is not present. Otherwise, yields its value to an optional block, which should return if the value is valid, or false otherwise. If the value is invalid, raises ArgumentError with message or a standard message.


90
91
92
93
94
95
96
97
98
# File 'lib/utilrb/kernel/options.rb', line 90

def validate_option(options, name, required, message = nil)
    if required && !options.has_key?(name)
        raise ArgumentError, "missing required option #{name}"
    elsif options.has_key?(name) && block_given?
        if !yield(options[name])
            raise ArgumentError, (message || "invalid option value #{options[name]} for #{name}")
        end
    end
end

#validate_options(options, *known_options) ⇒ Object

Validates an option hash, with default value support. See #filter_options

In the first form, option_hash should contain keys which are also in known_hash. The non-nil values of known_hash are used as default values. In the second form, known_array is an array of option keys. option_hash keys shall be in known_array. nil is treated as an empty option hash, all keys are converted into symbols.


70
71
72
73
74
75
76
77
78
79
# File 'lib/utilrb/kernel/options.rb', line 70

def validate_options(options, *known_options)
  options ||= Hash.new
  opt, unknown = Kernel.filter_options(options.to_hash, *known_options)
  unless unknown.empty?
 not_valid = unknown.keys.map { |m| "'#{m}'" }.join(" ")
 raise ArgumentError, "unknown options #{not_valid}", caller(1)
  end

  opt
end

#wait_until(cycle) ⇒ Object

Yields every cycle seconds until the block returns true.


11
12
13
14
15
# File 'lib/utilrb/kernel/poll.rb', line 11

def wait_until(cycle)
    until yield
 sleep(cycle)
  end
end

#wait_while(cycle) ⇒ Object

Yields every cycle seconds until the block returns false.


18
19
20
21
22
# File 'lib/utilrb/kernel/poll.rb', line 18

def wait_while(cycle)
    while yield
        sleep(cycle)
    end
end

#with_module(*consts, &block) ⇒ Object


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/utilrb/kernel/with_module.rb', line 18

def with_module(*consts, &block)
    Thread.current[:__with_module__] ||= Array.new
    Thread.current[:__with_module__].push consts
    Kernel.send(:extend, WithModuleConstResolutionExtension)
    Object.extend WithModuleConstResolutionExtension

    eval_string =
        if !block_given? && consts.last.respond_to?(:to_str)
            consts.pop
        end
    if eval_string
        instance_eval(eval_string)
    else
        instance_eval(&block)
    end

ensure
    Thread.current[:__with_module__].pop
end