2

Ok so I'm trying to set up my own subclassing structure in ruby to allow functionality that matches the protocol pattern in Swift. My current approach is to create two base classes, one called Protocol which allows the declaration of protocols with x number of required methods, the other is called SwiftClass, which allows subclasses to 'inherit' protocols.

Protocol Class

class Protocol
  @@required_methods = []

  def self.required_method(method_name)
    @@required_methods << method_name
  end

  def self.required_methods
    return @@required_methods
  end
end

Swift Class

class SwiftClass
  @@protocols = []

  def self.inherit_protocol(protocol_name)
     protocol = Object.const_get(protocol_name)
     @@protocols << protocol
  end

  def self.inherit_protocols(*protocol_names)
    protocol_names.each do |protocol_name|
      inherit_protocol(protocol_name)
    end
  end

  def self.verify_conforms_to_protocols
    @@protocols.each do |protocol|
      if protocol.superclass != Protocol
        raise "ERROR: #{protocol} must be a subclass of Protocol"
      end
      if (missing_methods = self.missing_methods_for protocol).length > 0
        raise "\nERROR: #{self} does not conform to protocol #{protocol}. \nMISSING METHODS: #{missing_methods.join(", ")}"
      end
    end
  end

  def self.missing_methods_for(protocol)
    protocol.required_methods.select do |required_method|
      !self.method_defined? required_method
    end
  end

end

I can then create a protocol with required methods as such

class SampleProtocol < Protocol
  required_method :number_of_sections_in
  required_method :number_of_rows_in_section
end

And set a class to inherit this protocol

class SampleClass < SwiftClass
  inherit_protocols :SampleProtocol
  verify_conforms_to_protocols
end

So far everything works the way it should. if I run the code it raises the error I set in SwiftClass as expected

ERROR: SampleClass does not conform to protocol SampleProtocol. 
MISSING METHODS: number_of_sections_in, number_of_rows_in_section

And then if I add the methods to SampleClass and run it it no longer raises any errors:

class SampleClass < SwiftClass
  inherit_protocols :SampleProtocol

  def number_of_sections_in; end
  def number_of_rows_in_section; end

  verify_conforms_to_protocols
end

Most of you guys who know anything about ruby can probably already see the glaring issue with this code; the class variables @@required_methods and @@protocols are both defined on the superclass level, so every time I add to them in a subclass it spreads to every other subclass. For example, if I were to add a separate protocol before the declaration of SampleClass

class SomeOtherProtocol < Protocol
   required_method :cell_for_row_at
end

and then run the code I get the error

ERROR: SampleClass does not conform to protocol SampleProtocol. 
MISSING METHODS: cell_for_row_at

Because the @@required_methods collects every required method from every Protocol subclass. The same is true for @@protocols for every subclass of SwiftClass.

So my question is, Can I use a superclass to set class variables for each of it's subclasses? I want the @@required_methods variable to hold different values for each subclass of Protocol and the @@protocols variable to hold different values for each subclass of SwiftClass, but I still want to hold the logic for this in the superclasses so that I don't have to repeat it in each subclass.

If possible, I'd like to keep the exact same structure for inheriting protocols in a SwiftClass, so that all I have to do to inherit a protocol is set "inherit_protocols :SomeProtocol." Same thing goes for adding methods to a new protocol with just the "method: some_method" calls.

moveson
  • 5,103
  • 1
  • 15
  • 32
Josh Hadik
  • 403
  • 5
  • 16
  • If you have two questions, you should ask two questions. In particular, your second question has already been asked and answered multiple times on [so], and so you risk your question getting closed as a duplicate (since at least one of your questions is already answered), as unclear (which of the two questions do you want to have answered) or too broad. – Jörg W Mittag Jan 21 '18 at 19:06
  • Ok got rid of the second question. – Josh Hadik Jan 21 '18 at 19:08
  • i just recently came across an issue like this - see https://stackoverflow.com/questions/48193017/class-variables-being-overwritten-with-inheritance – max pleaner Jan 21 '18 at 19:09
  • Try replacing the `@@` class variables with single `@`. That way they will be unique to each class inheriting from Protocol – max pleaner Jan 21 '18 at 19:09
  • Yeah I've tried changing @@ to @, the problem is that since everything else is being done on the class level (ie self.required_methods etc,) so @required_methods and @protocols won't be defined when those methods are called. (The error I get when attempting this: `required_method': undefined method `<<' for nil:NilClass) – Josh Hadik Jan 21 '18 at 19:14
  • @JörgWMittag If it's not too much trouble, I was wondering if you could point me to one of the questions on Stack Overflow that answered my second question from before: "Is there a way I can set up SwiftClass to call verify_conforms_to_protocols in each subclass automatically, instead of having to put it at the end every class that inherits from SwiftClass?" as I am not entirely sure of how to search for it. – Josh Hadik Jan 21 '18 at 19:54

0 Answers0