2010-06-19 5 views
2

Les amateurs de rubis! J'essaie d'écrire un DSL en ruby ​​et je voudrais être capable de créer des méthodes magiques (pas sûr que ce soit le terme le plus précis pour ce que je veux).Méthodes magiques en Ruby?

Je voudrais être en mesure de faire des choses comme ce qui suit:

a = [1, 2, 3] 
b = 2 

(a contains b) 

Et avoir à résoudre vrai ou faux.

Essentiellement, comment puis-je définir la fonction « contient » pour qu'il prenne un tableau a et une b variable et exécute a.contains?(b), mais sans toute la syntaxe spécifique-ruby associé?

+2

Utilisez ceci: 'a.include? b' – Adrian

+0

Ouais, quel est l'avantage de cela quand il est si facile de faire en utilisant une méthode qui existe déjà? –

Répondre

0

La chose la plus proche que je pouvais penser serait:

def contains var, useless_symbol, arr 
    arr.include? var 
end 

Ensuite, vous pouvez l'appeler comme:

contains b, :in, a 


Je ne pense pas qu'il y ait un moyen de pouvoir utiliser notation infixe dans vos propres fonctions.

+0

pourquoi omettez-vous les virgules? ce serait 'contient b,: in, a' – horseyguy

+0

@banister: Vous avez raison, édité maintenant. – bennybdbc

2

si vous voulez un DSL qui n'utilise pas la syntaxe Ruby, vous avez besoin d'écrire un analyseur au moins pour effectuer la transformation (raganwalds réécrivent lib pourrait être un point de départ, http://github.com/raganwald/rewrite)

Cela dit, tu ne veux pas faire ça. C'est plus de code à maintenir et Ruby a déjà pris beaucoup de décisions difficiles qui rendent difficile l'écriture d'une syntaxe de langage. La programmation en langage naturel n'est pas non plus beaucoup plus facile à utiliser pour les non-programmeurs car l'exactitude du format est l'aspect stimulant (voir applescript par exemple).

1

Vous pouvez abuser method_missing. La chose délicate est que vous ne pouvez pas accéder directement aux variables locales des blocs. Vous devrez capturer la liaison interne des blocs quelque part (malheureusement, block.binding renvoie la liaison externe du bloc).

Vous pouvez exécuter ce code:

DSL.new do 
    a = [1, 2, 3] 
    b = 2 
    a contains b 
end 

avec ce qui suit:

class DSL 
    attr_reader :last_binding 

    def initialize(&block) 
    set_trace_func method(:trace).to_proc 
    instance_eval(&block) 
    set_trace_func nil 
    end 

    def trace(event, file, line, id, binding, klass) 
    if event.to_s == "call" and klass == self.class and id.to_s == "method_missing" 
     @last_binding ||= @current_binding 
     set_trace_func nil 
    else 
     @current_binding = binding 
    end 
    end 

    def lvars 
    eval('local_variables', last_binding).map(&:to_s) 
    end 

    def method_missing(name, *args) 
    name = name.to_s 
    if lvars.include? name 
     eval(name, last_binding).send(*args.flatten) 
    else 
     ["#{name}?", *args] 
    end 
    end 
end 

class Array 
    alias contains? include? 
end 
Questions connexes