Existe um Ruby, ou Ruby-ism para not_nil? oposto de nulo? método?

87

Não tenho experiência em Ruby, então meu código parece "feio" e não idiomático:

def logged_in?
  !user.nil?
end

Eu prefiro ter algo como

def logged_in?
  user.not_nil?
end

Mas não consigo encontrar um método que opõe nil?

Berkes
fonte

Respostas:

51

quando você estiver usando o ActiveSupport, há user.present? http://api.rubyonrails.org/classes/Object.html#method-i-present%3F , para verificar apenas se não for nulo, por que não usar

def logged_in?
  user # or !!user if you really want boolean's
end
lwe
fonte
48
Cuidado: present?requer uma string não em branco. ! "".nil?retorna verdadeiro, mas "".present?retorna falso.
lambshaanxy
9
Cuidado 2: também observarei que !! o usuário NÃO distingue entre o usuário ser nulo e o usuário ser falso; o uso de double-bang combina esses dois. Então, se você realmente deseja determinar se um objeto não é nulo (ou seja, é: verdadeiro, falso, 0, "", qualquer coisa diferente de nulo), você precisa usar a abordagem 'feia' que o berkes não gosta ou o monkeypatch que @Tempus propõe abaixo . Claro que neste caso não nulo é não necessário (um usuário em Rails), a abordagem adoptada por Samo é o menos feio, imo.
likethesky de
12
false.present? == false !false.nil? == true
Dudo
3
Esta resposta não responde de forma alguma à pergunta feita. É uma resposta a este problema de implementação específico.
Ekkstein
3
Esta resposta não está correta. false.nil? é falso, enquanto false.present? TAMBÉM é falso!
Mike
49

Você parece excessivamente preocupado com booleanos.

def logged_in?
  user
end

Se o usuário for nulo, então conectado? retornará um valor "falsey". Caso contrário, ele retornará um objeto. Em Ruby, não precisamos retornar verdadeiro ou falso, uma vez que temos os valores "truthy" e "falsey" como em JavaScript.

Atualizar

Se você estiver usando Rails, você pode fazer isso de forma mais agradável usando o present?método:

def logged_in?
  user.present?
end
Samo
fonte
1
A expectativa de um método que termina com a ?é que ele retorne um booleano. !!valueé a maneira clássica de converter qualquer coisa em booleano. Não é exatamente o mesmo, mas neste caso Object#present?em RoR também é bom.
tokland
isso realmente quebra no Ruby 2.4 com o Path! [43] (erguer) # <ReactOnRails :: AssetsPrecompile>: 0> assets_path.blank? true [44] (pry) # <ReactOnRails :: AssetsPrecompile>: 0> assets_path.to_s "/ var / folders / rp / _k99k0pn0rsb4d3lm9l3dnjh0000gn / T / d20170603-96466-zk7di7PrecnO" [45R] (pry) >: 0> assets_path.present? false [46] (erguer) # <ReactOnRails :: AssetsPrecompile>: 0> caminho_de_matos.nil? false
justingordon
23

Cuidado com as outras respostas apresentadas present?como uma resposta à sua pergunta.

present?é o oposto de blank?em trilhos.

present?verifica se há um valor significativo. Essas coisas podem falhar em uma present?verificação:

"".present? # false
"    ".present? # false
[].present? # false
false.present? # false
YourActiveRecordModel.where("false = true").present? # false

Considerando que um !nil?cheque dá:

!"".nil? # true
!"    ".nil? # true
![].nil? # true
!false.nil? # true
!YourActiveRecordModel.where("false = true").nil? # true

nil?verifica se um objeto é realmente nil. Qualquer outra coisa: uma cadeia vazia, 0, false, qualquer que seja, não é nil.

present?é muito útil, mas definitivamente não é o oposto de nil?. Confundir os dois pode levar a erros inesperados.

Pois o seu caso de uso present?funcionará, mas é sempre bom estar ciente da diferença.

A Fader Darkly
fonte
Deve ser incluído na resposta aceita. false.blank?não é o mesmo quefalse.nil?
Yason
17

Talvez esta seja uma abordagem:

class Object
  def not_nil?
    !nil?
  end
end
Geo
fonte
Boa ideia. Eu concluo que não há not_nil? em Ruby. Mas isso não deveria ser!self.nil? antes !nil?ou está selfimplícito?
berkes em
3
Você não precisa de si mesmo. Estará implícito.
Geo
Self está implícito na leitura de métodos de instância (ou acessores, que na verdade são apenas métodos). Ao definir valores, uma variável local para o método será criada antes que Ruby verifique a instância da classe para um método setter com o mesmo nome. Regra geral: se você tiver um attr_accessor chamado xxx, use "self.xxx = 3" (definindo um valor) ou "temp = xxx" (lendo o valor). Usar "xxx = 3" não atualizará o acessador, apenas crie uma nova variável no escopo do método.
A Fader Darkly
4

Você pode apenas usar o seguinte:

if object
  p "object exists"
else
  p "object does not exist"
end

Isso não funciona apenas para nil, mas também para false etc., então você deve testar para ver se funciona em seu caso de uso.

Bitterzoet
fonte
4

Posso oferecer o !método Ruby no resultado do nil?método.

def logged_in?
  user.nil?.!
end

Tão esotérico que o IDE RubyMine irá sinalizar como um erro. ;-)

Christopher Oezbek
fonte
2

Cheguei a esta questão procurando por um método de objeto, para que pudesse usar a Symbol#to_procabreviação em vez de um bloco; Acho arr.find(&:not_nil?)um pouco mais legível do que arr.find { |e| !e.nil? }.

O método que encontrei é Object#itself. No meu uso, eu queria encontrar o valor em um hash para a chave name, onde em alguns casos essa chave foi acidentalmente capitalizada como Name. Esse one-liner é o seguinte:

# Extract values for several possible keys 
#   and find the first non-nil one
["Name", "name"].map { |k| my_hash[k] }.find(&:itself)

Conforme observado em outras respostas , isso falhará espetacularmente nos casos em que você estiver testando um booleano.

Ian
fonte
Como isso é diferente de my_hash.values_at("Name", "name").find?
berkes
É exatamente o mesmo. Eu tinha alguma outra lógica no meu original mapque removi para simplificar neste exemplo, então como está escrito aqui é intercambiável com values_at. A parte importante é o que é passado find.
Ian