2013-03-20 53 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