Search code examples
rubyormsequel

Same dataset-/filter-logic on different models in sequel (DRY)


I am building a sinatra web app with a sequel database backend. The primary tasks of this app is collecting status messages from different robots, store them in a database and provide various methods to view them. A common denominator in these messages is, that they provide a WGS84 position in lat/lon.

Now I want to provide various filters for querying messages based on their positions, but I want to write these filters only once, test them only once but re-use them in all model-classes with a lat/lon entry.

To boil it down to a very simple example:

Sequel.migration do
  up do
    create_table(:auvmessages) do
        primary_key :id
        Float       :lat
        Float       :lon
        String      :message
    end

    create_table(:asvmessages) do
        primary_key :id
        Float       :lat
        Float       :lon
        Integer     :chargestate
    end
  end
end

class Auvessage < Sequel::Model
  dataset_module do
    def north_of(lat)
        self.where{ latitude > lat}
    end
  end
end

class Asvessage < Sequel::Model
  dataset_module do
    def north_of(lat)
        self.where{ latitude > lat}
    end
  end
end

In both model classes have north_of(lat) to filter for messages which originate north of a given latitude. This function is fairly simple and you can easily repeat it two or three times, but what about more complex cases?

I have played around a bit with modules outside of dataset_module but nothing seem to be right.

Is there a preferred way how to re-use filters over different models? I have searched a lot, but didn't find any satisfying answer.

Edit:

To make my question a bit more precise: I want to move all functions like north_of(lat) (there are a lot more) into a service class. What I want to know now, is the best way to integrate that service class into a sequel-model:

  • "Just" include it?
  • Extend dataset_module, and if so, how?
  • Writing a dataset-plugin?
  • ...

Solution

  • You can pass an existing module to dataset_module:

    module NorthOf
      def north_of(lat)
        where{latitude > lat}
      end
    end
    Auvessage.dataset_module NorthOf
    Asvessage.dataset_module NorthOf