Table of Contents

Wejscie i wyjscie

Klawiatura

Pliki

File.open("text.txt").each { |line| puts line }
 
File.new("text.txt", "r").each { |line| puts line }

Obydwa polecenia otwieraja plik do odczytu (File.open - domyslnie, File.new poprzez “r”). Ponadto File.open moze przyjac blok kodu, a gdy dzialanie tego sie zakonczy, zamknie plik, co nie dzieje sie w przypadku drugiej metody (trzeba skorzystac z metody close).

File.open("text.txt") do |f|
    puts f.gets
end
 
f = File.new("text.txt", "r")
puts f.gets
f.close

Korzystajac z pierwszej opcji, mamy szybki i prosty sposob na przeprowadzenie operacji w pojedynczej lokalizacji. W drugim przypadku, udostepniamy referencje szerszej “publice”.

# writing.rb
 
File.open("test.txt", "w") do |f|
    f.puts "This is a test"
end

Output:

sqbell@sqbell-gentoo ~/ruby/beginning_ruby/ch09_files_db $ cat test.txt 
This is a test
Tryby Wlasnosci
r Tylko do odczytu, wskaznik na poczatku pliku
r+ Odczyt i zapis, wskaznik na poczatku pliku
w Tylko zapis, nowy plik jest tworzony (istniejace sa zastepowane)
w+ Zapis i odczyt, File.new tworzy nowy plik (istniejacy jest zastepowany)
a Zapis w trybie dolaczania do pliku, wskaznik jest umieszczony na koncu pliku
a+ Zapis i odczyt w trybie dolaczania, wskaznik umieszczony jest na koncu pliku
b Tryb binarny, do odczytu plikow nietekstowych
# logging.rb
 
f = File.new("logfile", "a")
f.puts Time.now
f.close

Output:

sqbell@sqbell-gentoo ~/ruby/beginning_ruby/ch09_files_db $ cat logfile 
2011-05-06 13:10:29 +0200
2011-05-06 13:10:30 +0200
2011-05-06 13:10:31 +0200
2011-05-06 13:10:32 +0200

Blokady

W systemach UNIXowych mamy dwa rodzaje blokad: współdzielona (shared) i na wyłączność ((exclusive). Druga może być w posiadaniu tylko przez jeden proces. Procesy współdzielące plik muszą najpierw zwlonić blokadę, by proces, który chce posiadać plik na wyłączność będzie mógł taką założyć. Przykład w Rubym:

#!/usr/bin/env ruby -wKU
 
File.open("/tmp/foo", "w") do |f|
  f.flock(File::LOCK_EX)
  f.puts "Something clever..."
  f.flock(File::LOCK_UN)
end

Katalogi

Tworzenie plikow tymczasowych

Wiekszosc systemow operacyjnych posiada katalogi, w ktorych programy moga przechowywac tymczasowe dane.

# tmp_dir.rb
 
require 'tmpdir'
 
puts Dir.tmpdir
 
tempfilename = File.join(Dir.tmpdir, "myapp.dat")
tempfile = File.new(tempfilename, "w")
tempfile.puts "This is only temporary."
tempfile.close
 
File.delete(tempfilename)
 
# poprzez biblioteke tempfile
require 'tempfile'
 
f = Tempfile.new('myapp')
f.puts "Hello"
puts f.path
f.close

Output:

/tmp
/tmp/myapp20110506-5927-3wba79

Strony kodowe

Aby okreslic kodowanie wczytywanego pliku, dodajemy je do trybu pliku po dwukropku.

File.new("text.txt", "r:utf-8").each { |line| puts line }

Aby sprawdzic “zewnetrzne” kodowanie jakiegokolwiek obiektu I/O, skorzystac mozemy z metody external_encoding.

# ext_encoding.rb
 
puts File.open("logfile", "r:utf-8").external_encoding
puts File.open("logfile", "r").external_encoding

Output:

UTF-8
UTF-8

Mozna transkodowac wejscie z jednego kodowania w drugie:

# transcoding.rb
 
File.open("text.txt", "r:utf-8:iso-8859-2") do |f|
    puts f.external_encoding
    first_line = f.gets
    puts first_line.encoding
end

Output:

UTF-8
ISO-8859-2

Wlasnosc ta jest przydatna gdy chcemy w calym programie korzystac z jednego kodowania (na przyklad utf-8) ale pliki wejsciowe mamy w innym.

Podstawowe bazy danych

Bazy danych w plikach tekstowych

CSV

# csv.rb
 
require 'csv'
 
CSV.open('text.txt', 'r').each do |person|
    puts person.inspect
end

Output:

["Fred Bloggs", "Manager", "Male", "45"]
["Laura Smith", "Cook", "Female", "23"]
["Debbie Watts", "Professor", "Female", "38"]

Przechowywanie obiektow i struktur danych

PStore

PStore umozliwia zapisywanie obiektow i struktur danych do pliku i ladowania ich z powrotem. Takie operacje zwane sa object persistence i opieraja sie na technice zwanej marshalling, gdzie struktury sa splaszczane.

# pstore.rb
 
class Person
    attr_accessor :name, :job, :gender, :age
end
 
fred = Person.new
fred.name = "Fred Bloggs"
fred.age = 45
 
laura = Person.new
laura.name = "Laura Palmer"
laura.age = 23
 
require 'pstore'
 
store = PStore.new("storagefile")
store.transaction do
    store[:people] ||= Array.new
    store[:people] << fred
    store[:people] << laura
end
# pstore_read.rb
 
class Person
    attr_accessor :name, :job, :gender, :age
end
 
require 'pstore'
 
store = PStore.new("storagefile")
people = []
store.transaction do
    people = store[:people]
end
 
people.each do |person|
    puts person.name
end

Output:

Fred Bloggs
Laura Palmer

YAML

# yaml.rb
 
require 'yaml'
 
class Person
    attr_accessor :name, :age
end
 
fred = Person.new
fred.name = "Fred Bloggs"
fred.age = 45
 
laura = Person.new
laura.name = "Laura Smith"
laura.age = 23
 
test_data = [ fred, laura ]
 
puts YAML::dump(test_data)

Output:

--- 
- !ruby/object:Person 
  age: 45
  name: Fred Bloggs
- !ruby/object:Person 
  age: 23
  name: Laura Smith

Wczytywanie:

# yaml_read.rb
 
require 'yaml'
 
class Person
    attr_accessor :name, :age
end
 
yaml_string = <<END_OF_DATA
--- 
- !ruby/object:Person 
  age: 45
  name: Fred Bloggs
- !ruby/object:Person 
  age: 23
  name: Laura Smith
END_OF_DATA
 
test_data = YAML::load(yaml_string)
puts test_data[0].name
puts test_data[1].name

Output:

Fred Bloggs
Laura Smith

Relacyjne bazy danych i SQL

SQLite

# sqlite.rb
 
require 'rubygems'
require 'sqlite3'
 
$db = SQLite3::Database.new("dbfile")   # $ - zmienna globalna
$db.results_as_hash = true              # wymusza zwracanie wynikow jako hasha
 
def disconnect_and_quit
    $db.close
    puts "Bye!"
    exit
end
 
def create_table
    puts "Creating 'people' table"
    $db.execute %q{CREATE TABLE people (
                    id integer primary key,
                    name varchar(50),
                    job varchar(50),
                    gender varchar(6),
                    age integer)
    }
end
 
def add_person
    puts "Enter name: "
    name = gets.chomp
    puts "Enter job: "
    job = gets.chomp
    puts "Enter gender: "
    gender = gets.chomp
    puts "Enter age: "
    age = gets.chomp
 
    $db.execute("INSERT INTO people (name, job, gender, age) VALUES (?, ?, ?, ?)", name, job, gender, age)
end
 
def find_person
    puts "Enter name or ID of person to find: "
    id = gets.chomp
 
    person = $db.execute("SELECT * FROM people WHERE name = ? OR id = ?", id, id.to_i).first
 
    unless person
        puts "No result found."
        return
    end
 
    puts %Q{Name: #{person['name']}
        Job: #{person['job']}
        Gender: #{person['gender']}
        Age: #{person['age']}
    }
end

MySQL

# mysql.rb
 
require 'rubygems'
require 'mysql'
 
db = Mysql.connect('localhost', 'root', 'test')
 
db.query("INSERT INTO people (name, age) VALUES('Chris', 25)")
 
begin
    query = db.query('SELECT * FROM people')
 
    puts "There were #{query.num_rows} rows returned."
 
    query.each_hash do |h|
        puts h.inspect
    end
 
rescue
    puts db.errno
    puts db.error
end
 
db.close

DBI

DBI (The DataBase Interface) to proba utworzenia jednego interfejsu do porozumiewania sie z roznymi bazami danych.

# dbi.rb
 
require 'dbi'
 
db = DBI.connect('DBI:Mysql:db_name', 'username', 'password')
 
db.do("INSERT INTO people (name, age) VALUES (?, ?)", name, age)
 
query = db.prepare('SELECT * FROM people')
query.execute
 
while row = query.fetch do
    puts row.inspect
end
 
query.finish
 
db.select_all('SELECT * FROM people') do |row|
    puts row.inspect
end