Skip to content

Commit

Permalink
Performance improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
steventebrinke committed Dec 2, 2015
1 parent 8c0fbf5 commit 4b58dac
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 28 deletions.
15 changes: 10 additions & 5 deletions lib/dbf/column.rb
Expand Up @@ -46,11 +46,7 @@ def initialize(table, name, type, length, decimal)
# @param [String] value
# @return [Fixnum, Float, Date, DateTime, Boolean, String]
def type_cast(value)
return nil if length == 0

klass = TYPE_CAST_CLASS[type.to_sym]
cast_value = klass.new(value, decimal).type_cast
binary? ? cast_value : encode(cast_value)
type_cast_class.type_cast(value)
end

# Returns true if the column is a memo
Expand Down Expand Up @@ -98,6 +94,15 @@ def to_hash

private

def type_cast_class # nodoc
@type_cast_class ||=
if @length == 0
ColumnType::Nil
else
TYPE_CAST_CLASS[type.to_sym]
end.new(@decimal, @encoding)
end

def encode(value, strip_output = false) # nodoc
return value if !value.respond_to?(:encoding)

Expand Down
56 changes: 39 additions & 17 deletions lib/dbf/column_type.rb
@@ -1,52 +1,58 @@
module DBF
module ColumnType
class Base
attr_reader :value, :decimal
attr_reader :decimal, :encoding

def initialize(value, decimal)
@value = value
def initialize(decimal, encoding)
@decimal = decimal
@encoding = encoding
end
end

class Nil < Base
def type_cast(value)
nil
end
end

class Number < Base
def type_cast
decimal.zero? ? value.to_i : value.to_f
def type_cast(value)
@decimal.zero? ? value.to_i : value.to_f
end
end

class Currency < Base
def type_cast
def type_cast(value)
(value.unpack('q<')[0] / 10_000.0).to_f
end
end

class SignedLong < Base
def type_cast
def type_cast(value)
value.unpack('l<')[0]
end
end

class Float < Base
def type_cast
def type_cast(value)
value.to_f
end
end

class Double < Base
def type_cast
def type_cast(value)
value.unpack('E')[0]
end
end

class Boolean < Base
def type_cast
def type_cast(value)
value.strip =~ /^(y|t)$/i ? true : false
end
end

class Date < Base
def type_cast
def type_cast(value)
v = value.tr(' ', '0')
v !~ /\S/ ? nil : ::Date.parse(v)
rescue
Expand All @@ -55,7 +61,7 @@ def type_cast
end

class DateTime < Base
def type_cast
def type_cast(value)
days, msecs = value.unpack('l2')
secs = (msecs / 1000).to_i
::DateTime.jd(days, (secs / 3600).to_i, (secs / 60).to_i % 60, secs % 60)
Expand All @@ -65,21 +71,37 @@ def type_cast
end

class Memo < Base
def type_cast
value
ENCODING_ARGS = [
Encoding.default_external,
{undef: :replace, invalid: :replace}
]

def type_cast(value)
if encoding and not value.nil?
value.force_encoding(@encoding).encode(*ENCODING_ARGS)
else
value
end
end
end

class General < Base
def type_cast
def type_cast(value)
value
end
end

class String < Base
def type_cast
value.strip
ENCODING_ARGS = [
Encoding.default_external,
{undef: :replace, invalid: :replace}
]

def type_cast(value)
value = value.strip
@encoding ? value.force_encoding(@encoding).encode(*ENCODING_ARGS) : value
end
end

end
end
14 changes: 8 additions & 6 deletions lib/dbf/record.rb
Expand Up @@ -90,7 +90,7 @@ def underscored_column_names # nodoc
end

def init_attribute(column) # nodoc
value = column.memo? ? memo(column) : unpack_data(column)
value = column.memo? ? memo(column) : get_data(column)
column.type_cast(value)
end

Expand All @@ -105,13 +105,15 @@ def memo(column) # nodoc
end

def memo_start_block(column) # nodoc
format = 'V' if %w(30 31).include?(@version)
unpack_data(column, format).to_i
data = get_data(column)
if %w(30 31).include?(@version)
data = data.unpack('V').first
end
data.to_i
end

def unpack_data(column, format = nil) # nodoc
format ||= "a#{column.length}"
@data.read(column.length).unpack(format).first
def get_data(column) # nodoc
@data.read(column.length)
end
end
end

0 comments on commit 4b58dac

Please sign in to comment.