How does UX transform businesses? The effects of usability testing
It has already become clear that User Experience (UX) is a key factor in shaping how companies build a business. The customer-centric approach has almost completely replaced […]
Kurs Ruby on Rails – Lekcja 3 – Struktura aplikacji
Na początek pojęcia, jakimi się posługujemy: Ruby on Rails
to framework, z którym będziemy pracować, dostarczony jest przez zainstalowanie gemu rails
. Gem
jest więc rozszerzeniem do języka Ruby. Gemy instaluje inny gem – bundler
, czyli rozszerzenie do instalowania innych wtyczek. Do kursu potrzebny będzie dowolny edytor tekstowy, my używać będzie edytora atom
.
$ cd katalog-projektu
$ gem install rails:6.1.4.4 bundler
$ rails -h #pokaże nam dostępne opcje podczas tworzenia nowej aplikacji
$ rails new .
$ atom .
Instalujemy gemy i sprawdzamy, czy działa
$ bundle
zainstaluje nam gemy, po wpisujemy
$ rails server
albo
$ bundle exec rails server
aby wystartować serwer na porcie :3000
Convention over configuration
Jednym z filarów Ruby on Rails jest convention over configuration. Oznacza to tyle, że część konfiguracji jest zautomatyzowana, aby przyśpieszyć rozwój aplikacji oraz zmniejszyć ilość redundantnych decyzji związanych ze strukturą aplikacji. Ruby on Rails jest natomiast elastyczne w kwestii niepodążania za konwencjami.
W otwartym edytorze widać drzewko aplikacji, jednak zanim przejdziemy przez najistotniejsze dla tego kursu foldery, krótko objaśnimy czym jest MVC.
MVC, czyli model architektoniczny, gdzie:
M – model – reprezentacja obiektu, logika aplikacji
V – view – wyświetlanie GUI dla modeli
C – controller – sterowanie przepływem danych
Drzewko aplikacji
app/assets - tu znajdują się pliki css oraz obrazki
app/channels - zawiera kanały dla WebSocketów
app/controllers - kontrolery (mvc)
app/helpers - metody używane w widokach
app/javascript - pliki .js
app/jobs - logika do procesów asynchronicznych
app/mailers - klasy do wysyłki maili
app/models - modele (mvc)
app/views - widoki i layouty (mvc)
/config - pliki konfiguracyjne
/config/routes - ścieżki w aplikacji
/db - migracje, seedy
Gemfile - plik z gemami
Gemfile.lock - autogenerowany plik z wersjami zainstalowanych gemów
Model, a tabela
(User -> users)
. Nie jest wymagane łączenie klasy z tabelą, dzieje się to automatycznie.Przede wszystkim, musimy stworzyć naszą bazę danych poprzez
$ rails db:create
Do tworzenia struktury aplikacji można używać tzw. scaffoldingu, czyli komend, które opisują strukturę w sposób zrozumiały dla generatora, który to wygeneruje za nas odpowiednie pliki w drzewku aplikacji.
Struktura takiej komendy wygląda tak:
$ rails generate model NazwaModelu nazwapola:typ nazwapola:typ
a więc, generując model User używamy:
$ rails generate model User name:string
Powyższa komenda wygeneruje migracje dla tabeli users, model User oraz pliki do testów
invoke active_record
create db/migrate/20210726055700_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
Po takiej komendzie zmieniamy stan naszej bazy poprzez:
$ rails db:migrate
Oczywiście scaffoldingu nie trzeba używać, jak widać generuje on kilka plików, które niekoniecznie muszą być w przyszłości potrzebne. Nic nie stoi na przeszkodzie, aby samemu stworzyć nowy plik w modelach i zainicjalizować w nim klasę
class User < ApplicationRecord
end
Następnie tworzymy samodzielną migrację poprzez
rails generate migration createUsers
która to wygeneruje nam pustą migrację:
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
end
end
end
Aby osiągnąć ten sam efekt co przy wywołaniu pierwszej komendy, uzupełniamy migrację poprzez dodanie kilku linijek kodu do ciała metody change
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name
t.timestamps # doda nam pola created_at oraz updated_at które automatycznie się będą wypełniać
end
end
end
Każda tabela automatycznie tworzy sobie również pole ID. Po migracji danych, czyli
rails db:migrate
możemy spojrzeć w db/schema.rb, aby zobaczyć jak aktualnie wygląda nasza baza danych.
Podobnie jak w przypadku irb z poprzedniej lekcji, mamy do dyspozycji konsolę, którą włączamy poprzez
$ rails console
lub
$ bundle exec rails console
Otworzy nam to konsolę, ale również ładuje całą aplikację, dając dostęp do wszystkich klas.
irb(main):001:0> user = User.new(name: 'Jan Kowalski') # inicjalizuje obiekt
irb(main):002:0> user.save # zapisuje obiekt do bazy
irb(main):003:0> User.create(name: 'Jan Kowalski') # inicjalizuje i jednocześnie próbuje zapisać obiekt do bazy
irb(main):004:0> user = User.create(name: 'Janina Kowalska')
irb(main):005:0> user.update(name: 'Katarzyna Kowalska') # aktualizuje obiekt i stan w bazie
irb(main):006:0> user.destroy # usuwa obiekt z bazy danych
Najczęstsze operacje do odczytywania obiektów z bazy
irb(main):001:0> User.find(1) # znajdzie w bazie obiekt User, który ma ID 1
irb(main):002:0> User.find_by(name: 'Jan Kowalski') # znajdzie w bazie obiekt User, który posiada podany name
irb(main):003:0> User.last # odczyta z bazy ostatnio stworzony obiekt User
irb(main):004:0> User.last(5) # odczyta z bazy 5 ostatnio stworzonych obiektów User
irb(main):005:0> User.first # odczyta z bazy pierwszy stworzony obiekt User
irb(main):006:0> User.all # odczyta z bazy wszystkie stworzone obiekty User
irb(main):007:0> User.count # odczyta z bazy wszystkie stworzone obiekty User i zwróci ich liczbę
irb(main):008:0> User.where(name: 'Jan Kowalski') # odczyta z bazy wszystkie stworzone obiekty User, które posiadają podany name
Modele mogą się łączyć za pomocą relacji, które odzwierciedlają zależności logiki biznesowej
Relacje jakie mamy do dyspozycji to:
Chcąc powiązać User z Post relacją jeden do wielu, wpisujemy
$ rails generate model Post user:belongs_to title:string body:string
Jak poprzednio, stworzy nam to model Post oraz migrację, w której przypisze obiektom Post ID Usera. W modelu Post zobaczymy też wspomnianą relację.
class Post < ApplicationRecord
belongs_to :user
end
Należy pamiętać żeby uzupełnić relacje w istniejącym już modelu User:
class User < ApplicationRecord
has_many :posts
end
Teraz możemy dodać posty naszemu użytkownikowi. Jeśli poprzednio go usunęliśmy, to tworzymy go znów
user = User.create(name: 'Jan Kowalski')
jednocześnie przypisaliśmy go do zmiennej user
.
Post.create(user_id: user.id, title: 'Witam!', body: 'Nowy Post Jan Kowalski')
Po czym możemy odczytać z bazy posty, które są przypisane do naszego użytkownika przez:
user.reload.posts
Aby czuwać nad spójnością danych w bazie, używamy walidacji, które sprawdzają stan obiektu zanim zostanie wysłany do bazy danych. Najprostszą i najszybszą drogą są walidacje ActiveRecord’owe. W modelu User dodajemy kolejne linijki kodu, które będą sprawdzać, czy jest podany name
oraz, czy jest w odpowiednim formacie
class User < ActiveRecord::Base
has_many: :posts
validates :name, presence: true
validates :name, format: /[A-Z][a-z]*/
end
Dla modelu Post dodajemy
class Post < ActiveRecord::Base
belongs_to: :user
validates :title, :body, presence: true
end
Przy tworzeniu postu sprawdzimy, czy podany jest tytuł, treść postu oraz, czy jest podany user_id, co jest walidowane automatycznie przez relację belongs_to.
W klasie modelu możemy tworzyć zakresy, które wywołane zwrócą odpowiednie rekordy z bazy; jest to dobra praktyka jeśli często korzystamy z konkretnego zapytania bazodanowego. Do klasy User dodajemy
scope :created_today, -> { where("created_at > ?", Date.today.beginning_of_day) }
sprawdź w konsoli, jak to się zachowa
User.created_today
Możemy też tworzyć zakresy przyjmujące parametry, czy nawet łączyć je w “methods chain”, czyli wywołać jedną po drugiej. Jeśli dodamy kolejny zakres
scope :with_name, ->(name) { where("name LIKE ?", "#{name}%") }
możemy użyć:
User.created_today.with_name("Jan")
1. Stworzyć w konsoli 5 obiektów Post z różnymi wartościami
2. Stworzyć model Comment z polem content (string), timestampami, który będzie miał relację do Post (post has_many :comments) i relację do User
3. Dodać walidację na pola content (presence) oraz walidację na ilość znaków (max. 140 w obu)
4. Napisać pętlę, która przeiteruje się przez Comment i wyrzuci na konsolę ich autorów. (można użyć puts)
5. Napisać query, które wyciągnie z bazy wszystkie obiekty Post użytkownika o podanym name.
6. (trudne) Napisać query, które wyciągnie z bazy wszystkie obiekty Comment użytkownika o podanym name. (tip: przeczytać o active_record_querying, w szczególności joins w linkach poniżej)
6. (trudne) Napisać query, które wyciągnie z bazy wszystkie obiekty Comment użytkownika o podanym name i podanym tytule posta.
7. (trudne) Stworzyć jeszcze jeden obiekt Comment, którego pole created_at jest ustawione na datę jutrzejszą. Napisać query, które wyciągnie z bazy wszystkie wiadomości, które zostały stworzone wcześniej niż 5 minut temu i mają więcej niż 100 znaków. (tip: sprawdź jak działa Date.today + 120.minutes i dostosuj do zadania)
http://guides.rubyonrails.org/
http://api.rubyonrails.org/
http://guides.rubyonrails.org/migrations.html
https://guides.rubyonrails.org/active_record_validations.html
https://guides.rubyonrails.org/association_basics.html
https://guides.rubyonrails.org/active_record_validations.
html https://guides.rubyonrails.org/active_record_basics.html
https://guides.rubyonrails.org/active_record_querying.html
Przeczytaj również o...
It has already become clear that User Experience (UX) is a key factor in shaping how companies build a business. The customer-centric approach has almost completely replaced […]
What if there was one simple method to find opportunities for improved design, uncover UX problems, and learn about your target users’ behavior and preferences? Would you […]