Ruby: Estado Implicito em um Bloco

spec = create_spec{
			:id > 1
			:id < 3
		}

quero que isso retorne:

[[:id, :>, 1],  [:id, :<, 3]]

A parte de method_missing e os diabos tá beleza.

O problema é: como eu mantenho o estado dentro do bloco? Precisava de algo como:

spec = create_spec{|lista|
			lista << :id > 1
			lista << :id < 3
		}

Só que a |lista| fosse implícita para não sujar a sintaxe.

Primeira versão: Gambiarra

[code]
class Symbol

def == arg
	add_to_my_criteria Sbqs::try_to_create_conditional_from "==", arg
	@@my_criteria	
end		

def method_missing method,arg
	add_to_my_criteria Sbqs::try_to_create_conditional_from method.id2name,arg
	@@my_criteria
end

def Symbol::clear
	@@my_criteria=[]
end

private
def add_to_my_criteria criteria
	@@my_criteria ||= []
	@@my_criteria << criteria
end

end[/code]

Invocando:

	def Sbqs.build_spec_object_from spec_definition_block		
		#this really sucks!
		result = spec_definition_block.call
		Symbol::clear
		result
	end

Por favor, me livrem desta gambiarra! :cry:

É uma DSL de brinquedo apra testar alguns conceitos mas queria saber como resolver isso me Ruby. E de um modo thread-sfafe, plz.

Nao rola mudar um pouquinho so a DSL e ficar com:

spec = Spec.new { [:id > 1, :id < 3] }

Com arrays tudo fica mais facil :slight_smile:

Phillip, já deu uma olhada?

http://www.zenspider.com/ZSS/Products/ParseTree/

Pensei numa coisa enquanto estava engarrafado com 40 graus hoje em frente á praia de Ipanema:


spec = create_spec {
 and :id&lt;10, :id&gt;1
}

#ou

spec = create_spec {
 [:id&lt;10]and[:id&gt;1]
}

Eu tinha feito tudo ser and para não criar complexidade mas acho que neste caso simplifica. Já que and pe keyword o que vocês recomendam no lugar? "combine"?

Olhando, Kung

Resolvi seguindo a sugestão dos arrays. Acabei conseguindo resolver a gambiarra da sobrescrita do === no Symbol com uma versão que identifica se pode aplicar o overload ou não.

http://dsl-lab.googlecode.com/svn/trunk/hash-ql/lib/hash-ql.rb

Phillip, só sendo chato, procura usar () nas definições de métodos com parâmetros e do…end para blocos com mais de uma linha.

Eu sei que é gosto pessoal, mas isso já é quase code convention em Ruby.

E matar uma das coisas mais legais de Ruby que é a sintaxe flexível? Se tirarem isso da linguagem vai ser quase tão difícil fazer Internal DSLs em Ruby quanto é em Java.

não tem problema nenhum não usar os () na chamada dos métodos. A convenção é só para a hora de definir o método.

Quanto aos blocos, não acho que prejudique a “expressividade” das dsls.

Acho que nest eponto você está certo. Não há problema em utilizar (), mesmo eu achando inútil.

Aí não :slight_smile:

Utilizar blocos do…end de mais de uma linha é convenção de Ruby. Existem dois problemas:

1 - Quando se utiliza uma DSL não se está programado (pelo menos não naquele trecho específico) em Ruby, se programa em uma outra linguagem que é criada. Esta linguagem não tem nenhuma obrigação de seguir as convenções da sua host language, pelo contrário.

2 - Mesmo que meu programa seja escrito na linguagem Ruby (ou java, ou C#, ou…) seguir convenções não traz sempre uma boa coisa. Utilizar Fluent Interfaces é exatamente abdicar das convenções para maior expressividade.

é, cai pro gosto pessoal mesmo. Eu continuo preferindo do…end para várias linhas, mesmo que nem fosse convenção! :wink:

Não exatamente gosto mas adequação ao domínio. imagine uma DSL para sistemas de equações, você usaria do…end ?

Não acho menos feio do que ter de colocar ‘{’ e ‘}’ onde naturalmente não seria obrigado. DSLs internas tem algumas restrições, não há como fugir disso.

As restrições (de sintaxe) da Internal DSL só existem quando a host language não é flexível o suficiente para que a DSL tenha uma sintaxe específica. Em Java escrever uma DSL Interna é um problema grande exatamente porque a linguagem não é flexível mas em Ruby não existe tanta limitação.

Minha opinião é que você acha ‘feio’ porque está lendo código Ruby e não lendo código na linguagem que ele foi escrito, que é uma linguagem parecida (por restrições técnicas) com Ruby mas diferente. Se você cria uma DSL que segue a convenção da host language você não tem uma DSL, tem uma API. Talvez tenha até uma Fluent Interface, depende do caso, mas não tem uma nova linguagem, continua preso a antiga.

Veja um exemplo de JMock:

allowing (bank).withdraw(Money.ZERO); 
    will(throwException(new IllegalArgumentException("you cannot withdraw nothing!");

Isso não é Java, é a linguagem do JMock. Se fosse Java o checkstyle ficaria louco.

Veja a DSL do LOOP em Common Lisp:

[code]
(loop for x in '(a b c d e)
do (print x) )
/code]

‘for x in …’ não é lisp, é a DSL do LOOP. Seria possível eu escrever a DSL utilizando o mesmo estilo de Lisp mas não faz sentido neste domínio.

(droga, tinha respondido isso ontem, mas deu crash no safari e eu perdi o texto todo…)

Eram justamente destas restrições que eu estava falando.

De resto concordo plenamente com você. É bem verdade que fui um pouco chato pq tenho aversão a blocos de várias linhas com ‘{ … }’. Você já deixou bem claro que não é obrigação usar sempre as convenções quando o objetivo é uma DSL; e eu concordo. Porém (na minha opinião), no seu caso não haveria problema algum em usar o do…end:

spec = create_spec do
  [:id&lt;10]and[:id&gt;1]  
end

Eu até prefiro assim. Não me expressei muito bem, mas no fundo o que eu queria dizer é apenas para ter preferência pela convenção (já que ela existe) nestes casos onde é o gosto pessoal que manda. Realmente, em uma DSL para expressões aritméticas ‘{ … }’ fica mais elegante.

ps.: Phillip, comparar com Java foi sacanagem demais. O abismo entre as convenções e a falta de flexibilidade é enorme (o que não vale para Ruby). Se eu fosse apelar nessa linha exagerando um pouco, não acho que DSL nenhuma justifica ter nomes de classe minúsculo: minha_classe.meuMetodoEstatico(). CamelCase para classes seria quase que uma convenção "inquebrável". Mas como eu não vou apelar mesmo… :wink: