Как убедить GroovyShell поддерживать вызовы состояния через eval ()? - PullRequest
3 голосов
/ 05 сентября 2008

Я пытаюсь использовать Groovy для создания интерактивного режима сценариев / макроса для моего приложения. Приложение - OSGi, и большая часть информации, которая может понадобиться сценариям, не известна заранее. Я подумал, что мог бы использовать GroovyShell и вызывать eval () несколько раз, непрерывно добавляя пространство имен по мере загрузки пакетов OSGi. GroovyShell поддерживает состояние переменной для нескольких вызовов eval, но не для определения классов или методов.

цель: создать базовый класс во время запуска. По мере загрузки пакетов OSGi создавайте производные классы по мере необходимости.

Ответы [ 3 ]

2 голосов
/ 15 сентября 2008

Я не уверен, что вы имеете в виду относительно объявленных классов, не существующих между evals, следующие два сценария работают, как и ожидалось, при уклонении один за другим:

class C {{println 'hi'}}
new C()

...

new C()

Однако методы становятся связанными с классом, который их объявил, и GroovyShell создает новый класс для каждого экземпляра. Если вам не нужно возвращаемое значение какого-либо из сценариев, и они действительно являются сценариями (не классами с основными методами), вы можете прикрепить следующее в конец каждого оцененного сценария.

Class klass = this.getClass()
this.getMetaClass().getMethods().each {
  if (it.declaringClass.cachedClass == klass) {
    binding[it.name] = this.&"$it.name"
  }
}

Если вы зависите от возвращаемого значения, вы можете вручную управлять оценкой и запускать сценарий как часть вашего анализа (предупреждение, непроверенный код приведен ниже, только для иллюстративного использования) ...

String scriptText = ...
Script script = shell.parse(scriptText)
def returnValue = script.run()
Class klass = script.getClass()
script.getMetaClass().getMethods().each {
  if (it.declaringClass.cachedClass == klass) {
    shell.context[it.name] = this.&"$it.name"
  }
}
// do whatever with returnValue...

Есть одно последнее предостережение, которое, я уверен, вы знаете. Статически типизированные переменные не хранятся между evals, поскольку они не сохраняются в привязке. Таким образом, в предыдущем скрипте переменная 'klass' не будет храниться между вызовами скрипта и исчезнет. Чтобы исправить это, просто удалите объявления типов при первом использовании всех переменных, это означает, что они будут прочитаны и записаны в привязку.

1 голос
/ 04 сентября 2009

Заканчивается внедрение кода перед каждой компиляцией скрипта. Конечная цель заключается в том, чтобы пользовательский сценарий имел доступный для использования язык, специфичный для домена.

0 голосов
/ 05 сентября 2008

Это может быть то, что вы ищете?

С Отличный в действии

def binding = new Binding(x: 6, y: 4)
def shell = new GroovyShell(binding)
def expression = '''f = x * y'''
shell.evaluate(expression)
assert binding.getVariable("f") == 24

Надлежащее использование Binding позволит вам поддерживать состояние?

...