2013-03-20 70 views
1

我是Ruby的新成員,甚至是DataMapper的新成員,或者任何針對此問題的ORM。我來自Perl背景,並不是真正的OOP類型的開發人員。所以我在這裏遊蕩在未知的領域。對於長期問題的歉意......Ruby DataMapper:模型關聯評論,完成後的模型更新以及關聯稍微自主的記錄

在這個項目中,我有設備類和設備的概念,這些設備類將被映射到deviceclasses下。設備類需要能夠擁有子設備類。通用根設備類名稱(換句話說,所有設備類來自的根)稱爲「FOO」或「BAR」(在此示例代碼中),並且每個設備類都可以有一組任意子設備類。最後,設備類最終包含設備。

所以: Deviceclasses有很多deviceclasses Deviceclasses有許多設備 一個設備類的有一個deviceclass_name 許多設備屬於設備類的

因此,IE:

FOO 
    JOHNSHOUSE 
     UPSTAIRS 
      device1 
      device2 
     DOWNSTAIRS 
      device1 
      device2 
    MANYHOUSES 
     JOE 
      GARAGE 
       device1 
       device2 
     SUZY 
      BEDROOM 
       device1 
       device2 
       device3 
     TIM 
      LIVINGROOM 
       device1 
    ARBITRARY 
     device1 
     SOMEPLACE 
      device1 
      device2 
BAR 
    ENGLAND 
     LONDON 
      MYHOUSE 
       BEDROOM 
        device1 
        device2 
        device3 

這裏的地方我卡住...設備和設備類必須能夠自動添加到數據庫並且它們的關聯將在稍後執行。所以,我不能這樣做

deviceclass = MyDB::Deviceclass.new 
device  = MyDB::Device 
deviceclass.device.new(blah) 

我的模塊,它包含了相關的模型,其中我立足本組問題...

問題1 - 我這樣做對嗎?請注意,Deviceclass下的self.validate_root_deviceclasses方法。我必須先在數據庫中安裝n根設備類,然後才能使用此方法創建它們。不幸的是,屬性更新不起作用。我會喜歡這方面的一些方向。

module DeviceDB 
    ROOT_DEVICECLASSES %w{FOO BAR} 

    class Deviceclass 
     include DataMapper::Resource 
     property :id, Serial 
     property :hw_id, String,    :unique => true 
     property :root_deviceclass, Boolean, :default => false 
     property :parent_deviceclass_id, Integer 
     property :deviceclass_name, String 
     property :updated_at, DateTime 
     property :created_at, DateTime 

     has n, :devices,  :through => Resource 
     has n, :deviceclasses, :through => Resource 
     has 1, :deviceclass, self, {:through=>:deviceclasses, :via=>:parent_deviceclass_id} 

     def self.validate_root_deviceclasses 
      root_deviceclasses = all(:root_deviceclass => true) 

      if root_deviceclasses.count > 0 
# get whats in the db now 
       db  = Array.new(root_deviceclasses.map(&:deviceclass_name)) 
# match it against the global list (top of this file) 
       missing = ROOT_DEVICECLASSES.map{|root| root unless db.grep(/#{root}/i)[0]}.compact 

# if something's missing, add it. 
       missing.each do |missing| 
        begin 
         create(:deviceclass_name => missing, :root_deviceclass => true).save 
        rescue DataMapper::SaveFailureError => e 
         @error = [e.resource.errors.map{|err| err}].join(', ') 
         return(false) 
        end 
       end 
      else 
       begin 
        ROOT_DEVICECLASSES.each do |root| 
         create(:deviceclass_name => root, :root_deviceclass => true).save 
        end 
       rescue DataMapper::SaveFailureError => e 
        @error = [e.resource.errors.map{|err| err}].join(', ') 
        return(false) 
       end 
      end 

      begin 
       default = first(:deviceclass_name => 'PTS').id 

       property :parent_deviceclass_id, Integer, :default => default # fail 
       DataMapper.finalize           # fail 
       return(self) 
      rescue DataMapper::SaveFailureError => e 
       @error = [e.resource.errors.map{|err| err}].join(', ') 
      end 

      return(true) 
     end 
    end 

    class Device 
     include DataMapper::Resource 
     property :id, Serial 
     property :deviceclass_id, Integer 
     property :device_id, String, :unique => true 
     property :devicename, String 
     ... more properties... 
     property :updated_at, DateTime 
     property :created_at, DateTime 

     belongs_to :deviceclass, :required => false 
    end 

    DataMapper.finalize 
    DataMapper.auto_upgrade! 

    Deviceclass.validate_root_deviceclasses 
end 

問題2:有沒有到deviceclasses和設備相關聯還是需要通過抓取設備的ID,並通過更新到相關的設備類相關聯,它做它的強硬的方式有些神奇的方式?

問題3:有沒有辦法在表已遷移之後向模型中添加屬性,以便通過添加以下內容來有效更改表:default(請參閱上面的失敗案例)。如果沒有,有什麼方法可以在創建模型期間獲得我的默認值。可以想到Lambda,但只有當表已經存在並且已經添加了ROOT_DEVICENAMES時纔可以使用。

回答

1

對於關於模式和自主添加項目的第一個問題,您可能會考慮使用dm-is-tree作爲您的Deviceclass,它會爲您做很多工作。以下是創建項目和「稍後」添加關聯項目的示例。

require 'rubygems' 
require 'data_mapper' 
require 'dm-is-tree' 

# setup 
DataMapper::Logger.new($stdout, :debug) 
DataMapper.setup(:default, 'sqlite::memory:') 

# models 
class Deviceclass 
    include DataMapper::Resource 

    property :id,   Serial 
    property :hw_id,  String, :unique => true 
    property :name,  String 
    property :updated_at, DateTime 
    property :created_at, DateTime 

    is :tree, :order => :name 
    has n, :devices 
end 

class Device 
    include DataMapper::Resource 

    property :id,    Serial 
    property :device_class_id, Integer 
    property :name,    String 
    property :updated_at,  DateTime 
    property :created_at,  DateTime 

    belongs_to :deviceclass, :required => false 
end 

# go! 
DataMapper.finalize 
DataMapper.auto_upgrade! 

# make the root deviceclass 
parent = Deviceclass.create(:name => "Root") 

# later on make a child 
child = Deviceclass.create(:name => "Child") 
# and add it to the parent 
parent.children << child 

# again later, create some devices 
d1 = Device.create(:name => "D1") 
d2 = Device.create(:name => "D2") 

# add them 
parent.devices << d1 
child.devices << d2 

# get stuffs 
puts parent.children 
puts child.root 
puts parent.devices 
puts child.devices 

我不確定使用驗證來生成缺少的Deviceclasses是個好主意。如果初始數據不是經常變化,我會在啓動時運行種子腳本。你可以使用類似dm-sweatshop的東西來播種數據庫。

我想我需要關於#3的更多細節,您是否希望Deviceclass的默認名稱(您可以添加:default => 'foo'),您可能已經知道,因爲您已經使用:default! :)

+0

不錯!我將檢查dm-is-tree和dm-sweatshop模塊。感謝這些指針!至於#3,我基本上試圖完成的是將默認記錄插入表中,其中:default將從屬性的其中一個記錄中派生:parent_deviceclass_id。在這方面有什麼額外的想法? – Jim 2013-03-22 17:19:35