Comparing Floating Point Values In Ruby

I had a recent issue where I had to compare floating point values in Ruby, and this little solution worked like a charm.

class Float

  def near?(another_float,delta=0.01)
    self.abs - another_float.abs
  end

end

Now if I want to compare, say, 1.25 and 1.249999998 (which would return false using the == operator), instead I can now call (1.25).near? 1.249999998 and get a true result.

The delta parameter allows for changing the measure of distance between the floats.

Hope this helps someone!

Finding the number of months between two dates in Ruby

One of my private projects is a Rails project, and today I needed to figure out how to calculate the difference (in months) between two dates.  Googling around didn’t prove very useful, so here’s what I came up with that works for me:

def difference_in_months(date1, date2)
  (date1.month-date2.month).abs + (12*(date1.year-date2.today.year).abs)
end

Hopefully this helps someone else. Happy colorful typing!

Delphi RTTI: Attributes not checked at compile time?

This is a real gotcha! Delphi’s compiler does not check attributes at compile time.  The logic escapes me; any insight on why this might be happening would be very welcome, but the takeaway is this: if you find your classes don’t have attributes that you’ve declared on them, check your uses clause!

This cost me an hour of frustration and hopefully this will save you the same.

Continue reading

Generating a SHA-1 checksum for a given class type in Delphi

Today I ran into the need to generate a checksum for a class type.  I’m working on a project where if the class definition changes down the road, the application needs to know it changed and behave accordingly.

In order to do this I needed to be able to gather info about the class and generate a checksum that would let me quickly check if the class had changed in the future.  This is an example of where the new Delphi 2010 RTTI really shines.  The link is for Robert Love’s articles on Delphi’s newer RTTI features and those articles helped me get under the hood and understand them.

In this example, I’m gathering info on the class name, it’s properties, fields, methods and attributes – likely overkill.  You could gather more info, or less, depending on your needs.  The goal here is to generate one giant input string to the SHA1FromString function that will likely change if anything about the class changes.

The SHA1 functionality comes from the Indy library.  Thanks to Zarko for pointing this out!

program ClassChecksum;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  IdHashSHA,
  Rtti;

type

  TPerson = class(TObject)
  private
    FBirthdate: TDate;
    FName: string;
    FPhone: string;
  public
    property Birthdate: TDate read FBirthdate write FBirthdate;
    property Name: string read FName write FName;
    property Phone: string read FPhone write FPhone;
  end;

function SHA1FromString(const AString: string): string;
var
  SHA1: TIdHashSHA1;
begin
  SHA1 := TIdHashSHA1.Create;
  try
    Result := SHA1.HashStringAsHex(AString);
  finally
    SHA1.Free;
  end;
end;

function GenerateClassChecksum(AClass: TClass): string;
var
  Attribute: TCustomAttribute;
  Context: TRttiContext;
  Field: TRttiField;
  InputStr: string;
  Method: TRttiMethod;
  Prop: TRttiProperty;
  RttiType: TRttiType;
begin
  Context := TRttiContext.Create;
  try
    RttiType := Context.GetType(AClass);
    { Start with the class name }
    InputStr := AClass.ClassName;
    { Walk the properties }
    for Prop in RttiType.GetProperties do
      InputStr := InputStr + Prop.Name;
    { Walk the fields }
    for Field in RttiType.GetFields do
      InputStr := InputStr + Field.Name;
    { Walk the methods }
    for Method in RttiType.GetMethods do
      InputStr := InputStr + Method.Name;
    { Walk the attributes }
    for Attribute in RttiType.GetAttributes do
      InputStr := InputStr + Attribute.ToString;
    { Generate and return the checksum }
    Result := SHA1FromString(InputStr);
  finally
    Context.Free;
  end;
end;

begin
  Writeln('Checksum for class TPerson:');
  Writeln(GenerateClassChecksum(TPerson));
  Readln;
end.

Here’s the output from the program:

Checksum for class TPerson:
EC0719180793CF0A63D31C81983FF6A05A37331E

A pretty unusual need, but hopefully this will help someone.  Enjoy!