续集(DRY)

时间:2017-04-18 02:07:03

标签: ruby orm sequel

我正在使用续集数据库后端构建一个sinatra Web应用程序。此应用程序的主要任务是收集来自不同机器人的状态消息,将它们存储在数据库中并提供各种方法来查看它们。这些消息的一个共同点是,它们提供纬度/经度的WGS84位置。

现在我想提供各种过滤器来根据它们的位置查询消息,但是我想只编写一次这些过滤器,只测试一次,但是在带有lat / lon条目的所有模型类中重复使用它们。 / p>

将其归结为一个非常简单的例子:

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

在两个模型类中都有north_of(lat)来过滤来自给定纬度以北的消息。这个功能相当简单,您可以轻松地重复两到三次,但更复杂的情况呢?

我使用dataset_module之外的模块玩了一下,但似乎没什么。

是否有一种首选方法可以在不同型号上重复使用滤镜?我搜索了很多,但没有找到任何令人满意的答案。

修改

为了使我的问题更精确:我想将所有函数(如north_of(lat)(有一个 lot 更多))移动到服务类中。我现在想知道的是将该服务类集成到续集模型中的最佳方法:

  • “只是”包括它?
  • 扩展dataset_module,若然,怎么做?
  • 编写数据集插件?
  • ...

3 个答案:

答案 0 :(得分:1)

以下是Bob叔叔的Screaming Architecture博文的链接,可能会有所帮助。

现在,在回答您的问题时,似乎north_of以及许多其他方法实际上是您的域逻辑的一部分。这个逻辑不应该用于持久性抽象,控制器或视图等。

设计,构建和编写用于解决问题域语言问题的对象集的测试。然后,您将拥有一组丰富的功能,您可以在ModelController,CLI等上使用这些功能。

我通常将我的服务对象放在lib/目录中并编写简单的单元测试,而没有任何设置测试数据库的持久性样板。他们通常也跑得很快。

答案 1 :(得分:1)

您可以将现有模块传递给dataset_module

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

答案 2 :(得分:1)

作为后续:我采取了@ jeremy-evans answer并通过模块的参数化方案对其进行了扩展。所以从现在开始,我可以通过模拟测试我的过滤器,我的模型类只有一个包含在他们的dataset_module中的列表。

我喜欢它。

作为解释我稍加修改的例子:

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       :gps_lat
        Float       :gps_lon
        Integer     :chargestate
    end
  end
end

module GPSFilter
    def self.create(lat_name, lon_name)
        Module.new do
            include GPS

            define_method :lat_col_name do
                lat_name
            end

            define_method :lon_col_name do
                lon_name
            end
       end
    end

    def north_of(lat)
        where( "#{lat_col_name} > #{lat}" )
    end

    ##### default parameters #####
    def lon_col_name
        "lon"
    end

    def lat_col_name
        "lat"
    end
end

class Auvmessage < Sequel::Model
  dataset_module do
     include GPSFilter
  end
end

class Asvmessage < Sequel::Model
  dataset_module do
     include GPSFilter.create :gps_lat, :gps_lon
  end
end