Estoy interesado en crear una DSL en Ruby para utilizarla en el análisis de actualizaciones de microblog. Específicamente, pensé que podría traducir texto a una cadena de Ruby de la misma manera que la gema Rails permite "4.days.ago". Ya tengo código expresiones regulares que se traducirá el textoCreación de una DSL de "lenguaje semi-natural" en Ruby
@USER_A: give X points to @USER_B for accomplishing some task
@USER_B: take Y points from @USER_A for not giving me enough points
en algo así como
Scorekeeper.new.give(x).to("USER_B").for("accomplishing some task").giver("USER_A")
Scorekeeper.new.take(x).from("USER_A").for("not giving me enough points").giver("USER_B")
Es aceptable para mí para formalizar la sintaxis de las actualizaciones de manera que sólo se proporciona texto estandarizado y analiza, lo que me para procesar inteligentemente las actualizaciones. Por lo tanto, parece que es más una cuestión de cómo implementar la clase DSL. Tengo el siguiente clase stub (eliminado toda la comprobación de errores y reemplazado algunos con comentarios para minimizar pegar):
class Scorekeeper
attr_accessor :score, :user, :reason, :sender
def give(num)
# Can 'give 4' or can 'give a -5'; ensure 'to' called
self.score = num
self
end
def take(num)
# ensure negative and 'from' called
self.score = num < 0 ? num : num * -1
self
end
def plus
self.score > 0
end
def to (str)
self.user = str
self
end
def from(str)
self.user = str
self
end
def for(str)
self.reason = str
self
end
def giver(str)
self.sender = str
self
end
def command
str = plus ? "giving @#{user} #{score} points" : "taking #{score * -1} points from @#{user}"
"@#{sender} is #{str} for #{reason}"
end
end
ejecutando los siguientes comandos:
t = eval('Scorekeeper.new.take(4).from("USER_A").for("not giving me enough points").giver("USER_B")')
p t.command
p t.inspect
produce los resultados esperados:
"@USER_B is taking 4 points from @USER_A for not giving me enough points"
"#<Scorekeeper:0x100152010 @reason=\"not giving me enough points\", @user=\"USER_A\", @score=4, @sender=\"USER_B\">"
Así que mi pregunta es principalmente, ¿Estoy haciendo algo para dispararme en el pie al construir sobre esta implementación? ¿Alguien tiene algún ejemplo de mejora en la clase de DSL o alguna advertencia para mí?
BTW, para obtener la cadena eval, estoy usando sub/gsub y regex, pensé que era la manera más fácil, pero podría estar equivocado.
debo añadir que Hice esto con métodos encadenados solo porque no pude descifrar cómo enviarlo usando la cadena vacía como eval ("Scorekeeper da 4 a USER_A por hacer algo") porque no sabía cómo conseguir el espacio incluido- cadenas en la lista de argumentos del método. Las ideas sobre eso son muy bienvenidas. – JohnMetta