国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

RAILS中利用YAML文件完成數(shù)據(jù)對接

fasss / 614人閱讀

摘要:最近在做的項目中,需要將遠(yuǎn)程數(shù)據(jù)庫中的數(shù)據(jù)對接到項目數(shù)據(jù)庫中,但是遠(yuǎn)程的數(shù)據(jù)不僅數(shù)據(jù)表名跟字段命名奇葩,數(shù)據(jù)結(jié)構(gòu)本身跟項目數(shù)據(jù)結(jié)構(gòu)出入比較大,在數(shù)據(jù)導(dǎo)入過程中代碼經(jīng)歷了幾次重構(gòu),最后使用了文件解決了基本數(shù)據(jù)對接的問題。

最近在做的Ruby on Rails項目中,需要將遠(yuǎn)程數(shù)據(jù)庫中的數(shù)據(jù)對接到項目數(shù)據(jù)庫中,但是遠(yuǎn)程的數(shù)據(jù)不僅數(shù)據(jù)表名跟字段命名奇葩,數(shù)據(jù)結(jié)構(gòu)本身跟項目數(shù)據(jù)結(jié)構(gòu)出入比較大,在數(shù)據(jù)導(dǎo)入過程中代碼經(jīng)歷了幾次重構(gòu),最后使用了YAML文件解決了基本數(shù)據(jù)1對接的問題。在此寫一篇博文,我會盡量重現(xiàn)一路過來的代碼變更,算是分享一下我的思考過程,也算是祭奠一下自己的苦逼歲月。

假設(shè)以及數(shù)據(jù)結(jié)構(gòu)預(yù)覽

因為遠(yuǎn)程數(shù)據(jù)庫服務(wù)器為Oracle Server,我在項目中使用到了Sequel這個gem用于連接數(shù)據(jù)庫以及數(shù)據(jù)查詢,因為數(shù)據(jù)庫連接的內(nèi)容不是本文的重點,故后續(xù)代碼直接用remote_database表示數(shù)據(jù)庫連接,而根據(jù)Sequel的用法,我們可以直接使用remote_database[table_name]連接到具體的表。

本次需要從遠(yuǎn)程數(shù)據(jù)庫中導(dǎo)入的基本數(shù)據(jù)主要有學(xué)生信息表(包含班級名稱)、老師信息表以及專業(yè)信息表,相應(yīng)地,項目中(以下稱為“本地”)也已經(jīng)創(chuàng)建好了對應(yīng)的model。其中學(xué)生信息表的表名以及部分?jǐn)?shù)據(jù)字段的從本地到遠(yuǎn)程的映射關(guān)系如表所示:

表名或字段名 本地 遠(yuǎn)程
表名 students XSJBXX
姓名 name XM
學(xué)號 number XH
年級 grade NJ
班級 belongs_to :klass ??? BJMC(班級名稱)

老師信息表的表名以及部分?jǐn)?shù)據(jù)字段的映射關(guān)系為:

表名或字段名 本地 遠(yuǎn)程
表名 teachers JZGJBXX
姓名 name XM
職稱 title ZC
證件號碼 id_number ZJHM
數(shù)據(jù)對接第一版:屬性方法顯式賦值

第一個導(dǎo)入的數(shù)據(jù)表是學(xué)生的信息表,在最開始的時候,因為只需要考慮一張多帶帶的表,所以代碼寫得簡單粗暴,基本過程就是:根據(jù)需要的信息,查詢對應(yīng)的遠(yuǎn)程數(shù)據(jù)字段,然后使用屬性方法賦值,最后保存接入的數(shù)據(jù)。對接方法的部分相關(guān)代碼示例(為了方便閱讀以及保護項目敏感信息,本文對項目中原有代碼進行了縮減以及修改):

# app/models/student.rb
class Student < ActiveRecord::Base
  def import_data_from_remote
    remote_students = remote_database[:xsjbxx].page(page)

    remote_students.each do |remote_student|
      name, number, grade = *remote_student.values_at(:xm, :xh, :nj)
      class_name = remote_student[:bjmc]

      klass = Klass.find_or_create_by name: class_name
      student = Student.find_or_create_by name: name,
                                          number: number,
                                          grade: grade,
                                          klass: klass
    end
  end
end

上面的代碼,呃,中規(guī)中矩,基本體現(xiàn)了各取所需的指導(dǎo)思想,但是總覺得怎么有點不好呢?

數(shù)據(jù)對接第二版:通過本地到遠(yuǎn)程數(shù)據(jù)庫字段映射關(guān)系自動匹配賦值

在第一版的代碼中,最大的壞味道在于:代碼中需要把所有需要對接的字段列舉出來,一旦遇到字段增刪修改的情況,就需要同時更新原來的邏輯代碼,太不靈活了,而且列舉所有字段本身就是一件非常繁瑣枯燥的事情。再假設(shè)字段很多的情況下,要從代碼中一個個檢查字段的名稱,肯定是件多么可怕的事情啊。

那么怎么修改呢?用映射表!仔細(xì)觀察第一段的代碼,其實代碼所做的工作如此簡單:無非是先從遠(yuǎn)程數(shù)據(jù)中取值,然后賦值到本地數(shù)據(jù)對象的對應(yīng)屬性中,這種“本地-遠(yuǎn)程”的字段映射關(guān)系,不就是我們每天面對的“鍵-值”對的特征嗎?那直接用一個Hash來保存這種對應(yīng)關(guān)系不就好了。

話不多說,我們開始重構(gòu):

# app/models/student.rb
class Student < ActiveRecord::Base
  LOCAL_TO_REMOTE_FIELDS_MAP = {
    number: :xh,
    name: :xm,
    age: :nj
  }

  LOCAL_TO_REMOTE_ASSOCIATION_MAP = {
    klass: {
      association_field_name: :name,
      remote_field_name: :bjmc
    }
  }

  def import_data_from_remote
    remote_students = remote_database[:xsjbxx].page(page)

    remote_students.each do |remote_student|
      student = Student.find_or_initialize_by xxx: xxx
      LOCAL_TO_REMOTE_FIELDS_MAP.keys.each do |attribute|
        # 逐一調(diào)用屬性賦值方法,完成Student屬性的賦值
        student.send("#{attribute}=", remote_student[LOCAL_TO_REMOTE_FIELDS_MAP[attribute]])
      end

      LOCAL_TO_REMOTE_ASSOCIATION_MAP.each do |association_name, association_fields_map|
        # 把遠(yuǎn)程數(shù)據(jù)賦給對應(yīng)的本地數(shù)據(jù)字段
        association_field_name = association_fields_map[:association_field_name]
        remote_value = remote_student[association_fields_map[:remote_field_name]]

        # 查找或創(chuàng)建關(guān)聯(lián)對象
        related_object =
          reflect_on_association(association_name).klass.find_or_create_by association_field_name => remote_value
        # 建立關(guān)聯(lián)關(guān)系
        local_object.send("#{association_name}=", related_object)
      end

      student.save
    end
  end
end

在上面的示例中,我們用常量LOCAL_TO_REMOTE_FIELDS_MAP保存Student這個model本身的字段跟遠(yuǎn)程數(shù)據(jù)字段的映射關(guān)系,這樣我們就可以通過類似LOCAL_TO_REMOTE_FIELDS_MAP[:number]知道學(xué)生的姓名在遠(yuǎn)程數(shù)據(jù)表中對應(yīng)的字段是:xm了。另外值得一提的是,我用了LOCAL_TO_REMOTE_ASSOCIATION_MAP這個常量保存了學(xué)生與班級關(guān)聯(lián)關(guān)系,同時保存了關(guān)聯(lián)的klass的數(shù)據(jù)字段映射關(guān)系。

在聲明了必要的字段映射關(guān)系之后,我就在代碼中遍歷了每一個字段,并且通過對應(yīng)的遠(yuǎn)程字段名稱查找對應(yīng)的數(shù)值,并且使用send方法調(diào)用了對象的屬性賦值方法,將數(shù)據(jù)自動對接到本地數(shù)據(jù)對象上。

到目前為止,代碼行數(shù)雖然反而多了,但是卻實現(xiàn)了字段映射關(guān)系與邏輯代碼的分離,我們可以獨立管理映射關(guān)系了。以后就算需要加入新的對接字段,只要在LOCAL_TO_REMOTE_FIELDS_MAP中添加新的鍵值對就好了,甚至可以在LOCAL_TO_REMOTE_ASSOCIATION_MAP添加類似klass的簡單關(guān)聯(lián)關(guān)系的數(shù)據(jù)接入,而這些都無需修改邏輯代碼。

數(shù)據(jù)對接第三版:教職工信息也需要導(dǎo)入了,代碼拷貝之旅開始了

毫無疑問,如果只是滿足于學(xué)生信息的對接,相信上面的代碼也都夠用了,代碼的重構(gòu)也可以告一段落了。

但是,前面說了,除了學(xué)生的信息,還有教職工的信息需要做接入,而且從最開始的假設(shè)以及數(shù)據(jù)結(jié)構(gòu)預(yù)覽一節(jié)看到,老師的數(shù)據(jù)結(jié)構(gòu)跟學(xué)生的數(shù)據(jù)結(jié)構(gòu)極其相似,所以,時間緊迫,我就直接拷貝代碼然后簡單刪改了一下:

# app/models/teacher.rb
class Teacher < ActiveRecord::Base
  LOCAL_TO_REMOTE_FIELDS_MAP = {
    number: :xh,
    title: :zc,
    id_number: :zjhm
  }

  def import_data_from_remote
    remote_teachers = remote_database[:jzgjbxx].page(page)

    remote_teachers.each do |remote_teacher|
      teacher = Teacher.find_or_initialize_by xxx: xxx
      LOCAL_TO_REMOTE_FIELDS_MAP.keys.each do |attribute|
        teacher.send("#{attribute}=", remote_teacher[LOCAL_TO_REMOTE_FIELDS_MAP[attribute]])
      end

      teacher.save
    end
  end
end

注意在上面的代碼中,Teacher中比起Student,少了LOCAL_TO_REMOTE_ASSOCIATION_MAP常量,并且也刪除了相關(guān)的代碼,雖然代碼已經(jīng)滿足需求了,教職工的數(shù)據(jù)導(dǎo)入也是無比順利,可是面對著一堆重復(fù)的代碼,真心別扭!

數(shù)據(jù)對接第四版:抽象邏輯,代碼共享

其實我多少也是有代碼潔癖的,大片Copy的代碼豈不是搞得自己逼格好Low?怎么可以忍受,繼續(xù)重構(gòu)!

這一次重構(gòu)其實就簡單多了,把重復(fù)的核心邏輯代碼抽取出來,然后放到一個專門負(fù)責(zé)數(shù)據(jù)對接的Concern里邊,最后在需要此concern的model里include一下就行了。話不多說,上Concern代碼:

# app/models/concerns/import_data_concern.rb
module ImportDataConcern
  extend ActiveSupport::Concern

  module ClassMethods
    def import_data_from_remote
      remote_objects = remote_database[self::REMOTE_TABLE_NAME].page(page)

      remote_objects.each do |remote_object|
        object = self.find_or_initialize_by xxx: xxx
        self::LOCAL_TO_REMOTE_FIELDS_MAP.keys.each do |attribute|
          # 逐一調(diào)用屬性賦值方法,完成Student屬性的賦值
          object.send("#{attribute}=", remote_object[self::LOCAL_TO_REMOTE_FIELDS_MAP[attribute]])
        end

        if self::LOCAL_TO_REMOTE_ASSOCIATION_MAP
          self::LOCAL_TO_REMOTE_ASSOCIATION_MAP.each do |association_name, association_fields_map|
            # 把遠(yuǎn)程數(shù)據(jù)賦給對應(yīng)的本地數(shù)據(jù)字段
            association_field_name = association_fields_map[:association_field_name]
            remote_value = remote_object[association_fields_map[:remote_field_name]]

            # 查找或創(chuàng)建關(guān)聯(lián)對象
            related_object =
              reflect_on_association(association_name).klass.find_or_create_by association_field_name => remote_value
            # 建立關(guān)聯(lián)關(guān)系
            local_object.send("#{association_name}=", related_object)
          end
        end

        object.save
      end
    end
  end
end

在上面的代碼中,我們把核心對接邏輯抽了出來,并且抽象了遠(yuǎn)程數(shù)據(jù)表名的配置,另外通過if self::LOCAL_TO_REMOTE_ASSOCIATION_MAP兼容關(guān)聯(lián)關(guān)系的導(dǎo)入。
為了在Teacher以及Student中正常運行上面的代碼,我們還需要在這兩個model分別include當(dāng)前的concern,并且聲明必要的常量:

# app/models/student.rb
class Student < ActiveRecord::Base
  include ImportDataConcern

  REMOTE_TABLE_NAME = "XSJBXX"
  LOCAL_TO_REMOTE_FIELDS_MAP = {
    number: :xh,
    name: :xm,
    age: :nj
  }

  LOCAL_TO_REMOTE_ASSOCIATION_MAP = {
    klass: {
      association_field_name: :name,
      remote_field_name: :bjmc
    }
  }
end
# app/models/teacher.rb
class Teacher < ActiveRecord::Base
  include ImportDataConcern

  LOCAL_TO_REMOTE_FIELDS_MAP = {
    number: :xh,
    title: :zc,
    id_number: :zjhm
  }
end

經(jīng)過上面的重構(gòu),原本重復(fù)的代碼已經(jīng)變成了一個Concern,通過Concern來管理獨立的業(yè)務(wù)邏輯,也使得代碼管理起來更方便了。但是,等等,我們的重構(gòu)之旅還在繼續(xù)!

數(shù)據(jù)對接第五版:砍掉惡心的常量,使用YAML配置映射關(guān)系

當(dāng)時在寫代碼的過程中,我就一直感覺一大堆的常量令人無法直視,但是,如果不用常量,我還能怎么做?盡管前面兩個表的數(shù)據(jù)導(dǎo)入任務(wù)完成了,我還是糾結(jié)于代碼中那惡心死了的常量(實際上,我當(dāng)時寫的常量比你們現(xiàn)在看到的更多,文章中的只不過是示例)。而慶幸的是,那天腦洞一開:“這些映射關(guān)系本質(zhì)上不就是一堆配置信息嗎?而我在代碼中的常量也就是用Hash存儲的,那用YAML文件不就剛好了嗎?”。是啊,像config/database.yml這類的文件,一直以來都是用于保存配置信息的啊,一個是符合Rails的使用習(xí)慣,另一個也確實符合數(shù)據(jù)結(jié)構(gòu)的要求。Awesome,這就開始動工。

首先第一件事,我就把那些常量搬到了yaml文件中,并且放在了項目的config/目錄下:

default:
  remote_unique_field_name: number

models:
  student:
    remote_table_name: xsjbxx
    local_to_remote_fields_map:
      number: xh
      name: xm
      grade: nj
    local_to_remote_association_map:
      klass:
        association_field_name: name
        remote_field_name: bjmc

  teacher:
    remote_table_name: jzgjbxx
    local_to_remote_fields_map:
      name: xm
      title: zc
      id_number: zjhm

配置好了yaml,那么又要如何方便地讀取配置信息呢?我的方法是在config/iniitializers/目錄下新建了一個initializer,主要用于在項目啟動時加載配置信息,關(guān)鍵代碼段:

module RemoteDatabase
  def self.fields_map
    return @fields_map if @fields_map

    @fields_map =
      YAML::load_file(Rails.root.join("config", "local_to_remote_oracle_database_map.yml"))
  end
end

所以,以后只要使用RemoteDatabase.fields_map就能讀取到所有數(shù)據(jù)字段映射關(guān)系了!

萬事俱備之后,我最后需要做的事情就是把Concern中的常量替換為從YAML中讀取到的配置就好了,重構(gòu)后的代碼為:

module ImportDataConcern
  extend ActiveSupport::Concern

  module ClassMethods
    def importing_fields_map
      return @fields_map if @fields_map

      @fields_map =
        RemoteDatabase.fields_map[:default].merge(
          RemoteDatabase.fields_map[:models][self.name.underscore]
        )
    end

    def import_data_from_remote
      remote_objects = remote_database[importing_fields_map[:remote_table_name]].page(page)

      remote_objects.each do |remote_object|
        # 通過值唯一的屬性查找對象
        remote_unique_field_name = importing_fields_map[:remote_unique_field_name]
        remote_unique_field = remote_object[importing_fields_map[:local_to_remote_fields_map][remote_unique_field_name]]
        local_object = find_or_initialize_by(remote_unique_field_name => remote_unique_field)

        local_to_remote_fields_map = importing_fields_map[:local_to_remote_fields_map]
        # 逐一設(shè)置本地對象需要對接的各個屬性
        local_to_remote_fields_map.keys.each do |attribute|
          local_object.send("#{attribute}=", remote_object[importing_fields_map[:local_to_remote_fields_map][attribute]])
        end

        # ... 關(guān)聯(lián)關(guān)系的保存

        next unless local_object.changes.any?

        local_object.save
      end
    end
  end
end

上面代碼中,importing_fields_map讀取與當(dāng)前Model匹配的字段映射關(guān)系,其內(nèi)部先通過RemoteDatabase.fields_map[:default]加載了默認(rèn)的配置,然后通過mergeRemoteDatabase.fields_map[:models][self.name.underscore]得到當(dāng)前model專屬的配置,其中的self.name.underscore的值類似于"student"或者"teacher"。

在后續(xù)的代碼中,基本跟前面列舉的代碼一致,只是將各種常量對應(yīng)替換為通過local_to_remote_fields_map存儲的配置,并且刪除Student以及Teacher的多余常量,在此就不列舉示例代碼了。

在整個重構(gòu)的過程中,代碼是越來越抽象的,但是代碼本身卻也因此變得越來越靈活,而至此,我們已經(jīng)完全將字段映射關(guān)系從Ruby代碼中剝離,假使以后還需要導(dǎo)入其他數(shù)據(jù),我們只需要修改YAML文件,而不再需要碰任何Ruby代碼,除非我們需要修改配置項的結(jié)構(gòu)。

收獲重構(gòu)后的果實:專業(yè)數(shù)據(jù)的導(dǎo)入

在經(jīng)歷過了幾次重構(gòu)后,今天開始導(dǎo)入學(xué)生專業(yè)的數(shù)據(jù),而我所需要做的全部事情,僅僅只是在yaml文件中加入專業(yè)相關(guān)的配置,并且在專業(yè)的modelMajorinclude一下數(shù)據(jù)導(dǎo)入的Concern就行了。整個過程幾分鐘就完成了,簡直絲般順滑??!

總結(jié)

最后簡單總結(jié)一下重構(gòu)完的代碼的特點吧:

避免了在model或者concern中生命一堆常量或者方法,到處定義的常量會讓映射關(guān)系的管理非常分散

避免不同命名空間下的同名常量,比如Student::LOCAL_TO_REMOTE_FIELDS_MAP以及Teacher::LOCAL_TO_REMOTE_FIELDS_MAP

更集中的字段映射關(guān)系配置,避免錯漏

邏輯跟映射關(guān)系解耦,更簡潔穩(wěn)健的代碼

自適應(yīng)新的數(shù)據(jù)表導(dǎo)入,不需要再修改或者添加Ruby代碼,配置即插即用

問題

如果涉及復(fù)雜關(guān)聯(lián),如何更好地擴展?
現(xiàn)在的數(shù)據(jù)對接是有限制的,就是數(shù)據(jù)本身比較規(guī)則,幾乎是一張表到一張表的對接,但是如果涉及一張表到多張表之間的對接,是否可以繼續(xù)再將以上代碼擴展?


說是基本數(shù)據(jù),是因為這篇文章介紹的方案目前僅針對數(shù)據(jù)關(guān)聯(lián)不是特別復(fù)雜的場景,而且介紹的場景,數(shù)據(jù)的導(dǎo)入也比較簡單,基本是從遠(yuǎn)程數(shù)據(jù)庫中取值,然后再直接賦值到項目數(shù)據(jù)庫的記錄中。對于需要在數(shù)據(jù)導(dǎo)入過程中做復(fù)雜的數(shù)據(jù)分析的案例,我暫時也沒有嘗試過,不過我預(yù)計可以嘗試使用Ruby中的代碼塊的方式解決,但是在此不贅述。??

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/17446.html

相關(guān)文章

  • 基于drone的CI/CD,對接kubernetes,見證靈活與自由,CI/CD對接kubernet

    摘要:所以我們選一個倉庫倉庫比較多,我這里選用,都行,根據(jù)需求自行選擇訪問端口,然后就沒有然后了功能沒有那么強大,不過占用資源少,速度快,我們穩(wěn)定運行了幾年了。 kubernetes集群三步安裝 CI 概述 用一個可描述的配置定義整個工作流 程序員是很懶的動物,所以想各種辦法解決重復(fù)勞動的問題,如果你的工作流中還在重復(fù)一些事,那么可能就得想想如何優(yōu)化了 持續(xù)集成就是可以幫助我們解決重復(fù)的代碼...

    iOS122 評論0 收藏0
  • 代碼級干貨 | 如何利用Docker與Rails API gem構(gòu)建微服務(wù)

    摘要:今天小數(shù)給大家?guī)淼氖且黄a級干貨文章,與大家分享一些利用以微服務(wù)形式設(shè)置應(yīng)用的經(jīng)驗與心得。為何選擇加在我效力的企業(yè)中,我們一直在利用為全部工程師構(gòu)建開發(fā)環(huán)境。運行命令,從而利用構(gòu)建鏡像并安裝。 今天小數(shù)給大家?guī)淼氖且黄a級干貨文章,與大家分享一些利用Rails API以微服務(wù)形式設(shè)置應(yīng)用的經(jīng)驗與心得。 為何選擇Docker加Rails API? 在我效力的企業(yè)中,我們一直在利用...

    stefanieliang 評論0 收藏0
  • 好雨云幫如何對接Git Server

    摘要:前言云幫目前支持對接或者主流代碼托管平臺的公開項目,后期會考慮接入其他類型的服務(wù)。對接通過應(yīng)用市場進行安裝安裝應(yīng)用由于依賴和,首先安裝和應(yīng)用。云平臺代碼,選擇自建,然后將授權(quán)添加應(yīng)用的列表里即可。 前言 云幫目前支持對接GitLab、Gogs、Github,或者主流代碼托管平臺的公開項目,后期會考慮接入其他類型的Git服務(wù)。 私有云 GitLab是一個用于倉庫管理系統(tǒng)的開源項目,私有云...

    dantezhao 評論0 收藏0
  • 前端速查表大全,分享一些技術(shù)和工具的簡明教程

    摘要:這個速查表主要是分享互聯(lián)網(wǎng)上一些比較常用的工具和技術(shù)常用內(nèi)容,如編輯器的快捷鍵的命令行的選擇器的屬性等,這個列表簡單收集了常用的工具,可以收藏用于平時的備忘錄,需要用到的時候可以及時查閱。 這個速查表主要是分享互聯(lián)網(wǎng)上一些比較常用的工具和技術(shù)常用內(nèi)容,如編輯器的快捷鍵、git的命令行、jQuery的API選擇器、CSS的flexbox屬性等,這個列表簡單收集了常用的工具,可以收藏用于平...

    xiaochao 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<