Домашно 3
Command Line Toolkit
След като се справихте отлично с имплементацията на файлова система, сега е време да проверим вашите UI умения.
Задачата ви този път е да имплементирате framework за изграждане на текстов интерфейс, който би могъл да работи в конзола. Тъй като това е сложна задача, ще се ограничим само до най-базовата функционалност. Това, което трябва да реализирате е частта за "подредба" на компонентите на екрана. Ще изтестваме тази функционалност, създавайки примерен компонент - текстови етикет.
TextScreen
TextScreen
е основният обект за изграждане на текстов интерфейс. Той ви позволява да дефинирате изгледа на вашето приложение, указвайки какви компоненти съдържа той и по какъв начин са подредени. Използваме го по следния начин:
UI::TextScreen.draw do
# ...
end
На мястото на коментара задаваме интерфейса на приложението, използвайки методите, описани в следващите секции. Резултатът от TextScreen.draw
е String
, съдържащ текстовото представяне на дефинираните компоненти.
label
Текстовият етикет е компонентът, с който ще тестваме подредбата на нашия UI. Пример:
screen = UI::TextScreen.draw do
label text: 'Something'
end
puts screen
Горното парче код принтира Something
на стандартния изход.
Подредба на компонентите
По подразбиране TextScreen
подрежда компонентите си хоризонтално.
screen = UI::TextScreen.draw do
label text: 'Something'
label text: 'else'
end
puts screen
Резултатът от изпълнение на кода горе е принтиране на Somethingelse
на стандартния изход.
Можем да променяме това поведение на TextScreen
използвайки групи.
Вертикална група
Вертикална група създаваме с метода vertical
. Всички компоненти, дефинирани в групата, се подреждат вертикално, един под друг. Пример:
screen = UI::TextScreen.draw do
vertical do
label text: '1'
label text: '2'
label text: '3'
end
end
Резултатът от screen.to_s
e 1\n2\n3\n
, което принтирано на стандартния изход визуализира етикетите един под друг.
Хоризонтална група
Хоризонтална група създаваме с метода horizontal
. Всички компоненти, дефинирани в групата, се подреждат хоризонтално, един след друг. Пример:
screen = UI::TextScreen.draw do
horizontal do
label text: '1'
label text: '2'
label text: '3'
end
end
Резултатът от screen.to_s
e 123
, което принтирано на стандартния изход визуализира етикетите един до друг.
Комбиниране на групи
Можем да комбинираме групи, за да създаваме по-сложни изгледи. Кодът по-долу описва табличен интерфейс:
screen = UI::TextScreen.draw do
vertical do
horizontal do
label text: '1'
label text: '2'
label text: '3'
end
horizontal do
label text: '4'
label text: '5'
label text: '6'
end
horizontal do
label text: '7'
label text: '8'
label text: '9'
end
end
end
Резултатът след принтиране на screen
e:
123
456
789
Стилизиране на компоненти
Да организираме компоненти по екрана е важно, но не достатъчно. Искаме възможност за стилизиране на компонентите, преди те да бъдат нарисувани. Въвеждаме следните две опции за избиране на стил на компонент:
Рамка
Методът label
приема няколко незадължителни именувани аргумента (keyword arguments). Един от тях е border
. По подразбиране стойността на border
за всеки компонент е nil
, което символизира липсата на рамка. Можем да зададем произволен низ, който да бъде използван за рамка на компонента. Рамката чертаем в ляво и дясно от съдържанието на компонента.
screen = UI::TextScreen.draw do
label text: 'bordered', border: '|'
end
Резултатът в screen
е низът "|bordered|".
Рамки можем да слагаме и на групи от компоненти:
screen = UI::TextScreen.draw do
horizontal border: '#' do
label text: '1'
label text: '2'
label text: '3'
end
end
Резултатът в screen
е низът "#123#".
При вертикални групи е важно да съобразим рамката с най-широкия елемент, за да не я начертаем накриво :)
screen = UI::TextScreen.draw do
vertical border: '|' do
label text: 'ha'
label text: 'haha'
label text: 'hahahaha'
end
end
Резултатът след принтиране на screen
е:
|ha |
|haha |
|hahahaha|
Стилове
Освен възможността за поставяне на рамка около компонентите, добавяме и изискването за избиране на стил. Това става чрез задаване на стойност на параметъра style
, като валидни стойности са :upcase
, :downcase
и nil
(стойността по подразбиране). :upcase
и :downcase
променят визуализацията на компонент, превръщайки буквите му съответно в главни и малки. nil
символизира липса на указан стил, при което компонентът се "рендерира" без някаква промяна.
screen = UI::TextScreen.draw do
horizontal style: :upcase do
label text: 'ha'
label text: 'HA', style: :downcase
label text: 'ha'
end
end
Резултатът в screen
е низът HAhaHA
.
Дизайн на кода
Както и в предната задача, ще спазим добрата практика целият код на нашата малка библиотечка да се намира в модул, с цел по-добра капсулация. Модулът е UI
. Извън този модул не трябва да имате нищо друго.
От друга страна, имате пълна свобода да слагате в UI колкото искате други класове и модули и да съставите такава архитектура, каквато ви харесва, стига да спазите описания по-горе публичен интерфейс.
DSL
Методите, използвани за описание на екраните, са тясно свързани с проблемната област. Имената им и начинът, по който ги ползваме, моделират тази проблемна област. Тази концепция се нарича domain-specific language. Съкращава се като DSL. Това е задачата ви – да имплементирате един DSL.
Тези методи трябва да са налични само в блока, подаван на draw
. Извън него те не трябва да работят. Това не трябва да са глобални методи. Не трябва да ги има в Object
. Ще има тестове, които ще проверяват това. Ако видим такива методи в Object
, ще ви възнаградим и с наказателни точки.
Бележка
Ще тестваме само с латиница и специални символи от сорта на |~!@#$%^&*
. Други азбуки към момента не ни вълнуват.
Примерни тестове
Примерните тестове се намират в хранилището ни в GitHub!
Ограничения
Тази задача има следните ограничения:
- Най-много 6 реда на метод
- Най-много 80 символа на ред
- Най-много 1 нива на влагане
- Най-много 6 метода в клас
- Не ползвайте ; за да разделяте изрази
- Без whitespace на края на реда
- Изисква валиден синтаксис
- Забранява използването на глобални променливи
- Налага спазване на конвенциите за именуване
- Най-много 3 аргумента на метод
- Налага спазване на конвенциите за поставяне на интервали около оператори
- Изисква да ползвате само комбинации валидни английски думи за именуване, със следните изключения: upcase downcase delegator