Module: NWN::Gff::Field

Defined in:
lib/nwn/gff/field.rb,
lib/nwn/yaml.rb,
lib/nwn/gff/api.rb,
lib/nwn/gff/cexolocstr.rb

Overview

A Field wraps a GFF label->value pair, providing:

  • .field_type describing the field type (e.g. :int)

  • .field_value holding the value of this Field

and, if loaded by Gff::Reader or through YAML:

  • .field_label holding the label

  • .parent holding the struct this Field is child of.

Note that it is ADVISED to use the provided accessors, since they do some structure-keeping in the background. If you do NOT want it to do that, use hash-notation for access:

field['value'], field['type'], field['str_ref'], field['label']

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#parentObject

The parent struct. This is set internally by Gff::Reader on load.



17
18
19
# File 'lib/nwn/gff/field.rb', line 17

def parent
  @parent
end

Class Method Details

.new(label, type, value) ⇒ Object

Create a new NWN::Gff::Field



20
21
22
23
24
25
26
# File 'lib/nwn/gff/field.rb', line 20

def self.new label, type, value
  s = {}.extend(self)
  s['label'], s['type'], s['value'] = label, type, value
  s.extend_meta_classes
  s.validate
  s
end

.valid_for?(value, type) ⇒ Boolean

Validate if value is within bounds of type.

Returns:

  • (Boolean)


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
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/nwn/gff/field.rb', line 105

def self.valid_for? value, type
  case type
    when :byte, :char
      value.is_a?(Integer) && value >= 0 && value <= 255

    when :short
      value.is_a?(Integer) && value >= -0x8000 && value <= 0x7fff
    when :word
      value.is_a?(Integer) && value >= 0 && value <= 0xffff

    when :int
      value.is_a?(Integer) && value >= -0x80000000 && value <= 0x7fffffff
    when :dword
      value.is_a?(Integer) && value >= 0 && value <= 0xffffffff

    when :int64
      value.is_a?(Integer) && value >= -0x800000000000 && value <= 0x7fffffffffff
    when :dword64
      value.is_a?(Integer) && value >= 0 && value <= 0xffffffffffff

    when :float, :double
      value.is_a?(Float)

    when :resref
      value.is_a?(String) && (0..16).member?(value.size)

    when :cexostr
      value.is_a?(String)

    when :cexolocstr
      value.is_a?(Hash) &&
        value.keys.reject {|x| x.is_a?(Fixnum) && x >= 0 }.size == 0 &&
        value.values.reject {|x| x.is_a?(String) }.size == 0

    when :struct
      value.is_a?(Hash)

    when :list
      value.is_a?(Array)

    when :void
      value.is_a?(String)

    else
      false
  end
end

Instance Method Details

#each_by_flat_path(&block) ⇒ Object

Used by NWN::Gff::Struct#by_flat_path



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/nwn/gff/api.rb', line 5

def each_by_flat_path &block
  case field_type
    when :cexolocstr
      yield("", self)
      field_value.sort.each {|lid, str|
        yield("/" + lid.to_s, str)
      }

    when :struct
      yield("", self)
      field_value.each_by_flat_path {|v, x|
        yield(v, x)
      }

    when :list
      yield("", self)
      field_value.each_with_index {|item, index|
        yield("[" + index.to_s + "]", item)
        item.each_by_flat_path("/") {|v, x|
          yield("[" + index.to_s + "]" + v, x)
        }
      }

    else
      yield("", self)
  end
end

#extend_meta_classesObject

This extends this field object and its’ value with the appropriate meta classes, depending on field_type.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/nwn/gff/field.rb', line 76

def extend_meta_classes
  return if field_type == :struct

  field_klass_name = field_type.to_s.capitalize
  field_klass = NWN::Gff.const_defined?(field_klass_name) ?
    NWN::Gff.const_get(field_klass_name) : nil
  field_value_klass = NWN::Gff.const_defined?(field_klass_name + 'Value') ?
    NWN::Gff.const_get(field_klass_name + 'Value') : nil

  self.extend(field_klass) unless field_klass.nil? ||
    self.is_a?(field_klass)

  field_value.extend(field_value_klass) unless field_value_klass.nil? ||
    field_value.is_a?(field_value_klass)
end

#field_labelObject Also known as: l



51
52
53
# File 'lib/nwn/gff/field.rb', line 51

def field_label
  self['label']
end

#field_label=(l) ⇒ Object Also known as: l=



56
57
58
# File 'lib/nwn/gff/field.rb', line 56

def field_label= l
  self['label']= l
end

#field_typeObject Also known as: t



28
29
30
# File 'lib/nwn/gff/field.rb', line 28

def field_type
  self['type']
end

#field_type=(t) ⇒ Object Also known as: t=



33
34
35
# File 'lib/nwn/gff/field.rb', line 33

def field_type= t
  self['type'] = t
end

#field_valueObject Also known as: v



38
39
40
# File 'lib/nwn/gff/field.rb', line 38

def field_value
  self['value']
end

#field_value=(v) ⇒ Object Also known as: v=



43
44
45
46
47
48
# File 'lib/nwn/gff/field.rb', line 43

def field_value= v
  NWN::Gff::Field.valid_for?(v, field_type) or raise ArgumentError,
    "Given field_value is not valid for type #{field_type.inspect}."

  self['value'] = v
end

#has_str_ref?Boolean

Returns:

  • (Boolean)


2
3
4
# File 'lib/nwn/gff/cexolocstr.rb', line 2

def has_str_ref?
  false
end

#pathObject

Returns the path to this field, including all parents structs. For example: UTI/PropertiesList/CostTable

Raises:



63
64
65
66
67
68
69
70
71
72
# File 'lib/nwn/gff/field.rb', line 63

def path
  raise NWN::Gff::GffError, "field not bound to a parent" unless @parent
  parent_path = @parent.path
  if @parent.element && @parent.element.field_type == :list
    idx = @parent.element.field_value.index(@parent)
    parent_path + "[#{idx}]/" + field_label
  else
    parent_path + "/" + field_label
  end
end

#to_yaml(opts = {}) ⇒ Object



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

def to_yaml(opts = {})
  YAML::quick_emit(nil, opts) do |out|
    out.map(taguri, to_yaml_style) do |map|
      map.style = :inline unless NWN::Gff::YAMLNonInlineableFields.index(self['type'])
      map.add('type', self['type'])
      map.add('str_ref', self['str_ref']) if has_str_ref?
      map.add('value', self['value'])
    end
  end
end

#valid?Boolean

Validate if this field value is within the bounds of the set type.

Returns:

  • (Boolean)


93
94
95
# File 'lib/nwn/gff/field.rb', line 93

def valid?
  NWN::Gff::Field.valid_for? self.v, self.t
end

#validateObject

Validate this field, and raise an Excpetion if not valid.



98
99
100
101
102
# File 'lib/nwn/gff/field.rb', line 98

def validate
  valid? or raise NWN::Gff::GffError,
    "#{self.path rescue $!.to_s + '/' + self.l}: " +
      "value '#{self.v.inspect}' not valid for type '#{self.t.inspect}'"
end