Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a (Ruby) DataMapper custom type to work?

I have a SchoolDay class that represents a school day: it can tell you the date, the semester, the term, the week, and the day. It can generate a string like "Sem1 13A Fri". To store these objects in the database, I want them serialized as a string.

Here is my DataMapper custom type code. I've sort of scraped ideas from the code in dm-types because (disappointingly) there is no real documentation for creating custom types. Sorry it's long.

module DataMapper
  class Property
    class SchoolDay < DataMapper::Property::String

      #load_as ::SchoolRecord::DomainObjects::SchoolDay
      # Commented out: the 'load_as' method is not found

      def load(value)
        # Take a string from the database and load it. We need a calendar!
        val = case value
        when ::String then calendar.schoolday(value)
        when ::SR::DO::SchoolDay then value
        else
          # fail
        end
      end

      def dump(value)
        # Store a SchoolDay value into the database as a string.
        case value
        when SR::DO::SchoolDay
          sd = value
          "Sem#{sd.semester} #{sd.weekstr} #{sd.day}"
        when ::String
          value
        else
          # fail
        end
      end

      def typecast(value)
        # I don't know what this is supposed to do -- that is, when and why it
        # is called -- but I am aping the behaviour of the Regexp custom type,
        # which, like this one, stores as a String and loads as something else.
        load(value)
      end

      # private methods calendar() and error_message() omitted
    end
  end
end

This code works for reading from the (SQLite) database, but not for creating new rows. The error message is:

Schoolday must be of type String

The code that defines the DataMapper resource and tries to create the record is:

class LessonDescription
  include DataMapper::Resource
  property :id,          Serial
  property :schoolday,   SchoolDay  # "Sem1 3A Fri"
  property :class_label, String     # "10"
  property :period,      Integer    # (0..6), 0 being before school
  property :description, Text       # "Completed yesterday's worksheet. hw:(4-07)"
end

# ...

ld = LessonDescription.create(
  schoolday:    @schoolday,
  class_label:  @class_label,
  period:       @period,
  description:  description
)

Here is the code for the Regexp datamapper type in the dm-types library. It's so simple!

module DataMapper
  class Property
    class Regexp < String
      load_as ::Regexp                          # NOTE THIS LINE

      def load(value)
        ::Regexp.new(value) unless value.nil?
      end

      def dump(value)
        value.source unless value.nil?
      end

      def typecast(value)
        load(value)
      end

    end
  end
end

For some reason, I cannot use the load_as line in my code.

To summarise: I am trying to create a custom type that translates between a SchoolDay (domain object) and a String (database representation). The translation is easy, and I've copied the code structure primarily from the DataMapper Regexp type. But when I try to save a SchoolDay, it complains that I'm not giving it a string. Frustratingly, I can't use the "load_as" method that the built-in and custom types all use, even though I have the latest gem. I can't find the "load_as" method defined anywhere in the source code for DataMapper, either. But it's called!

Sorry for the ridiculous length. Any help would be greatly appreciated, as would a pointer to a guide for creating these things that I have somehow missed.

like image 293
nosedog Avatar asked Dec 29 '25 20:12

nosedog


1 Answers

It seems that the current code of dm-types at github hasn't made it to any official release -- that's why load_as doesn't work in your example. But try to add this method:

module DataMapper
  class Property
    class SchoolDay < DataMapper::Property::String

      def custom?
        true
      end

    end
  end
end

That's working here.

like image 90
Adiel Mittmann Avatar answered Jan 01 '26 20:01

Adiel Mittmann