Содержание сайта
Главная Новичку Цитаты Реализации Статьи Документация
Компании Программы Ссылки Обсуждение Обсуждение 2 Гостевая

Расширение "Инспектора"

Самое несложное из всего! - ответил он. -
Тем, кто хорошо знаком с пятым измерением,
ничего не стоит раздвинуть помещение до
желательных пределов. Скажу вам более,
уважаемая госпожа, до черт знает каких пределов!
М.Булгаков "Мастер и Маргарита"

Эта статья является первой из ряда заметок посвященных возможностям расширения инструментальной среды VisualWorks 7. Начнём, пожалуй, с самого простого расширения и, в то же время, самого нужного при разработке приложений.

Инспектор - инструмент, при помощи которого можно видеть "внутренности" объекта. Чтобы просмотреть объект в инспекторе ему нужно послать сообщение #inspect. Например, если выполнить в рабочей области (workspace) нижеприведённый кусок кода, то откроется инспектор.

'Двухбайтовая строка' inspect

Традиционно инспектор в VW выглядел так:
Простой инспектор
Две панели: в левой отображается список переменных объекта, в правой значение выбранной переменной. В VW7 появился новый инспектор - Trippy. Он имеет множество новых возможностей, но основная из них - настраиваемость. Инспектор может изменять способ отображения информации в зависимости от отображаемого класса.

Например, выполнив в рабочей области такой код:

Icon defaultIcon inspect
вы увидите, помимо содержания переменных объекта, еще и изображение иконки в "человеческом" формате:
Новый инспектор - Trippy

Рассмотрим, как можно добавить в инспектор подходящий вам способ отображения объектов.

Для примера рассмотрим создание инспектора к Proxy из GLORP. GLORP это средство для отображения объектов на данные в реляционных базах данных (Object-Relation mapping).

При первом обращении к прокси, он из БД извлекает объект, которому затем пересылает все сообщения посылаемые к прокси. Таким образом, прокси практически полностью прозрачен для клиентского приложения. Но подобная прозрачность создаёт некоторые проблемы при отладке. Например, открыв какой-либо из прокси в испекторе вы увидите нижележащий объект. А если этого объекта еще не существовало, то будет выполнен запрос к БД и создание нового объекта. Но именно изменения состояния прокси и хотелось бы избежать. Для этого нужно определить в классе прокси несколько служебных методов. Эти методы определены в классе Object. Так как Proxy не унаследован из Object-а, то определить эти методы должны мы сами.

Пара методов #instVarAt: #instVarAt:put: служит для доступа ко внутренним переменным метода в обход методов для получения или мутации переменных. Нужны эти методы для инструментов разработчика. К примеру, ими пользуется GLORP когда "собирает" объекты из данных полученных из БД, или инспектор, когда считывает состояние объектов. Текст методов можно посмотреть в классе Object. Следующая пара: #basicPrintOn: и #basicPrintString. В них можно подставить вызов соответсвующих методов природы прокси.

На этом подготовительные работы закончены. Переходим к инспектору.

Метод #inspectorClass - возвращает инспектор который будет использоваться если включен режим "простого" инспектора. Обычно не нужно переопределять этот метод.

Метод #inspectorClasses - возвращает масив "инспекторов" которые могут отображать этот объект. Например, для объекта класса Icon этот метод возвращает масив из двух значений: Tools.Trippy.PreviewingInspector и Tools.Trippy.BasicInspector. Соответсвенно, на вышеприведённом снимке можно видеть две закладки "Preview" и "Basic" для двух разных способов отображения класса.

Для класса Proxy можно определить метод #inspectorClasses так, что бы он возвращал Tools.Trippy.BasicInspector. Результат вас несколько удивит (или наоборот - не удивит). Вы увидите список переменных нижележащего объекта, в которые подставлены значения взятые из переменных этого прокси! Так происходит потому, что испектор пользуется методом #class для получения класса инспектируемого объекта и, соответсвенно, списка переменных. Этот метод для класса Proxy определён так, что возвращает класс нижележащего объекта.

Самое простое решение этой проблемы - создать подкласс класса BasicInspector (назовём его ProxyBasicInspector) и изменить методы ответсвенные за получение перечня переменных. Это методы #namedFields и #protectedVariableNames. Их мы скопируем из базового класса, заменив отправку сообщения #class на #proxyClass. Метод #proxyClass определим в классе Proxy:

proxyClass
	^Proxy
Осталось в классе Proxy определить метод #inspectorClasses :
inspectorClasses
	^Array with: Tools.Trippy.ProxyBasicInspector
Вуаля!

Но не будем останавливаться на достигнутом. Инспектор Trippy использует еще несколько методов, переопределив которые, без особого труда, можно расширить выводимую испектором информацию.

Метод класса #protectedInstVarNames - возвращает список имён "защищенных" переменных. Защищенные переменные - это переменные, значение которых нельзя изменить из инспектора. Эти переменные выводятся в инспекторе со значком диеза "#" после имени переменной. Мы определим этот метод так:

protectedInstVarNames
	^self instVarNames
То есть, все переменные в объекте класса Proxy являются защищенными

Метод #inspectorExtraAttributes - позволяет создать "виртуальные переменные". То есть значение "переменной" вы можете задать сами при помощи блока. Пример ниже создаёт две переменные 'class' и 'query string'. Такие переменные в списке переменных в инспекторе идут в самом начале и предваряются знаком "-". В любом инспекторе всегда присутсвует минимум одна подобная переменная - self.

inspectorExtraAttributes
^Array 
  with: (Tools.Trippy.DerivedAttribute 
	label: 'class' 
	valueBlock: [self class])
  with: (Tools.Trippy.DerivedAttribute 
	label: 'query string'
	valueBlock: 
		[(query notNil and: [session notNil]) 
			ifTrue: 
				[parameters ifNil: 
					[parameters := Dictionary new: 0].
				query session ifNil: 
					[query session: session].
				(query sqlWith: parameters) sqlString]
			ifFalse: ['no query']])

Метод #inspectorActions - возвращает масив из дополнительных действий которые можно произвести на объектом. Эти действия будут включены в меню инспектора. Каждое действие создаётся посылкой классу Action сообщения #label:block: или пункта меню, block: - блок который будет выполнен при выборе соответсвующего пункта, enablement: - позволяет задать условие при котором соответсвующий пункт меню становится активным. Условие задаётся блоком, который возвращает true или false. Пример:

inspectorActions
    ^Array with: (Tools.Trippy.Action 
		label: 'Instantiate'
		block: [self getValue]
		enablement: [self isInstantiated not])
	with: (Tools.Trippy.Action 
		label: 'Uninstantiate'
		block: [self uninstantiate]
		enablement: [self isInstantiated])

Метод #inspectorCollaborators. Инспектор Trippy имеет пункт меню 'Go', который позволяет быстро перейти к отображению какого либо предопределённого объекта. Элементы, возвращаемые в ответ на сообщение #inspectorCollaborators добавляются в пункты этого меню. Пример:

inspectorCollaborators
	^Array with: (Tools.Trippy.Collaborator 
		label: 'Value of the proxy'
		block: [self getValue])
После определения этого метода, при инспектировании объекта класса Proxy в пункте меню 'Go' помимо стандартных пунктов появится пункт 'Value of the proxy', выбрав который вы увидите в инспекторе нижележащий объект.

Метод #inspectorHierarchies - позволяет определить какой иерархии принадлежит инспектируемый объект. Для того чтобы создать идентификатор иерархии нужно послать классу Hierarchy сообщение иерархию. Тип Symbol. Если один объект возвращает несколько иерархий, то они должны иметь разные идентификаторы. label: текст, описывающий иерархию. parentBlock: - блок, в качестве параметра принимающий объект. Результат выполнения блока - родительский объект в иерархии. childrenBlock: - коллекция "детей" данного объекта в иерархии. Так как в нашем примере прокси не участвуют ни в каких иерархиях, то определим этот метод так:

inspectorHierarchies
	^#()

Результат на снимке:
Инспектор для прокси

Краткие выводы: определив в своём классе несколько методов вы можете значительно расширить возможности по инспектированию объектов этого класса. Переопределять можно такие методы: #inspectorActions, #inspectorExtraAttributes, #inspectorCollaborators, #inspectorHierarchies и метод класса #protectedInstVarNames.




Есть комментарии? Пишите.