Ruby - wprowadzenie, część 6 - walidacja

czwartek, 16 marzec 2006, w kategoriach: Programowanie, Ruby

Ostatni odcinek zakończyliśmy w miarę już działającym programem. Niestety, daleko jest mu do funkcjonalności w stosunku do swojej długości, a w dodatku jest zabugowany. Dziś postaramy się doprowadzić do postaci, za którą nie będzie się trzeba wstydzić.

Po pierwsze, program nie dokonuje żadnego sprawdzenia, czy dane podane przez użytkownika są poprawne, nawet wtedy, kiedy nakazuje mu wprowadzenie ich w takim i takim formacie np. przy podawaniu terminu ukończenia zadania. Chcielibyśmy więc wprowadzić jakiś ogólny sposób walidacji danych wprowadzonych przez użytkownika. Popularnym rozwiązaniem tego problemu są wyrażenia regularne - znany większości programistów mechanizm opisywania wzorców. Używa się ich do wyszukiwania, dzielenia, zastępownia i ekstrahowania danych z łancuchów znaków (napisów). Skoro kazaliśmy więc użytkownikowi wprowadzić datę w formacie “dd.mm.yyyy”, należałoby sprawdzić czy rzeczywiście wprowadził on dwie cyfry, kropkę, kolejne dwie cyfry, kropkę i cztery cyfry. Wyrażenie regularne opisujące taki wzorzec wygląda tak:

/(d{2}).(d{2}).(d{4})/

Rozbudujmy więc metodę IO.prompt, o dodatkową funkcję - jeśli wprowadzony napis będzie pasował do danego wyrażenie regularnego, zostanie on przyjęty, a jeśli nie, program poprosi o ponownienie wprowadzenia danych. W rezultacie mamy coś takiego:

class IO
  def prompt(text, regexp=nil)
    puts text
    return gets.chop if(regexp==nil)
    while(true)
      input = gets.chop
      return input if(input =~ regexp)
      puts "Nieprawidlowe wejscie, sprobuj jeszcze raz"
    end
  end
end

“regexp=nil” w parametrach metody mówi nam, że domyślnie parametr regexp przybiera wartość nil, jeśli nie zostanie podany przez użytkownika metody. Dzięki takiemu rozwiązaniu zachowaliśmy kompatybilność starych wywołań, zmienić będziemy musieli jedynie te fragmenty kodu, w których bedziemy chcieli wprowadzić walidację danych.

Kolejna nieznana konstrukcja to operator “=~” (równa się + tylda). Sprawdza on, jak można się domyślać, czy dany napis pasuję do wzorca. Jeśli tak, zwraca prawdę, jeśli nie - fałsz.

Jeśli nie zostało podane wyrażenie regularne, po prostu zwracamy wejście, tak jak to miało miejsce przedtem. Jeśli jednak dokonujemy walidacji, zostaje wykonana pętla while, która zostaje przerwana dopiero wtedy, gdy wejście będzie pasować do zadanego wzorca. W przeciwnym razie do skutku będzie wyświetlany upierdliwy komunikat i pobierane będzie wejście od użytkownika.

Teraz pozostaje już tylko zmienić wywołanie metody IO.prompt w miejscu pobierania daty zakończenia zadania na:

zadanie.termin_ukonczenia = IO.prompt(
  "Podaj termin ukonczenia zadania (format dd.mm.yyyy): ",
  /(d{2}).(d{2}).(d{4})/)

I gotowe. Nauczyć się konstrukcji podstawowych wyrażeń regularnych można np. stąd, a potem pozostaje już tylko lektura książki, uważanej powszechnie za “biblię” tego tematu.

Druga usterka dotyczy konstrukcji klasy Zadanie. Termin ukończenia zadania uczyniliśmy bowiem napisem, a datę wprowadzenia - klasą. Taka niekonsekwencja to sygnał bardzo, bardzo złego stylu, tak więc naprawmy to, zmieniając metodą Zadanie.zbuduj:

def Zadanie.zbuduj
  zadanie = Zadanie.new
  zadanie.tresc = IO.prompt("Podaj tresc zadania: ")
  zadanie.termin_ukonczenia = IO.prompt(
    "Podaj termin ukonczenia zadania (format dd.mm.yyyy): ",
    /(d{2}).(d{2}).(d{4})/)
  now = DateTime.now
  tokens = [now.day, now.month, now.year]
  tokens.map! { |token| token.to_s.rjust(2, "0") }

  zadanie.data_wprowadzenia = "#{tokens[0]}.#{tokens[1]}.#{tokens[2]}";

  return zadanie
end

Tworzymy tablicę, zawierającą kolejne interesujące nas części obiektu DateTime - dzień, miesiąc, rok. Następnie używamy tajemniczego map!. Jest to konstrukcja znana z Lispa, zastępująca każdy element listy, wartością zwracaną przez funkcję wywołaną na tym elemencie. Wykrzyknik na końcu nazwy metody informuje nas, że jest ona destruktywna, czyli modyfikuje przekazany argument. Zwykłe map nie modyfikuje tablicy, a jedynie zwraca jej zmodyfikowaną kopię. Poniższe dwie konstrukcje są równoważne:

tokens = tokens.map { |token| token.to_s.rjust(2, "0") }
tokens.map! { |token| token.to_s.rjust(2, "0") }

Tak więc zamiast now.day, w tablicy mamy efekt wywołania now.day.to_s.rjust(2, “0″), zamiast now.month efekt wywołania now.month.to_s.rjust(2, “0″) itp. itd. Metoda “rjust”, wyrównuje tekst do prawej strony znakiem podanym jako argument, do zadanej ilości znaków - czyli zamiast “7″ dostaniemy “07″. Efekt naszej pracy przedstawia ostatni listing.

No i to tyle na dziś. Krótko było, ale szkielet programu jest gotowy, dopisanie do niego np. kontekstów czy priorytetów jest dość trywialne i pozostawiam to jako ewentualne ćwiczenie dla czytelnika. Nie mam ochoty męczyć dłużej tego przykładu, odnoszę wrażenie że “kursanci” też nie, bo nie jest on szczególnie jarający, ale trudno było wymyśleć coś lepszego kiedy nie mieliśmy opanowanych podstaw.

Co dalej? W najbliższym czasie, pociągniemy przede wszystkim temat Ruby On Rails, czyli zajmiemy się programowaniem aplikacji sieciowych. Zobaczymy na jak długo starczy nam weny i co z tego wszystkiego wyjdzie, ale możliwe że w trakcie wrócimy do zwykłego Ruby’ego żeby zrobić jakąś aplikację GUI albo prostą grę.

Nie chcę pisać czegoś w stylu “Zostawcie komenty!!!!1111″, ale miłoby było gdyby ktoś się wypowiedział na temat tutoriala w dotychczasowej formie. Czy ktoś to wogóle czyta i usiłuje śledzić, czy moje tłumaczenia są jasne, co się podoba, co nie? Na razie ozwały się bodaj dwie osoby i prośby obu zostaną zrealizowane - wezmę się w końcu i zrobię coś żeby listingi były ładne i z pokolorowaną składnią, oraz zrobię osobną stronę z listą wszystkich odcinków chronologicznie, obliguję się do tego publicznie :) Czekam jednak na dalsze uwagi, pamiętajcie, że to od was zależy jaką formę i czy wogóle przybierze dalej ten “kurs”.

Dodaj do del.icio.us | Dodaj do wykop.pl

Komentarze ():

lopex, 18 marzec 2006, 5:03 pm

Dobry pomysł z tym jabberem, zapraszam w rakim razie na

Fipaj. Blog ;] : Ruby jest, 27 marzec 2006, 6:03 pm

[...] konfiguracją serwera i zacząłem programować w Rubym. Z linii komend ;-) Zacząłem od tutoriali Sztywnego. Dobra lektura na początek, chociaż po przetrawieniu jego 6 wpisów jeszcz [...]

Uzytkownik, 2 kwiecień 2006, 5:04 pm

Nie lepiej:
class IO
def prompt(text, regexp=nil, msg=”Nieprawidlowe wejscie, sprobuj jeszcze raz”)
puts text
return gets.chop if(regexp==nil)
while(true)
input = gets.chop
return input if(input =~ regexp)
puts msg
end
end
end

Dla użytkownika łatwiejsze będzie nieprawidłowy format daty, niż nieprawidłowe wejście
Pozdrawiam

sztywny, 2 kwiecień 2006, 6:04 pm

Racja, aczkolwiek to raczej kosmetyka :>

Michal "johny_cage" Sobczak, 20 kwiecień 2006, 9:04 pm

Witam,
co do wyrazen regularnych, to ksiazka Mastering Regular Expressions, jest z serii must to have, a przynajmniej must to read. Co do tego, ze nie ma tutaj opisu jak sie je tworzy i to oznaczaja poszczegolne rzeczy, http://google.com/search?q=Regular+Expressions+Reference+Card
Ogolnie wszystkie Reference Card, a w szczegolnosci Quick Reference Card sa dobre do tego, zeby miec wszystko co najwazniejsze przy sobie i o niczym nie zapomniec.
Kurs jest dla mniej dobry. Moglby byc lepszy. Jakies ciekawsze przyklady by sie przydaly, nie koniecznie z GUI, ale cos bardziej praktycznego, i bez tego chorobliwego rozdrabniania wszystkiego na moduly, klasy itp bo na malych przykladach dziwnie i smiesznie to wyglada.
OOP wlasnie jest po to zeby duze projekty moznabylo ogarnac jednym spojrzeniem.

pozdrawiam,
M

Michal Sobczak, 20 kwiecień 2006, 9:04 pm

Witam,
jesli chodzi o wyrazenia regularne to dla poczatkujacych:
http://google.com/search?q=regular+expressions+reference+card
Wszystkie Quick Reference Card sa dobrym sposobem zeby miec
wszystko najwazniejsze pod reka. A co do ksiazki Mastering
Regular Expressions, to jest to ksiazka typu, jesli nie
must to have, to przynajmniej must to read. Kurs calkiem niezly,
przynajmniej jak dla mnie, a dopiero zaczynam z Ruby.
pozdrawiam,
Michal

Bellois, 24 kwiecień 2006, 8:04 pm

Fajne te Twoje tutoriale. Zamierzam się pouczyć niedługo. Dzięki :)

Wenihal, 29 kwiecień 2006, 10:04 pm

Keep up the good work!!! Nie myslales przypadkiem o zalozeniu jakiejs sensownej stronki (portalu) o Ruby? To czego tutaj brakuje to jakies forum, na ktorym mozna by podyskutowac o programowaniu w Ruby, powymieniac sie doswiadczeniem itp. Pozdrawiam.

sztywny, 30 kwiecień 2006, 6:04 am

Myślałem, ale community Ruby w Polsce jest na razie trochę mikroskopijne :) Zresztą w tej chwili jest już http://www.rubyonrails.pl, gdzie można podyskutować.

Agnieszka, 12 maj 2006, 9:05 am

Czesc! Ja własnie zaczynam moja przygode z Ruby, poczytalam troche madrych ksiazek (in English) i musze przyznac ze preferuje nauke na przykladach. Twoj tutorial’ik pomaga tylko ze przydaloby sie wiecej i wiecej … Musze w niedlugim czasie napisac aplikacje webowa w Ruby on Rails, a poki co to znam pobieznie struktury. Zabieram sie do pracy i nie ukrywam ze byloby fajnie gdybys pisal dalsza czesc tutoriala, z pewnoscia mi pomoze. A narazie dzieki! Milego kodowania :-)

Agnieszka, 12 maj 2006, 12:05 pm

Witam ponownie… Czy mozna wiedziec na podstawie czego piszesz ten tutorial ? ‘Programming in Ruby’ czy masz moze jakis fajny tutorial in English. Bardzo prosze o informacje bo pilnie potrzebne mi sa zrozumiale napisane materialy.
pozdrawiam

sztywny, 12 maj 2006, 3:05 pm

Mam w domu “Programming Ruby” w orginale, na papierze, traktowane niczym relikwia prawie :) W związku z czym kiedy czegoś nie pamiętam albo nie jestem pewien, to tam sięgam, jako że jest to najbardziej autorytatywne źródło. Szczęśliwe jest jednak w sieci troche fajnych tutorialów Ruby’ego po angielsku:

http://poignantguide.net/ruby/
http://www.rubycentral.com/book/
http://pine.fm/LearnToProgram/

Agnieszka, 13 maj 2006, 5:05 pm

dzieki sztywny !!! Jestes równy gość :-)
pozdrawiam

PeCet, 13 lipiec 2006, 9:07 pm

Świetny tutorial , trochę się nawet dzięki niemu ‘zbliżyłem’ do rubiego, chociaż nie uważam że może zastąpić C/C++ ale czasem sie przyda ;)

kaczuuur, 31 sierpień 2006, 9:08 pm

Dzięki za tutoriale - zacznę zgłębiac ten język :D

Beny, 28 grudzień 2006, 9:19 am

Jakie są znane (lub nie) programy napisane w Ruby?
Proszę o przykłady.

sztywny, 28 grudzień 2006, 10:40 am

Sztandarowym przykładem jest Rails i setki web aplikacji w nim napisanych. Pozatym jest trochę różnych luźnych ciekawy projektów, w większości są to jednak biblioteki, narzędzia dla developerów, frameworki itp. Najłatwiej chyba wejść na rubyforge.org i tam samemu popatrzeć. Fakt faktem jest, że brakuje popularnych aplikacji użytkowych napisanych w Ruby (jak pythonowe Gajimy, Quod Libety itp.) i nie zamierzam się z tym kłócić. Ale zapewniam, że można w nim takie aplikacje pisać - wiem bo próbowałem.

Stefan, 28 marzec 2007, 4:47 pm

A mnie sie podobalo.

Tylko ze te listingi sa niedostepne.

Pozdrawiam

asd, 28 kwiecień 2007, 7:35 pm

calkiem fajny tut, tylko ze listingi troche za mala czcionka pisane ;)

micki21, 21 maj 2007, 8:19 pm

Witam mam pytanie czy w ruby jest jakaś komęda żeby zapisać program na dysku albo coś takiego ???????

baby_ruby, 8 czerwiec 2007, 5:14 pm

ogolnie bardzo fajny tutorial tylko pogubilem sie kompletnie przy tym “poprawianiu” kodu a zaden z linkow do listingow nie dziala

Sha-Kaan, 7 wrzesień 2007, 7:56 pm

Witam

Bardzo fajny tutorial szczególnie, że oparty na praktycznym przykładzie mam nadzieje zę kolejne odcinki się pojawią i też będą bazować na czymś konkretnym:)
Też trochę się pogubiłęm przy poprawianiu kodu, w efekcie nic nie działa i mnustwo błędów ;P (ale to początki) kombinuję jak je poprawić… Przydałyby się te listingi (obecnie niedostępne), to dużo by pomogło.

Psionides, 24 październik 2007, 2:04 pm

Moje uwagi:

1. Nie (d{2}), tylko {\d(2)}, bo inaczej to ci się tylko ciąg ‘dd.dd.dddd’ zmatchuje do tego :>

2. Jak na mój gust to konwertowanie daty z obiektu DateTime na tablicę trzech stringów, zamiast zrobić dokładnie odwrotnie (zrobić tak żeby obydwie daty były jako DateTime) jest dość nieładne…
Poza tym po tej zmianie dalej jest niekonsekwencja - jedna data jest jednym stringiem, a druga tablicą trzech stringów - też nijak to nie pasuje do siebie.

3. W tym regexpie dodałbym jeszcze ^ i $ na końcach, bo inaczej ciąg “Zadanie dodane 24.10.2007 o 14:02″ też zostanie przyjęty jako data…

Kajtek, 11 marzec 2008, 12:22 am

Listingi nie działają.

wit3k, 27 październik 2009, 10:19 pm

Dzięki za tutka. Konsolową todo listę Jarosława zapamiętam z pewnością na długo - to był mój pierwszy kontakt z rubim.
ps. sweet blogasek

Skomentuj