Paramètres optionnels Ruby

121

Si je définis des fonctions Ruby comme ceci:

def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

Comment puis-je l'appeler en fournissant uniquement les 2 premiers et les derniers arguments? Pourquoi quelque chose comme

ldap_get( base_dn, filter, , X)

possible ou si c'est possible, comment le faire?

Bruno Antunes
la source

Réponses:

131

Ce n'est actuellement pas possible avec le rubis. Vous ne pouvez pas passer d'attributs «vides» aux méthodes. Le plus proche que vous pouvez obtenir est de passer nul:

ldap_get(base_dn, filter, nil, X)

Cependant, cela définira la portée sur nil, pas LDAP :: LDAP_SCOPE_SUBTREE.

Ce que vous pouvez faire est de définir la valeur par défaut dans votre méthode:

def ldap_get(base_dn, filter, scope = nil, attrs = nil)
  scope ||= LDAP::LDAP_SCOPE_SUBTREE
  ... do something ...
end

Maintenant, si vous appelez la méthode comme ci-dessus, le comportement sera celui que vous attendez.

tomafro
la source
21
Un petit problème avec cette méthode: par exemple, si vous essayez de définir la valeur par défaut pour scopetrue et que vous passez false, scope ||= truecela ne fonctionnera pas. Il évalue la même chose nilet le définira àtrue
Joshua Pinter
4
est-ce possible avec la version actuelle de ruby, 3 ans après cette réponse?
dalloliogm
1
@JoshPinter, belle explication. Fondamentalement || = n'est pas a = b ou c, j'ai grincé des dents en voyant xyz||=true. Il dit que si c'est nul, c'est toujours vrai. Si c'est vrai, c'est vrai.
Damon Aw
7
Alors que tout le monde dit à quel point scope ||= truec'est mauvais , je suis surpris que personne n'ait mentionné que la meilleure façon de faire est de le faire scope = LDAP::LDAP_SCOPE_SUBTREE if scope.nil?. Bien sûr, même cela suppose que ce nilsoit une valeur invalide.
Erik Sandberg
1
Mise à jour de cet ancien: une alternative consiste à utiliser la notation de soulignement. Malheureusement, cela a le même effet que de définir le paramètre sur nil. Certains peuvent aimer la notation: ldap_get(base_dn, filter, _, X)(note: je ne sais pas (encore) quand cela a été introduit dans Ruby. Sujet SO intéressant ).
Eric Platon
137

Il est presque toujours préférable d'utiliser un hachage d'options.

def ldap_get(base_dn, filter, options = {})
  options[:scope] ||= LDAP::LDAP_SCOPE_SUBTREE
  ...
end

ldap_get(base_dn, filter, :attrs => X)
jshen
la source
23
Une stratégie courante consiste à avoir un hachage d'options par défaut et à fusionner tout ce qui a été transmis:options = default_options.merge(options)
Nathan Long
7
Je déconseille cela car les options ne vous disent pas ce que la méthode attend ou quelles sont les valeurs par défaut
Bron Davies
53

Le temps a évolué et depuis la version 2, Ruby prend en charge les paramètres nommés:

def ldap_get ( base_dn, filter, scope: "some_scope", attrs: nil )
  p attrs
end

ldap_get("first_arg", "second_arg", attrs: "attr1, attr2") # => "attr1, attr2"
Steenslag
la source
1
Vous pouvez également utiliser un double splat pour collecter des arguments de mot-clé non définis supplémentaires. Ceci est lié à ce problème: stackoverflow.com/a/35259850/160363
Henry Tseng
3

Il n'est pas possible de le faire comme vous l'avez défini ldap_get. Cependant, si vous définissez ldap_getcomme ceci:

def ldap_get ( base_dn, filter, attrs=nil, scope=LDAP::LDAP_SCOPE_SUBTREE )

Maintenant vous pouvez:

ldap_get( base_dn, filter, X )

Mais maintenant vous avez le problème que vous ne pouvez pas l'appeler avec les deux premiers arguments et le dernier argument (le même problème qu'avant mais maintenant le dernier argument est différent).

La raison en est simple: chaque argument de Ruby n'est pas obligé d'avoir une valeur par défaut, vous ne pouvez donc pas l'appeler comme vous l'avez spécifié. Dans votre cas, par exemple, les deux premiers arguments n'ont pas de valeurs par défaut.

Chris Bunch
la source
1

1) Vous ne pouvez pas surcharger la méthode ( pourquoi ruby ​​ne prend-il pas en charge la surcharge de méthode? ) Alors pourquoi ne pas écrire une nouvelle méthode?

2) J'ai résolu un problème similaire en utilisant l'opérateur splat * pour un tableau de longueur zéro ou plus. Ensuite, si je veux passer un ou plusieurs paramètres que je peux, il est interprété comme un tableau, mais si je veux appeler la méthode sans aucun paramètre, je n'ai rien à transmettre. Voir Ruby Programming Language pages 186/187

rupweb
la source
0

Récemment, j'ai trouvé un moyen de contourner cela. Je voulais créer une méthode dans la classe du tableau avec un paramètre facultatif, pour conserver ou supprimer des éléments du tableau.

La façon dont j'ai simulé cela était de passer un tableau comme paramètre, puis de vérifier si la valeur à cet index était nulle ou non.

class Array
  def ascii_to_text(params)
    param_len = params.length
    if param_len > 3 or param_len < 2 then raise "Invalid number of arguments #{param_len} for 2 || 3." end
    bottom  = params[0]
    top     = params[1]
    keep    = params[2]
    if keep.nil? == false
      if keep == 1
        self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
      else
        raise "Invalid option #{keep} at argument position 3 in #{p params}, must be 1 or nil"
      end
    else
      self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
    end
  end
end

Essayer notre méthode de classe avec différents paramètres:

array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126, 1]) # Convert all ASCII values of 32-126 to their chr value otherwise keep it the same (That's what the optional 1 is for)

production: ["1", "2", "a", "b", "c"]

D'accord, c'est cool qui fonctionne comme prévu. Maintenant, vérifions et voyons ce qui se passe si nous ne transmettons pas la troisième option de paramètre (1) dans le tableau.

array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126]) # Convert all ASCII values of 32-126 to their chr value else remove it (1 isn't a parameter option)

production: ["a", "b", "c"]

Comme vous pouvez le voir, la troisième option du tableau a été supprimée, initiant ainsi une section différente dans la méthode et supprimant toutes les valeurs ASCII qui ne sont pas dans notre plage (32-126)

Sinon, nous aurions pu attribuer la valeur nulle dans les paramètres. Ce qui ressemblerait au bloc de code suivant:

def ascii_to_text(top, bottom, keep = nil)
  if keep.nil?
    self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
  else
    self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
end
Singularité
la source
-1

C'est possible :) Il suffit de changer la définition

def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

à

def ldap_get ( base_dn, filter, *param_array, attrs=nil )
scope = param_array.first || LDAP::LDAP_SCOPE_SUBTREE

scope sera maintenant dans le tableau à sa première place. Lorsque vous fournissez 3 arguments, alors vous aurez assigné base_dn, filter et attrs et param_array sera [] Quand 4 arguments et plus alors param_array sera [argument1, or_more, and_more]

L'inconvénient est ... ce n'est pas une solution claire, vraiment moche. C'est pour répondre qu'il est possible d'omettre l'argument au milieu d'un appel de fonction dans ruby ​​:)

Une autre chose que vous devez faire est de réécrire la valeur par défaut de scope.

m4risU
la source
4
Cette solution est complètement fausse. C'est une erreur de syntaxe d'utiliser un paramètre de valeur par défaut ( attrs=nil) après un splat ( *param_array).
Erik Sandberg
3
-1: Erik a raison. Provoque une erreur de syntaxe dans irb 2.0.0p247. Selon le langage de programmation Ruby , dans Ruby 1.8, le paramètre splat devait être le dernier sauf pour an &parameter, mais dans Ruby 1.9, il pouvait également être suivi de "paramètres ordinaires". Dans les deux cas, un paramètre avec une valeur par défaut était légal après un paramètre avec un splat.
andyg0808
Ruby Programming Language page 186/187 le splat peut être utilisé avec des méthodes. Il doit s'agir du dernier paramètre de la méthode à moins que & ne soit utilisé.
rupweb
Donc AndyG a raison, l'ordre doit être: def ldap_get (base_dn, filter, attrs = nil, * param_array)
rupweb
-1

Vous pouvez le faire avec une application partielle, bien que l'utilisation de variables nommées mène définitivement à un code plus lisible. John Resig a écrit un article de blog en 2008 sur la façon de le faire en JavaScript: http://ejohn.org/blog/partial-functions-in-javascript/

Function.prototype.partial = function(){
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function(){
    var arg = 0;
    for ( var i = 0; i < args.length && arg < arguments.length; i++ )
      if ( args[i] === undefined )
        args[i] = arguments[arg++];
    return fn.apply(this, args);
  };
};

Il serait probablement possible d'appliquer le même principe dans Ruby (sauf pour l'héritage prototypique).

EriF89
la source