Class: Rod::Property::Field

Inherits:
Base
  • Object
show all
Defined in:
lib/rod/property/field.rb

Overview

This class defines the field property. A field has to define its name and its type.

Constant Summary collapse

VALID_TYPES =

The valid types of fields.

[:string, :integer, :float, :ulong, :object, :json]
VARIABLE_TYPES =

The fields with variable size.

[:string, :object, :json]
IDENTIFIER =

The name of the field used to identify the objects.

:rod_id

Instance Attribute Summary collapse

Attributes inherited from Base

#name, #options

Instance Method Summary collapse

Methods inherited from Base

#define_finders, #difference, #has_index?, #index, #reset_index

Constructor Details

#initialize(klass, name, type, options = {}) ⇒ Field

Initialize the field associated with the klass with name, type and options. The type should be one of:

  • :integer

  • :ulong

  • :float

  • :string

  • :object (value is marshaled durign storage, and unmarshaled during read)

  • :json (value is dumped in JSON format during storage, and loaded during read. Note: some Ruby types are not unified during conversion, e.g. String and Symbol)

The valid options are:

  • :index builds an index for the field and might be:

** :flat simple hash index (true works as well for backwards compatiblity) ** :segmented index split for 1001 pieces for shorter load times (only

one piece is loaded on one look-up)


36
37
38
39
40
# File 'lib/rod/property/field.rb', line 36

def initialize(klass,name,type,options={})
  super(klass,name,options)
  check_type(type)
  @type = type
end

Instance Attribute Details

#typeObject (readonly)

The type of the property.



11
12
13
# File 'lib/rod/property/field.rb', line 11

def type
  @type
end

Instance Method Details

#association?Boolean

Predicate indicating that this property is not an association.

Returns:

  • (Boolean)


53
54
55
# File 'lib/rod/property/field.rb', line 53

def association?
  false
end

#copy(klass) ⇒ Object

Creates a copy of the field with a new klass.



43
44
45
# File 'lib/rod/property/field.rb', line 43

def copy(klass)
  self.class.new(klass,@name,@type,@options)
end

#default_valueObject

Returns the default value for given type of field.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/rod/property/field.rb', line 58

def default_value
  case @type
  when :integer
    0
  when :ulong
    0
  when :float
    0.0
  when :string
    ''
  when :object, :json
    nil
  end
end

#define_c_accessors(builder) ⇒ Object

Defines the accessor of the field’s constituents (C struct field/fields that hold the field data).



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/rod/property/field.rb', line 146

def define_c_accessors(builder)
  unless variable_size?
    field_reader(@name,@klass.struct_name,c_type(@type),builder)
    field_writer(@name,@klass.struct_name,c_type(@type),builder)
  else
    field_reader("#{@name}_length",@klass.struct_name,c_type(:ulong),builder)
    field_reader("#{@name}_offset",@klass.struct_name,c_type(:ulong),builder)
    field_writer("#{@name}_length",@klass.struct_name,c_type(:ulong),builder)
    field_writer("#{@name}_offset",@klass.struct_name,c_type(:ulong),builder)
  end
end

#define_getterObject

Defines the getter of the Ruby class which corresponds to this field.



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
210
211
212
213
214
215
216
217
# File 'lib/rod/property/field.rb', line 172

def define_getter
  field = @name.to_s
  unless variable_size?
    @klass.send(:define_method,field) do
      value = instance_variable_get("@#{field}")
      if value.nil?
        if self.new?
          value = nil
        else
          value = send("_#{field}",@rod_id)
        end
        instance_variable_set("@#{field}",value)
      end
      value
    end
  else
    is_object = @type != :string
    type = @type
    property = self
    database = @klass.database
    @klass.send(:define_method,field) do
      value = instance_variable_get("@#{field}")
      if value.nil? # first call
        if self.new?
          return (is_object ? nil : "")
        else
          length = send("_#{field}_length", @rod_id)
          if length == 0
            return (is_object ? nil : "")
          end
          offset = send("_#{field}_offset", @rod_id)
          read_options = {}
          if is_object
            read_options[:skip_encoding] = true
          end
          value = database.read_string(length, offset)
          value = property.load(value)
          # caching Ruby representation
          # don't use setter - avoid change tracking
          instance_variable_set("@#{field}",value)
        end
      end
      value
    end
  end
end

#define_setterObject

Defines the settor of the Ruby class which corresponds to this field.



220
221
222
223
224
225
226
227
228
229
# File 'lib/rod/property/field.rb', line 220

def define_setter
  # optimization
  field = @name.to_s
  @klass.send(:define_method,"#{field}=") do |value|
    old_value = send(field)
    send("#{field}_will_change!") unless old_value == value
    instance_variable_set("@#{field}",value)
    value
  end
end

#dump(value) ⇒ Object

Dumps the value of the field according to its type.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/rod/property/field.rb', line 79

def dump(value)
  case @type
  when :object
    Marshal.dump(value)
  when :json
    JSON.dump([value])
  when :string
    # TODO the encoding should be stored in the DB
    # or configured globally
    value.encode("utf-8")
  when :ulong
    raise InvalidArgument.new(value,"ulong") if value < 0
    value
  else
    value
  end
end

#field?Boolean

Predicate indicating that this property is a field.

Returns:

  • (Boolean)


48
49
50
# File 'lib/rod/property/field.rb', line 48

def field?
  true
end

#identifier?Boolean

Returns true if the field is used to identify the objects.

Returns:

  • (Boolean)


114
115
116
# File 'lib/rod/property/field.rb', line 114

def identifier?
  @name == IDENTIFIER
end

#layoutObject

Returns the memory layout of the C struct fields that correspond to this field.



233
234
235
236
237
238
239
240
# File 'lib/rod/property/field.rb', line 233

def layout
  unless variable_size?
    "#{@name}[value:#{sizeof(@type)}]"
  else
    "#{@name}[length:#{sizeof(:ulong)}+" +
      "offset:#{sizeof(:ulong)}]"
  end
end

#load(value) ⇒ Object

Loads the value of the field according to its type.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/rod/property/field.rb', line 98

def load(value)
  return value unless variable_size?
  case @type
  when :object
    value.force_encoding("ascii-8bit")
    value = Marshal.load(value) rescue nil
  when :json
    value.force_encoding("ascii-8bit")
    value = JSON.load(value).first rescue nil
  when :string
    value.force_encoding("utf-8")
  end
  value
end

#metadataObject

Returns the metadata of the field in form of a hash.



119
120
121
# File 'lib/rod/property/field.rb', line 119

def 
  @options.merge({:type => @type})
end

#seal_c_accessorsObject

Make the C accessors private.



159
160
161
162
163
164
165
166
167
168
169
# File 'lib/rod/property/field.rb', line 159

def seal_c_accessors
  unless variable_size?
    @klass.send(:private,"_#{@name}")
    @klass.send(:private,"_#{@name}=")
  else
    @klass.send(:private,"_#{@name}_length")
    @klass.send(:private,"_#{@name}_length=")
    @klass.send(:private,"_#{@name}_offset")
    @klass.send(:private,"_#{@name}_offset=")
  end
end

#to_c_structObject

Converts the field to fields in a C struct.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/rod/property/field.rb', line 124

def to_c_struct
  unless variable_size?
    str = <<-SUBEND
    |#ifdef __BYTE_ORDER
    |#  if __BYTE_ORDER == __BIG_ENDIAN
    |  uint64_t #{@name};
    |#  else
    |  #{c_type(@type)} #{@name};
    |#  endif
    |#else
    |  #{c_type(@type)} #{@name};
    |#endif
    SUBEND
    str.margin
  else
    "  #{c_type(:ulong)} #{@name}_length;\n" +
      "  #{c_type(:ulong)} #{@name}_offset;\n"
  end
end

#variable_size?Boolean

Returns true if the field has a variable size.

Returns:

  • (Boolean)


74
75
76
# File 'lib/rod/property/field.rb', line 74

def variable_size?
  VARIABLE_TYPES.include?(@type)
end