Module: NWN::Gff::Scripting

Included in:
Sandbox
Defined in:
lib/nwn/scripting.rb

Overview

This module contains scripting functions and helpers.

Include this if you want to eval nwn-gff-dsl scripts.

Defined Under Namespace

Classes: Sandbox

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.run_script(code, run_on = nil, arguments = []) ⇒ Object

Run a script in a sandboxish environ. Returns true if the code modified run_on in any way.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/nwn/scripting.rb', line 13

def self.run_script code, run_on = nil, arguments = []
  $code = code
  $argv = arguments
  $standalone = run_on.nil?
  $satisfy_loaded = {}
  run_on ||= Sandbox.new

  $script_obj_hash = run_on.hash
  catch(:exit) {
    begin
      run_on.instance_eval $code
    rescue => e
      raise
    end
  }
  $script_obj_hash != run_on.hash
end

Instance Method Details

#ask(question, match = nil) ⇒ Object

Ask the user for something. question the Question to ask match a selection of answers to choose from (eg, a hash, the user would choose the key)



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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/nwn/scripting.rb', line 168

def ask question, match = nil
  object = case match
    when Array
      i = 0 ; Hash[match.map {|x| [i+=1, x]}]

    when Hash, Regexp, Fixnum, Float
      match

    else
      raise NWN::Gff::GffError, "Do not know how to " +
        "validate against #{match.class}"
  end

  ret = nil
  while true
    y object
    $stderr.print File.basename($SCRIPT) + ": " + question + " "
    ret = $stdin.gets
    ret = ret.rstrip

    break if object.nil? || case object
      when Hash
        if object.keys.index(ret) || (ret != "" && object.keys.index(ret.to_i))
          ret = object[ret] || (ret != "" && object[ret.to_i])
        else
          nil
        end
      when Regexp
        ret =~ match
      when Fixnum
        ret =~ /^\d+$/
      when Float
        ret =~ /^\d+(.\d+)?$/
    end
  end

  case match
    when Float; ret.to_f
    when Fixnum; ret.to_i
    else; ret
  end
end

#log(*args) ⇒ Object

Log a friendly message to stderr. Use this instead of puts, since SAFE levels greater than 0 will prevent you from doing logging yourself.



155
156
157
158
159
160
161
162
# File 'lib/nwn/scripting.rb', line 155

def log *args
  perc = $PERCENTAGE_DONE.nil? ? "" : " (%d%%)" % [ $PERCENTAGE_DONE.to_i ]
  if $options
    $stderr.puts [$SCRIPT, perc, " on ", $options[:infile], ": ", *args].join("")
  else
    $stderr.puts [$SCRIPT, perc, ": ", *args].join("")
  end
end

#need(*what) ⇒ Object

Same as want, but error out (don’t continue processing).



60
61
62
# File 'lib/nwn/scripting.rb', line 60

def need *what
  satisfy(*what) or raise ArgumentError, "Needs #{what.inspect}, cannot satisfy - aborting."
end

#progress(position, total_size = nil) ⇒ Object

You can call this to provide a progress indicator, if your script is long-running. the calculated percentage will be prefixed before each log message.

position The number of items in the work queue finished. total_size The total size of the work queue, defaults to ARGV.size.



147
148
149
150
# File 'lib/nwn/scripting.rb', line 147

def progress position, total_size = nil
  total_size ||= ARGV.size
  $PERCENTAGE_DONE = position.to_f / total_size.to_f * 100
end

#satisfy(*what) ⇒ Object

This checks if the currently-operating field or struct satisfies one of the given conditions.

When you’re running in standalone mode, the first argument is expected to be a file or IO stream that needs to satisfy the given conditions.

Example:

need ARGV.shift, :bic, :utc, :uti

will require the user to supply a filename as the first argument to the standalone script, which needs to resolve to a bic, utc, or uti data_type.

Conditions can be:

  • A symbol describing the data_type, eg :utc

  • A symbol describing the field_type, eg :int

  • A module or class name

Returns the object that satisfies the asked-for conditions, or nil if none can be given.

If only a filename/string is given and no further arguments, the read object will be returned as-is.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/nwn/scripting.rb', line 101

def satisfy *what
  if $standalone
    fn = what.shift
    io = case fn
      when String
        File.new(fn, "r")
      when IO
        fn
      else
        return nil
        #raise ArgumentError, "When running in standalone mode, " +
        #  "`need', `want' and `satisfy' need a filename or a IO " +
        #  "object to read from (usually the first script argument)."
    end
    obj = NWN::Gff.read(io, NWN::Gff.guess_file_format(fn))
    log "satisfied #{fn} -> #{obj.to_s}"
    $satisfy_loaded[obj.object_id] = [fn, obj.hash]

    return obj if what.size == 1
  else
    obj = self
  end

  what.each {|w|
    case w
      when Class, Module
        return obj if obj.is_a?(w)

      when Symbol
        case obj
          when NWN::Gff::Struct
            return obj if obj.data_type.downcase == w.to_s.downcase
          when NWN::Gff::Field
            return obj if obj.field_type.to_sdowncase == w.to_s.downcase
        end
    end
  }

  return nil
end

#save(object) ⇒ Object

Save the given object if it was loaded via want/need



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/nwn/scripting.rb', line 32

def save object
  fn, hash = $satisfy_loaded[object.object_id]
  if fn
    if hash != object.hash
      File.open(fn, "wb") {|f|
        NWN::Gff.write(f, NWN::Gff.guess_file_format(fn), object)
      }
      log "saved #{object.to_s} -> #{fn}"
    else
      log "not saving #{fn}: not modified"
    end
  else
    raise ArgumentError,
      "#save: object #{object.to_s} was not loaded via want/need/satisfy, cannot save"
  end
end

#stop_outputObject

Call this to prevent nwn-gff from emitting output.



65
66
67
68
69
70
71
72
# File 'lib/nwn/scripting.rb', line 65

def stop_output
  if $standalone
    log "warn: no need to stop_output on standalone scripts"
  else
    log "#{$SCRIPT}: not emitting any data."
  end
  $stop_output = true
end

#want(*what) ⇒ Object

This script only runs for the following conditions (see #satisfy).



50
51
52
53
54
55
56
57
# File 'lib/nwn/scripting.rb', line 50

def want *what
  obj = satisfy(*what)
  unless obj
    log "Wants #{what.inspect}, cannot satisfy - continuing."
    throw(:exit)
  end
  obj
end

#will_output?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/nwn/scripting.rb', line 74

def will_output?
  !$stop_output
end