Как создать переменную класса, которая не разделяет состояние между детьми - PullRequest
2 голосов
/ 07 мая 2019

Я хочу создать способ, чтобы классы Children выражали некоторые бизнес-определения на уровне класса.

Я пытался использовать переменные класса для этого, но обнаружил, что они разделяют состояние между всеми классами, поэтому, как только я определю второй класс, класс «@@ attribute» var изменит свое значение для всех смежных экземпляров класса.

class Parent
  def self.type(value)
    @@_type = value
  end

  def render
    puts @@_type
  end
end

class Children < Parent
  type "name"
end

Children.new.render # Result: name. Expected: name

class Children2 < Parent
  type "title"
end

Children2.new.render # Result: title. Expected: title
Children.new.render # Result: title. Expected: name

Как я могу создать этот DSL самым простым и прямым способом? Это общий шаблон для нескольких Ruby Gems, таких как HTTParty, Virtus и т. Д.

Я даже пытался взглянуть на их исходный код, чтобы понять, как это делается, но он кажется слишком сложным для того, что я хочу.

Спасибо за вашу помощь!

Ответы [ 2 ]

1 голос
/ 07 мая 2019

Переменные класса - это один из триумвиратов инструментов Ruby, который редко используют, если вообще используют, опытные Rubiests. 1 .Вместо этого вы хотите использовать переменную экземпляра уровня класса , Parent являющуюся экземпляром класса Class.

class Parent
  def self.type=(value)
    @type = value
  end

  def self.type
    @type
  end

  def render
    puts self.class.type
  end
end

class Children < Parent
  self.type = "name"
end

Children.new.render
  #=> "name"

class Children2 < Parent
  self.type = "title"
end

Children2.new.render
  #=> "title"
Children.new.render
  #=> "name"

Во-первых, метод класса type= называется «сеттером», а метод класса «тип» называется «геттером».У вас был сеттер type, принимающий аргумент.Если вы сделаете это, как вы просто получите его значение?Чтобы использовать его как геттер, вам нужно сделать что-то вроде следующего:

  def self.type=(value=nil)
    if value.nil?
      @type
    else
      @type = value
    end
  end

Здесь было бы более целесообразно просто определить геттер

  def self.type
    @type
  end

и иметьнет сеттера, просто пишу, например, @type = "name".

Это глупо и работает, только если вы не хотите устанавливать @type на nil.Вы также можете оставить свой метод как установщик и использовать self.class.instance_variable_get(:@type), чтобы получить его значение, но это также ужасно.Лучше всего иметь сеттер и геттер.

При использовании сеттера нам нужно предварять type с помощью self., чтобы сказать Ruby, что мы хотим вызвать геттер и не устанавливать локальную переменную type вданное значение.Конечно, вместо этого мы могли бы просто написать, например, `@type =" title ".

Обычный способ создания установщика и получателя - написать attr_accessor :type (вызывая метод класса Module# attr_accessor ).Так как методы класса хранятся в одноэлементном классе класса, это можно сделать следующим образом: 2 :

class Parent
  class << self
    attr_accessor :type
  end
  def render
    puts self.class.type
  end
end

class Children < Parent
  self.type = "name"
end

Children.new.render
  #=> "name"

class Children2 < Parent
  self.type = "title"
end

Теперь рассмотрим метод экземпляра Parent#render.Будучи методом экземпляра, его получатель является экземпляром класса (или подкласса), скажем, parent = Parent.new.Это означает, что когда render вызывается в методе, self равно parent.Однако мы хотим вызвать метод класса type.Поэтому мы должны преобразовать parent в Parent, что мы делаем с self.class.

1.Две другие (на мой взгляд, конечно) глобальные переменные и циклы for.Их популярность среди новичков в Ruby, вероятно, связана с тем, что они, как правило, дебютируют в главе 1 многих книг по изучению Ruby.

2.Есть много способов определить attr_accessor в синглтон-классе Parent.Два других singleton_class.instance_eval do { attr_accessor :type } и singleton_class.send(:attr_accessor, :type).

0 голосов
/ 07 мая 2019

Хорошо, я понятия не имею, почему это работает, но это работает так:

class Parent
  class << self
    attr_accessor :_type

    def type(value)
      self._type = value
    end
  end

  def render
    puts self.class._type
  end
end

Я действительно хочу понять, почему, но кажется, что "self.class" и "class << self"много темного для меня. </p>

Свет?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...