2008 07 08 Serwis zdjęć z wakacji [Programowanie]


Programowanie
Ruby/RubyOnRails
Serwis zdjęć z wakacji
Marek Sawerwain
Tworzenie rozbudowanych serwisów internetowych to trudne zadanie. Co więcej, staje się ono bardzo
trudne, jeśli wszystkie elementy będą tworzone od podstaw. Ten problem został bardzo szybko
rozwiązany i powstały tzw. frameworki, czyli gotowe systemy, które wspomagają pracę nad tworzeniem
rozbudowanych serwisów internetowych.
ista obecnie dostępnych systemów jest bardzo Przykład na rozgrzewkę
duża. Mniej popularne rozwiązania to systemy Nasz pierwszy przykład sprowadzi się do opracowania
takie jak np. Yaws (napisany w języku Erlang), aplikacji, która generuje prosty komunikat tekstowy. Jed-
Lczy Seaside opracowany no Smalltaku. Do naj- nakże, aby nieco skomplikować zadanie, zrobimy to na
bardziej popularnych należą np. Zope, Zend, Apache Coco- trzy sposoby. Zakładamy, że będziemy pracować z pozio-
on oraz pakiet RubyOnRails, który w ostatnim czasie zyskał mu konsoli - choć trzeba nadmienić, że istnieje specjal-
bardzo wiele na popularności. na odmiana środowiska Eclipse o nazwie Aptana wraz z
RubyOnRails zdobywa coraz większą popularność rozszerzeniem do tworzenia aplikacji Rails. Niestety, jego
dzięki świetnie przemyślanej budowie. Dla wielu typowych wadą jest spore zapotrzebowanie na pamięć. 1GB pamię-
zadań, jakie się napotyka podczas tworzenia serwisów (np. ci RAM jest niezbędny do w miarę sprawnej pracy. Na-
współpraca z bazą danych) RubyOnRails oferuje gotowe tomiast zaletą jest spora wygoda, ponieważ Aptana ofe-
rozwiązania, co w efekcie przekłada się na przyspieszenie ruje wszystkie elementy do tworzenia serwisów, od wła-
pracy nad serwisem. Jednakże, każde narzędzie warto po- snej implementacji języka Ruby, poprzez niezbędne ser-
znać podczas realizacji choćby najprostszego projektu. Dla- wery; oferuje także wbudowany serwer bazy danych oraz
tego warto spróbować zrealizować prosty projekt, np. ser- przeglądarkę.
wis, w którym to użytkownicy będą mogli umieszczać zdję- Pierwsza czynność to utworzenie szkieletu aplikacji.
cia np. z wakacji. W dowolnym katalogu, nawet w katalogu domowym, z
Nasz projekt opracujemy w wersji 1.2.6 pakietu RubyOn- poziomu konsoli wydajemy polecenie rails App1. Na-
Rails. Istnieje nowa wersja 2.0 ale jak na razie najbardziej po- zwa naszej aplikacji to App1  możemy naturalnie podać
pularna jest wersja 1.2.x. Tym bardziej, iż większość dostęp- inną. Wynikiem działania wydanego polecenia jest katalog
nych książek oraz informacji jakie znajdziemy w Internecie zawierający kompletny szkielet naszej aplikacji. Możemy
powołuje się na nieco starszą wersję pakietu RubyOnRails. nawet już w tym momencie uruchomić serwer, przecho-
50 lipiec/sierpień 2008
linux@software.com.pl
Programowanie
Ruby/RubyOnRails
dząc do katalogu i wydając odpowiednie po- pomocą kombinacji klawiszy CTRL-C. Po uru- niem będzie teraz dopisanie metody o nazwie
lecenie, co przedstawia się następująco: chomieniu serwera, co może być dla wielu osób index, która będzie domyślnie wywoływana
zaskoczeniem, możemy zacząć tworzyć naszą w aplikacji po wpisaniu do przeglądarki ad-
cd App1 pierwszą aplikację. W oddzielnym oknie konsoli resu http://127.0.0.1:3000/newindex. Metoda
ruby script/server ponowie przechodzimy do katalogu naszej apli- index przedstawia się bardzo prosto: wyko-
kacji i wydajemy następujące polecenie: rzystując akcję render do pola text wpisuje-
Jeśli instalacja języka Ruby oraz pakietu Rails my treść naszego komunikatu (Listing 1.)
odbyła się w sposób podobny do opisanego w ruby script/generate controller I jest to pierwszy sposób w jaki możemy
ramce, to aplikacja została uruchomiona za po- newindex wyświetlić komunikat. Drugim sposobem jest
mocą serwera Mongrel i jest dostępna lokalnie umieszczenie komunikatu w pliku html i wyświe-
pod adresem http://127.0.0.1:3000. Serwer uru- Zostanie utworzony obiekt odpowiedzialny za tlenie jego zawartości w następujący sposób:
chomiony z poziomu konsoli łatwo wyłączyć za kontroler o nazwie newindex. Naszym zada-
render :file =>  ścieżka/do/pliku,html
Ostatnim sposobem jest utworzenie w meto-
dzie index zmiennej o nazwie np. message-
ToTheWorld w następujący sposób:
def index
@messageToTheWorld =  treść naszej
wiadomości
end
Listing 1. Metoda index odpowiedzialna za
wyświetlenie komunikatu
class NewindexController <
ApplicationController
def index
render :text =>  a tu
znajduje się bardzo śmieszna
Rysunek 1. Wynik działania pierwszej aplikacji oraz domyślna strona serwera
wiadomość
end
end
serwer bazy
danych My SQL
Listing 2. Polecenie SQL tworzące tabelę ze
zdjęciami
create table photos (
id int primary key auto_
edycja zdjęcia nowe zdjęcie
increment,
lista zdjęć
usunięcie
pokaż zdjęcie picture mediumblob,
zdjęcia
description text,
name text );
kontroler aplikacji
photos
Listing 3. Przykładowa zawartość pliku data-
base.yml pozbawiona sekcji test development:
adapter: mysql
database: photodb
serwer aplikacji
Mongrel url: localhost
username: root
password: root
production:
adapter: jdbc
przegl darka WWW
driver: org.apache.derby.jdbc.C
lientDriver
url: jdbc:derby://localhost/
RubyTest1_production;create=true
użytkownik
username: app
serwisu
password: app
Rysunek 2. Schemat funkcjonowania naszego serwisu ze zdjęciami
www.lpmagazine.org 51
Programowanie
Ruby/RubyOnRails
Wywołanie akcji index spowoduje utworzenie Fragment kodu objętego tagami <% oraz %>.
Listing 4. Fragmenty klasy kontrolera
zmiennej, ale aby wyświetlić jej zawartość na- To naturalnie program w języku Ruby. Jest
leży utworzyć dodatkowy plik który zajmie się to analogiczne rozwiązane jak w przypad- class PhotosController <
wyświetleniem zawartości zmiennej. Dodat- ku PHP. ApplicationController
kowy plik o nazwie index.rhtml tworzymy w W podanym przykładzie podajemy tylko
podkatalogu app\views\newindex. Ostatni ele- nazwę zmiennej, to wystarczy aby komunikat def get_photo
ment ścieżki, czyli newindex to naturalnie na- został wyświetlony przez przeglądarkę. Ten @photo=Photo.find(params[:id])
zwa kontrolera. Nazwa tego pliku, a dokładniej prosty przykład pokazuje, jak następuje prze- send_data(@photo.picture, :
rozszerzenie rhtml, zdradza, iż plik ten zostanie twarzanie informacji. type=> 'image/jpeg')
poddany dodatkowej obróbce przez Ruby'ego: Zostało ono podzielone na dwa etapy. Etap end
pierwszy to przetwarzanie pliku z implementacją def show

Bardzo ważny komunikat

metody index. W drugim etapie utworzone in- @photo = Photo.find(params[:
<%= @messageToTheWorld %> formacje mogą zostać przeniesione do formatki. id])
end
def new
@photo = Photo.new
Instalacja języka Ruby oraz pakiety RubyOnRails
end
Wiele dystrybucji oferuje gotowe pakiety z językiem Ruby, toteż nie trzeba samo- def create
dzielnie instalować odpowiedniego pakietu. Jednak z drugiej strony może się oka- @photo = Photo.new(params[:
zać, iż wersja dostępna w pakiecie może być nieco nowsza niż dostępna w systemie.
photo])
W takim przypadku warto zainstalować Ruby'ego samodzielnie ze zródeł.
if @photo.save
Nie jest to trudne zadanie. W pierwszej kolejności ściągamy ze strony głównej pro- flash[:notice] = 'Zdjęcie
jektu Ruby najnowszą wersję stabilną. Następnie dekompresujemy archiwum:
oraz opis zostały wpisane do bazy
danych.'
tar zxvpf ruby-1.8.6.tar.gz
redirect_to :action =>
'list'
Ponieważ obecny jest skrypt configure, za pomocą trzech poleceń dokonamy konfiguracji,
else
kompilacji oraz instalacji w systemie:
render :action => 'new'
end
./configure  prefix=/opt
end
make
def edit
make install
@photo = Photo.find(params[:
Drugim ważnym krokiem jest instalacja pakietu gem, za pomocą którego można instalować
id])
dodatkowe rozszerzenia do Ruby'ego. Tym razem ściągamy archiwum o nazwie np. ru- end
bygems-1.0.1.tgz. Dekompresujemy archiwum, ale sposób instalacji jest innym, bowiem
def update
należy wydać polecenie:
@photo = Photo.find(params[:
id])
ruby setup.rb
if @photo.update_
attributes(params[:photo])
Powyższe polecenie wykona wszystkie niezbędne czynności związane z instalacją gema.
flash[:notice] = 'Zdjęcie
W tym momencie możemy zainstalować dodatkowy program, użyteczny podczas pracy z
oraz opis zostały uaktualnione.'
pakietem RubyOnRails, w następujący sposób:
redirect_to :action =>
'show', :id => @photo
gem install rake
else
Kolejny krok to instalacja środowiska Rails za pomocą następującego polecenia:
render :action => 'edit'
end
gem install -v=1.2.6 rails --include-dependencies
end
end
Zgodnie z naszymi ustaleniami instalujemy wersję 1.2.6, natomiast opcja --include-de-
pendencies zapewnia, iż zostaną zainstalowany dodatkowe pakiety np. ActiveRecord uła-
Listing 5. Trywialna formatka odpowiedzialna za
twiający obsługę bazy danych.
wyświetlenie nazw poszczególnych kolumn
Nasza aplikacja wymaga jeszcze instalacji sterownika do bazy MySQL oraz serwera

Mongrel, co wykonamy w następujący sposób:
<% for column in Photo.content_
columns %>
gem install mysql
<%= column.human_name
gem install mongrel --include-dependencies
%>
Należy jeszcze dokonać instalacji bazy danych MySQL. Jednakże najlepiej zainstalować ba- <% end %>
zę z pakietów dostępnych w danej dystrybucji ponieważ zaoszczędzi nam to trochę czasu.

52 lipiec/sierpień 2008
Programowanie
Ruby/RubyOnRails
Plan naszej aplikacji
Listing 6. Formatka odpowiedzialna za wyświetlanie listy zdjęć wprowadzonych do bazy danych
Naszym głównym zadaniem jest opracowa-

Dostępne zdjęcia

nie nieskomplikowanego systemu, w którym
użytkownik będzie mógł umieszczać dane:
zdjęcie, jego opis oraz imię i nazwisko auto-
ra. Zostaną one umieszczone w bazie danych,
w naszym przypadku będzie to baza danych
oparta o serwer MySQL.
Oznacza to konieczność utworzenia tabe-
<% for photo in @photos %> li. Dla naszej bardzo prostej bazy danych jest
to kilka linii, co potwierdza Listing 2. Ma-
słane przez użytkownika. Jego opis znajdzie
Możliwości naszej aplikacji nie bę-
zy. Będzie można wyświetlić listę wszyst-
kich zdjęć dostępnych w bazie z podziałem
na strony. Pojedyncze zdjęcie będzie można
niała możliwość zmiany opisu. Dla ułatwie-
nia nie będziemy wprowadzać kont użytkow-
<% end %> nika, które naturalnie w prawdziwym serwi-
Zdjęcie Opis Imię i nazwisko
my cztery proste pola. Pierwszym jest id,
photo.id ) %>" czyli główny klucz tabeli. Drugie pole o na-
height="100" /> zwie picture będzie zawierać zdjęcie prze-
się w polu description, a imię i nazwisko
<%= photo.send("description") %> w polu name.
dą zbyt duże, tzn. użytkownik będzie mógł
<%= photo.send("name") %> tylko wgrać plik ze zdjęciem do naszej ba-
<%= link_to 'Pokaż', :action => 'show', :id => photo %> <%= link_to 'Edycja', :action => 'edit', :id => photo %> <%= link_to 'Skasuj zdjęcie', { :action => 'destroy', :id => photo usunąć bądz zmienić, jak również będzie ist-
}, :confirm => 'Czy na pewno?', :post => true %>
sie byłyby niezbędne.
<%= link_to 'Poprzednia strona', { :page => @photo_pages.current.previous
} if @photo_pages.current.previous %> Konfiguracja bazy danych
<%= link_to 'Następna strona', { :page => @photo_pages.current.next } if Tworzenie aplikacji powinniśmy zacząć od
@photo_pages.current.next %> utworzenia bazy danych. Użyć możemy np.

programu mysql i z poziomu konsoli utwo-
<%= link_to 'Nowe zdjęcie', :action => 'new' %> rzyć odpowiednią tabelę. Warto także sko-
rzystać z narzędzi graficznych takich jak My-
Listing 7. Metoda o nazwie up, która tworzy tabelę ze zdjęciami SQL GUI, gdzie łatwo będzie utworzyć tablę
oraz poddać ją edycji. Będzie można także
def self.up sprawdzić nowe rekordy, które będą wpisy-
create_table :photos do |t| wane z poziomu naszej aplikacji sieciowej.
t.column :picture, :mediumblob Drugim elementem jest określenie sposo-
t.column :description, :text bu dostępu do danych z poziomu naszej apli-
t.column :name, :text kacji sieciowej. Należy poddać edycji plik da-
end tabase.yml, który znajduje się w katalogu con-
end fig. W tym pliku określamy nazwę użytkow-
nika, hasło, nazwę bazy danych oraz nazwę
Listing 8. Formularz za pomocą którego wprowadzamy nowe zdjęcie do bazy hosta na którym został uruchomiony serwer.
Podczas pracy nad aplikacją serwer MySQL
<%= start_form_tag( { :action => 'create' }, :multipart => true ) %> warto naturalnie uruchamiać na lokalnej ma-
<%= render :partial => 'form' %> szynie. Ogólne warto wspomnieć, iż aplikacje
<%= submit_tag "Nowe zdjęcie" %> Rails są uruchamiane w trzech podstawowych
<%= end_form_tag %> trybach: test, development oraz production
<%= link_to 'Lista fotografii', :action => 'list' %> (o tym, w jakim trybie uruchamiana jest apli-
kacja decyduje wartość zmiennej środowisko-
Listing 9. Fragment formularza odpowiedzialnego za tabelę ze zdjęciami wej RAILS_ENV). Pierwszy tryb jest przezna-
czony tylko do testów, drugi tryb jest stosowa-
<%= error_messages_for 'photo' %> ny podczas tworzenia aplikacji i ten tryb bę-


dziemy stosować. Ostatni tryb  produkcyj-
<%= file_field 'photo', 'photo' %>

ny  przeznaczony jest do codziennej pracy
aplikacji.
www.lpmagazine.org 53
Programowanie
Ruby/RubyOnRails
Listing 3 zawiera przykładowy plik da- Znaczenie pozostałych metod jest nastę- zostanie tylko jedna z nich odpowiedzialna
tabase.yml. W sekcji development stosu- pujące: metoda get_photo pobiera dane ob- za listę zdjęć.
jemy sterownik MySQL, natomiast w sek- razu w formacie jpeg, metoda show jest od- Listing 6 przedstawia cały kod zródłowy
cji production widać jeszcze wpis związa- powiedzialna za wyświetlenie zdjęcia oraz tej formatki. W pewnym sensie składa się ona
ny z dostępem do bazy danych poprzez ste- opisu, jednakże ta metoda tylko inicjuje ten z trzech sekcji. Pierwsza to tabela, w której
rownik jdbc. proces  za poprawne wyświetlenie danych znajduje się lista zdjęć.
odpowiedzialna jest formatka show.rhtml. Druga sekcja to menu ze stronami, jeśli
Kontroler aplikacji Zadaniem metody create jest umiesz- baza zawiera dużą ilość zdjęć. Trzecia sekcji
Wszystkie podstawowe metody obsługują- czenie danych w bazie danych. Metoda składa się z jednego przycisku odpowiedzial-
ce naszą aplikację znajdują się w kontrolerze ta jest wywoływana z poziomu formatki nego za wprowadzanie nowej fotografii.
photos. Kontroler ten tworzymy w typowy list.rhtml. Jak widać, oprócz kontrolera Pierwszy element tabeli ze zdjęciami to
dla pakietu Rails sposób (będąc naturalnie w istotną rolę ogrywają formatki, gdzie może- nagłówek z nazwami kolumn. Rozwiązanie,
katalogu naszej aplikacji): my reagować na czynności użytkownika. jakie prezentuje Listing 6 jest bezpośrednie,
gdyż tworzymy w HTML nagłówek naszej
ruby script/generate controller photos Lista zdjęć tabeli wpisując bezpośrednio nazwy kolumn.
W naszej aplikacji występuje kilka forma- Możemy jednak zautomatyzować ten proces
Obszerne fragmenty kontrolera zawarta tek. Z racji ograniczonego miejsca opisana postępując tak jak pokazano na Listingu 5.
są na Listingu 4. Brakuje tam jednak kil-
ku metod, które pokrótce omówimy w tym
miejscu.
Pierwsza to naturalnie metoda index, któ-
rej zadaniem jest wyświetlenie spisu wprowa-
dzonych do bazy danych zdjęć. Jej kod jest
bardzo krótki i przedstawia się następująco:
def index
list
render :action => 'list'
end
Wywołujemy inną metodę list, bezpośred-
nio odpowiedzialną za obsługę listy zdjęć,
a następnie akcją render niejako przekazu-
jemy sterowanie do akcji list.
Sposób implementacji metody list rów-
nież nie jest zbyt obszerny:
def list
@photo_pages, @photos = paginate
:photos, :per_page => 6
end Rysunek 3. Pogląd na tabelę ze zdjęciami w programie MySQL GUI
Wykorzystujemy dostępną metodę paginate,
której zadaniem jest podział wszystkich wpi-
Automatyczne tworzenie tabeli ze zdjęciami
sów w bazie na strony po sześć zdjęć na jed-
ną stronę.
Na początku tworzenia naszej aplikacji tabela z danymi została utworzona przez nas
Wynikiem działania metody jest obiekt
samodzielnie. Zadanie to może zostać zrealizowane za pomocą RubyOnRails. Nale-
reprezentujący naszą bazę photos oraz obiekt
ży bowiem na początku utworzyć specjalny obiekt w następujący sposób:
photo_pages wykorzystywany do nawigacji
ruby script/generate migration create_photos
po stronach.
W katalogu db/migration zostanie utworzony 001_create_photos.rb. W tym pliku, trzeba
zmienić definicję metody self.up na następującą (Listing 7.)
W wywołaniu następującego polecenia:
Na płycie CD/DVD
Na płycie CD/DVD znajdują się wy- rake db:migration
korzystywane biblioteki, kod zródłowy
tabela ze zdjęciami zostanie samodzielnie utworzona. Wymaga to naturalnie poprawnej
programu oraz wszystkie listingi z ar-
konfiguracji dostępu do bazy danych w pliku database.yml.
tykułu.
54 lipiec/sierpień 2008
Programowanie
Ruby/RubyOnRails
Ruby samodzielnie utworzy odpowiedni na- Kolejne elementy tabeli to polecenia do edy- opisem. Jednakże jak widać nie definiujemy
główek. Niestety nazwy kolumn będą w języku cji zdjęcia, wyświetlenia pojedynczego zdję- pól, gdzie użytkownik może podać np. opis,
angielskim, ponieważ nasza tabela została w ta- cia oraz - w końcu - do skasowania go za po- ale korzystamy z oddzielnego pliku o nazwie
ki sposób utworzona. Możemy naturalnie utwo- mocą metody destroy znajdującej się kon- _from.rhtml, który zostanie wczytany za po-
rzyć polskie nazwy kolumn, jednak wiele syste- trolerze. Przed skasowaniem zadajemy pyta- mocą linii kodu: <%= render :partial =>
mów baz danych nie obsługuje poprawnie zna- nie za pomocą akcji confirm czy istotnie ska- 'form' %>. Dlatego formularz dodający no-
ków narodowych w nazwach tabel i pól, to- sować zdjęcie. we zdjęcie jest tak krótki:
też najlepiej pozostać przy nazwach pisanych Metoda destroy zostanie wywołana w mo- W podobny sposób postępujemy w przy-
po angielsku lub pozbawionych polskich zna- mencie uzyskania odpowiedzi twierdzącej. padku formularza edit.rhtml. Ponownie ko-
ków, a nazwy kolumn wprowadzić bezpośred- rzystamy z pliku _from.rhtml którego począt-
nio do formatki. <%= link_to 'Skasuj zdjęcie', { : kowy fragment przedstawia się następująco:
Po definicji nagłówka za pomocą pętli bę- action => 'destroy', :id => photo }, Pierwsza linia będzie odpowiedzialna za
dziemy wyświetlać poszczególne zdjęcia. Pę- :confirm => 'Czy na pewno?', :post => sprawdzania poprawności wprowadzonych
tla ta rozpoczyna się od wyrażenia: true %> danych. Natomiast drugi fragment określa po-
le, gdzie użytkownik będzie mógł podać na-
<% for photo in @photos %> Nowe zdjęcie zwę pliku ze zdjęciem. Pojawi się też przycisk
Formatka odpowiedzialna za wczytanie no- o nazwie Przeglądaj, za pomocą którego bę-
Wyświetlenie np. opisu, to definicja komórki ta- wego zdjęcia jest bardzo krótka. Jej najważ- dzie można wybrać plik do wysłania na ser-
beli i odpowiednie wyrażenie w języku Ruby: niejszy fragment to utworzenie formularza za wer. Definicja pola opis również jest krótka:
pomocą tagu start_form_tag. Po naciśnię-
<%= photo.send("description") ciu przycisku z etykietą Nowe zdjęcie do ba-



<%= text_area 'photo', 'description'
%>


Podsumowanie
Opisana aplikacja, choć bardzo prosta, ukazu-
je największą zaletę pakietu RubyOnRails: ła-
twość używania tego systemu. Jednym z przy-
kładów jest obsługa bazy danych. Tylko na sa-
mym początku należało odnieść się do języka
SQL. Opracowanie tego typu aplikacji siecio-
wej przy pomocy pakietu Rails to jak widać
praca na przysłowiowy jeden wieczór.
Podobna baza opracowana w PHP wy-
magałaby od nas znacznie większego nakła-
du pracy, gdyż pakiet RubyOnRails uwalnia
nas od wielu czynności o charakterze czysto
technicznym.
Tworzenie tabel czy też zadawanie odpo-
Rysunek 4. Środowisko Aptana wiednich zapytań to zadanie dla pakietu Ru-
byOnRails  ta właściwość na skupieniu się
na tworzeniu samej aplikacji. Dlatego zachę-
cam do wprowadzania zmian w przedstawio-
O autorze
nej bazie danych, bo możliwości są wręcz
Autor zajmuje się tworzeniem oprogramowania dla WIN32 i Linuksa. Zainteresowania:
nieograniczone. Można dodać do bazy dodat-
teoria języków programowania oraz dobra literatura.
kowe pola, jak np. datę dodania zdjęcia oraz
Kontakt z autorem: autorzy@linux.com.pl.
popracować nad stroną graficzną.
W Sieci
" http://www.ruby-lang.org/pl  Strona o języku Ruby
" http://www.rubyonrails.org/  Główna strona o pakicie RubyOnRails
" http://www.rubyonrails.pl/  Wersja polska strony o RubyOnRails
" http://freeride.rubyforge.org/wiki/wiki.pl  Środowisko IDE do pracy z Ruby'm
" http://www.aptana.com/rails/  RadRails oraz środowisko Aptana
" http://en.wikipedia.org/wiki/List_of_web_application_frameworks  Spis innych systemów do tworzenia aplikacji Internetowych
www.lpmagazine.org 55


Wyszukiwarka

Podobne podstrony:
2008 07 08 Superkaramba [Poczatkujacy]
ZEM 07 08 Program
K1 07 08 zad3 rozwiazanie?gmaraK gr2 (2)
Kolokwium zaliczeniowe sem 1 07 08 rozwiazania
IV WL harmonogram zajec 07 08
pytania z sesji letniej 07 08
07 08? I
Elektrotechnika i elektronika 07 i 08
07 08 PAME O przekroczeniu progu
07 08 Rezystancja uziemien i rezystywnosc gruntu
Wydz TiR zima 07 08 lic S N II rok
ZEM 07 08 Kontrakt
ZEM 07 08 Literatura
Ogranicz 2004 07 08
Kolokwium zaliczeniowe sem 1 07 08 b w

więcej podobnych podstron