ruby wayの高度なデータアクセス


詳細
常に、より透明な方法でデータを格納し、得る必要がある.Marshalモジュールは簡単なオブジェクト持続化を提供し,PStoreライブラリはこの技術の上に構築されている.dbmライブラリはhashのように永久にディスクに格納.
1簡単なMarshaling
常にオブジェクトを作成し、後で使用するために保存する必要があります.rubyはこのようなオブジェクトの持続化(あるいはMarshaling)に基本的なサポートを提供した.Marshalモジュールは、プログラマにrubyオブジェクトをシーケンス化および逆シーケンス化することができる.
# array of elements [composer, work, minutes]
    works = [["Leonard Bernstein","Overture to Candide",11],
         ["Aaron Copland","Symphony No. 3",45],
         ["Jean Sibelius","Finlandia",20]]
# We want to keep this for later...
File.open("store","w") do |file|
  Marshal.dump(works,file)
end

# Much later...
File.open("store") do |file|
  works = Marshal.load(file)
end

ここで注意すべきは、この技術はすべての対象がdumpされるわけではない.オブジェクトに低レベルのクラスのオブジェクトが含まれている場合、IO、Proc、Bindingなどのdumpは使用できません.単一のオブジェクト、匿名のオブジェクト、モジュールもシーケンス化できません.
Marshal.dumpには、呼び出し時にパラメータが入力されると、文字列で表されるデータが返されます.最初の2バイトはプライマリ・バージョン番号とセカンダリ・バージョン番号です.
こちらでは1.9と1.8の結果が違います.
s = Marshal.dump(works)
p s[0]  #  4
p s[1]  #  8

上記の結果は1.8で印刷された結果ですが、1.9では004とbになります.つまり、バージョン番号はありません.
これらのデータをロードしたい場合は、プライマリ・バージョン番号が同じで、セカンダリ・バージョン番号が等しいか小さい場合にのみloadします.ruby解釈器がverboseパラメータを使用する場合、バージョン番号は完全に一致する必要があります.
3番目のパラメータlimitは、シーケンス化されたオブジェクトがネストされたオブジェクトである場合にのみ意味があります.それは遍歴時の深さです.オブジェクトのネストさがlimit以上である場合、ArgumentErrorが放出されます.
File.open("store","w") do |file|
  arr = [ ]
Marshal.dump(arr,file,0)      #   in `dump': exceed depth limit
                              #  (ArgumentError)
  Marshal.dump(arr,file,1)

  arr = [1, 2, 3]
Marshal.dump(arr,file,1)      # in `dump': exceed depth limit
                              # (ArgumentError)
  Marshal.dump(arr,file,2)

  arr = [1, [2], 3]
Marshal.dump(arr,file,2)      # in `dump': exceed depth limit
                              # (ArgumentError)
  Marshal.dump(arr,file,3)
end

File.open("store") do |file|
  p Marshal.load(file)          #  [ ]
  p Marshal.load(file)          #   [1, 2, 3]
  p Marshal.load(file)          #   arr = [1, [2], 3]
end

3番目のパラメータのデフォルト値は1,-1である、深さ検出を行わない.
2より複雑なMarshaling
いくつかの制限のため、シーケンス化をカスタマイズする必要がある場合があります.作成loadと_dumpメソッドはカスタマイズできます.この2つの方法は、シーケンス化が完了すると呼び出される.
次の例では、この人は生まれてから5%の利息を彼の初期口座で稼いだ.年齢と現在の残高は保存されていません.これは、時間の関数だからです.
class Person

     attr_reader :name
     attr_reader :age
     attr_reader :balance

     def initialize(name,birthdate,beginning)
       @name = name
       @birthdate = birthdate
       @beginning = beginning
       @age = (Time.now - @birthdate)/(365*86400)
       @balance = @beginning*(1.05**@age)
     end

     def marshal_dump
       Struct.new("Human",:name,:birthdate,:beginning)
       str = Struct::Human.new(@name,@birthdate,@beginning)
       str
     end

     def marshal_load(str)
       self.instance_eval do
         initialize(str.name, str.birthdate, str.beginning)
       end
     end

     # Other methods...

   end

   p1 = Person.new("Rudy",Time.now - (14 * 365 * 86400), 100)
   p [p1.name, p1.age, p1.balance]  # ["Rudy", 14.0, 197.99315994394]

   str = Marshal.dump(p1)
   p2  = Marshal.load(str)

   p [p2.name, p2.age, p2.balance]  # ["Rudy", 14.0, 197.99315994394]

オブジェクトを保存するときに年齢と現在の残高は計算されず、オブジェクトが再生するときに年齢と現在の残高が計算される.注意marshal_loadはすでにオブジェクトが存在すると仮定し、initializeを呼び出せばよい.
3 Marshalを使用して制限深いコピーを実行する.
rubyは深いコピーの操作がなくて、方法dupとcloneはあなたが想像したほどではないかもしれません.オブジェクトにネストされたオブジェクト参照が含まれる場合、Pick-Up-Sticksのゲームのようにcopy操作が実行される.
ここでは、Marshalを使用するために制限された深度コピーを提供しますが、Marshalには多くの制限があります.
def deep_copy(obj)
  Marshal.load(Marshal.dump(obj))
end

a = deep_copy(b)

4 PStoreを使用してより良い持続性を実現
PStoreライブラリは、ファイルベースのrubyオブジェクトの永続化ストレージを提供し、1つのPStoreオブジェクトはrubyオブジェクトのいくつかの階層を持つことができ、各階層には1つのkeyで識別されるrootがある.1つのトランザクションが開始されると、1つのディスク・ファイルから階層が読み出され、トランザクションが終了すると、その階層が書き戻されます.
require "pstore"

# save
db = PStore.new("employee.dat")
db.transaction do
    db["params"] = {"name" => "Fred", "age" => 32,
                    "salary" => 48000 }
end


# retrieve
require "pstore"
db = PStore.new("employee.dat")
emp = nil
db.transaction { emp = db["params"] }

通常、PStoreオブジェクトをトランザクション・コード・ブロックに直接転送できます.
トランザクションの進行中にcommitまたはabortメソッドで終了することができます.前者は私たちがした変化を維持し、後者は変化を放棄します.
require "pstore"

# Assume existing file with two objects stored
store = PStore.new("objects")
store.transaction do |s|

  a = s["my_array"]
  h = s["my_hash"]

  # Imaginary code omitted, manipulating
  # a, h, etc.

  # Assume a variable named "condition" having
  # the value 1, 2, or 3...

  case condition
    when 1
      puts "Oops... aborting."
      s.abort   # Changes will be lost.
    when 2
      puts "Committing and jumping out."
      s.commit  # Changes will be saved.
    when 3
      # Do nothing...
  end

  puts "We finished the transaction to the end."
  # Changes will be saved.

end

トランザクションでは、メソッドrootsを使用してルートの配列を返すこともできます.deleteメソッドを使用してrootを削除することもできます.
store.transaction do |s|
  list = s.roots          # ["my_array","my_hash"]
  if s.root?("my_tree")
    puts "Found my_tree."
  else
    puts "Didn't find # my_tree."
  end
  s.delete("my_hash")
  list2 = s.roots         # ["my_array"]
end

5 CSVデータの処理
ここでは主にFastercSVライブラリを紹介します.rubyに内蔵されているcsvライブラリよりも速いからです.
CSVモジュール(csv.rb)はcsvのフォーマットに従ってデータを生成または解析することができ、csvのフォーマットについては統一的な観点はない.FastercSVライブラリの作成者は、独自のcsvのフォーマットの基準を定義します.
参照
Record separator: CR + LF
Field separator: comma (,)
Quote data with double quotes if it contains CR, LF, or comma
Quote double quote by prefixing it with another double quote ("-> "")
Empty field with quotes means null string (data,"",data)
Empty field without quotes means NULL (data,,data)
カンマで分割されたデータを書くために、ファイルの作成から始めましょう.簡単に書き込みモードでファイルを開くことができます.
require 'csv'

CSV.open("data.csv","w") do |wr|
  wr << ["name", "age", "salary"]
  wr << ["mark", "29", "34500"]
  wr << ["joe", "42", "32000"]
  wr << ["fred", "22", "22000"]
  wr << ["jake", "25", "24000"]
  wr << ["don", "32", "52000"]
end

上のコードはdataを生成する.csvファイル.
参照
"name","age","salary"
"mark",29,34500
"joe",42,32000
"fred",22,22000
"jake",25,24000
"don",32,52000
次のプログラムは読み取りを担当します.
require 'csv'

CSV.open('data.csv', 'r') do |row|
  p row
end

# Output:
# ["name", "age", "salary"]
# ["mark", "29", "34500"]
# ["joe", "42", "32000"]
# ["fred", "22", "22000"]
# ["jake", "25", "24000"]
# ["don", "32", "52000"]

より高度な特性はruby-docを除去することができる.org見に行きます.
6 YAMLによるシーケンス化
YAMLの意味はYAML Ain't Markup Languageです.彼は柔軟で人に読むのに適したストレージフォーマットです.xmlと似ていますが、もっと美しいです.
require yamlを使用すると、各オブジェクトにto_が表示されます.yamlメソッド:
require 'yaml'

str = "Hello, world"

num = 237

arr = %w[ Jan Feb Mar Apr ]

hsh = {"This" => "is", "just a"=>"hash."}

puts str.to_yaml

puts num.to_yaml

puts arr.to_yaml

puts hsh.to_yaml

# Output:

# --- "Hello, world"

# --- 237

# ---

# - Jan

# - Feb

# - Mar

# - Apr

# ---

# just a: hash.

# This: is

to_yamlメソッドとYAML.loadの方法は逆に、私は彼に文字列やストリームをパラメータにすることができますか?
データがあるとしたらyamlのファイル:
参照
---
- "Hello, world"
- 237
-
  - Jan
  - Feb
  - Mar
  - Apr
-
  just a: hash.
  This: is
もし私たちが今loadというストリームを手に入れたら、私たちはさっきの配列を手に入れます.
require 'yaml'
file = File.new("data.yaml")
array = YAML.load(file)
file.close
p array
# Output:
# ["Hello, world", 237, ["Jan", "Feb", "Mar", "Apr"],
#  {"just a"=>"hash.", "This"=>"is"}]

7 Madeleineを使用したオブジェクトのPrevalence
一部の分野では、オブジェクトのPrevalenceが流行しており、主な観点は、メモリが安くなり、ますます安くなるが、データベースが小さいため、データベースを忘れ、すべてのオブジェクトをメモリに保持することである.
JAvaが実装したバージョンをPrevaylerと呼び、rubyに対応するバージョンをMadeleineと呼ぶ.
Madeleineはすべての人やすべてのプログラムに適用されるわけではありません.オブジェクトのprevalenceには独自のルールと制限があります.まず、すべてのオブジェクトをメモリにロードする必要があります.次に、すべてのオブジェクトをシーケンス化する必要があります.
すべてのオブジェクトは確定しなければならない、すなわち数学の関数に似ていて、入力も同じで、出力も一定に同じである(これはsystem clockや乱数を使用することはできないことを意味する).
オブジェクトは、可能な限りすべてのIOから分離すべきである、すなわち、一般にprevalenceシステムの外でこれらのIO動作を呼び出す.
最後に、prevalenceシステムを変更する各コマンドは、コマンドオブジェクトの形式で発行する必要がある.
Madeleineを研究したいなら、自分で資料を探すしかない.
8 DBMライブラリの使用
dbmは、文字列ベースのハッシュ・ファイル・ストレージ・メカニズムのプラットフォームに関連しています.keyとこのkeyに関連付けられたデータを格納します.文字列です.
例を見てみましょう.
require 'dbm'

d = DBM.new("data")
d["123"] = "toodle-oo!"
puts d["123"]        # "toodle-oo!"
d.close

puts d["123"]        # RuntimeError: closed DBM file

e = DBM.open("data")
e["123"]                # "toodle-oo!"
w=e.to_hash                # {"123"=>"toodle-oo!"}
e.close

e["123"]                # RuntimeError: closed DBM file
w["123"]                # "toodle-oo!

DBMクラスmixはEnumerableモジュールで、彼のクラスメソッドnewとopenはsingletonsです.
q=DBM.new("data.dbm")   #
f=DBM.open("data.dbm")  # Errno::EWOULDBLOCK:
                        #   Try again - "data.dbm"

操作dbmオブジェクトと操作hashオブジェクトの差は多くなく、その具体的な方法を知りたい場合はドキュメントを見ることができる.