2017-07-28 2 views
2

Ma première classe qui a constante. Je veux que cela fonctionne comme une valeur dynamique.Get self comme invocation de classe dans le parent en Ruby

class Post < ActiveRecord::Base 
    TEST = ["#{self.name}", "test1"] 
end 


class FakePost < Post 
end 

En rails console j'essaie d'accéder constant TEST avec FakePost mais il est toujours montrer self comme objet « Post ». Y'a-t-il une quelconque façon de réussir cela?

actuel:

RIR (principal): 004: 0> FakePost :: Test de

=> [ "Post", "test1"]

Mais je Je veux retourner ce résultat quand j'accède à Constant via Post non FakePost vi.

attendu:

RIR (principal): 004: 0> FakePost :: Test de

=> [ "FakePost", "test1"]

Répondre

3

Le problème est que le code TEST = ["#{self.name}", "test1"] n'est interprété qu'une fois, lorsque la classe Post est instanciée. Après cela, il sera fixé comme ["Post", "test1"].

Du haut de ma tête, je ne peux pas penser à une façon de faire ce travail avec une constante, mais si vous deviez le remplacer par une méthode de classe, il fonctionne très bien:

class Post 
    def self.TEST 
    [self.name, "test1"] 
    end 
end 

class FakePost < Post 
end 

Post.TEST 
#=> ["Post", "test1"] 

FakePost.TEST 
#=> ["FakePost", "test1"] 

La raison cela fonctionne est que le code à l'intérieur de la méthode de classe est interprété à l'exécution (c'est-à-dire lorsque vous appelez la méthode), plutôt que lorsque la classe est interprétée. Le calendrier pour les deux cas est la suivante:

Avec TEST Constant:

  1. classe Post est instancié et interprété ligne par ligne.
  2. self.name est interprété et renvoie 'Post', car self est actuellement la classe Post.
  3. Post::TEST est réglé de manière irrévocable à ["Post", "test1"]
  4. FakePost classe est instanciée et hérite de Post, y compris le déjà instanciée TEST constante de ["Post", "test1"]
  5. FakePost::TEST == Post::TEST == ["Post", "test1"] #=> true

Avec la méthode TEST de classe:

  1. La classe Post est instanciée et interprétée d ligne par ligne.
  2. La méthode de classe de poste TEST est ajoutée (mais pas encore interprétée).La classe est instanciée et hérite de la méthode de classe TEST de Post. Toujours n'a pas encore été interprété.
  3. Lorsque vous exécutez Post.TEST ou FakePost.TEST, la méthode sera finalement interprétée. self sera maintenant soit Post ou FakePost selon la classe appelée la méthode TEST.
+0

Merci pour la réponse, mais je connais déjà cette réponse, mais il y a beaucoup de places et de contrôles nécessaires si je vais faire un changement, alors je pose une question ici. Je veux une solution pour travailler. – Sourabh

+0

@ user4510998 autant que je sache, ce n'est pas possible comme vous voulez le faire - voir la chronologie dans mon édition pour la raison que cela ne fonctionne pas. – omnikron

+0

Merci, est la meilleure pratique pour créer une méthode pour créer avec upcase? – Sourabh

1

Les constantes ne peuvent pas être modifiées. Par conséquent, lorsque vous vous y référez dans le successeur, elles renvoient la valeur déjà affectée. Utilisez les méthodes de classe.

class Post < ActiveRecord::Base 
    def self.test 
    ["#{self.name}", "test1"] 
    end 
end 

class FakePost < Post 
end 
0

Comme il a été signalé par d'autres, TEST est égal à ["Post", "test1"] lorsque Post est analysé; ce n'est pas différent que la ligne ayant été simplement TEST = ["Post", "test1"].

Pour obtenir le résultat souhaité à partir d'une constante (TEST), vous pouvez définir TEST comme un proc et ensuite appeler ce proc avec self comme argument. Cela pourrait être fait comme suit.

class News 
    TEST = ->(slf) { ["#{slf}", "test1"] } 
end 

class FakeNews < News 
end 

News.instance_eval { self::TEST[self] } 
    #=> ["News", "test1"] 
FakeNews.instance_eval { self::TEST[self] } 
    #=> ["FakeNews", "test1"] 

BasicObject#instance_eval est nécessaire pour faire self égal au récepteur de instance_eval. Proc#[] est la même que Proc#call (ainsi que Proc.yield, Proc#=== et la valeur dépréciée Proc#() [écrite TEST.(self)]). Faites votre choix!