Четвърта задача
- Краен срок:
- 26.11.2014 17:00
- Точки:
- 6
Срокът за предаване на решения е отминал
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!
Ограничения
Тази задача има следните ограничения:
- Най-много 80 символа на ред
- Най-много 6 реда на метод
- Най-много 1 нива на влагане
- Най-много 3 аргумента на метод
Ако искате да проверите дали задачата ви спазва ограниченията, следвайте инструкциите в описанието на хранилището за домашните.
Няма да приемаме решения, които не спазват ограниченията. Изпълнявайте rubocop
редовно, докато пишете кода. Ако смятате, че rubocop
греши по някакъв начин,
пишете ни на fmi@ruby.bg, заедно с прикачен код или линк към такъв като
private gist. Ако пуснете кода си публично (например във форумите), ще смятаме
това за преписване.