1

我是 Ruby 的新手,甚至是 DataMapper 的新手,或者任何 ORM 的新手。我来自 Perl 背景,并不是真正的 OOP 类型的开发人员。所以我在这里徘徊在未知的领域。为这个冗长的问题道歉......

在这个项目中,我有设备类和设备的概念,它们将映射到设备类下。设备类需要能够有子设备类。通用的根设备类名称(换句话说,所有设备类来自的根)称为“FOO”或“BAR”(在此示例代码中),每个设备类都可以具有任意一组子设备类。最后,设备类最终包含设备。

所以: Deviceclasses 有很多 deviceclasses Deviceclasses 有很多设备 一个 deviceclass 有一个 deviceclass_name 许多设备属于一个 deviceclass

所以,即:

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:是否有一些神奇的方法来关联设备类和设备,或者我需要通过获取设备的 id 并通过更新将其关联到关联的设备类来以艰难的方式来做到这一点?

问题3:有没有一种方法可以在迁移表后向模型添加一个属性,这可以通过添加 :default 来有效地更改表(参见上面的失败案例)。如果没有,有什么方法可以在创建模型期间获得我的默认值。我想到了 Lambda,但这只有在表已经存在并且 ROOT_DEVICENAMES 已经添加时才有效。

4

1 回答 1

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

我不确定使用验证来生成丢失的设备类是个好主意。如果初始数据不是不断变化,我会在启动时运行种子脚本。您可以使用dm-sweatshop之类的东西来播种数据库。

我想我需要更多关于#3 的详细信息,您是否想要一个 Deviceclass 的默认名称(您可以添加:default => 'foo'),您可能知道您:default已经在使用它了!:)

于 2013-03-21T09:19:50.240 回答