I'm in the process of writing a gem, and I've not been really focused on writing the best possible code, in order to build something in the least amount of time. I now have some time on my hands, so I've decided to try to make it clearer.
This is the code I've had:
Output = Struct.new(:body, :method, :headers, :url)
def initialize
self.method = 'GET'
self.headers = {}
self.url = ''
self.body = ''
end
def sort_headers
self.headers = headers.sort.to_h
end
def to_http
output = "#{method} #{url} HTTP/1.1\n\n"
headers.each do |key, value|
output += "#{key}: #{value}\n"
end
output
end
end
class Parser
def parse(command, format:)
self.output = Output.new
output.sort_headers
case format
when :json
output.to_h.to_json
when :http
output.to_http
else
output.to_h
end
end
end
What's itching me is how Output
knows about the different formats
this gem supports. What happens when another format is introduced?
I've decided to tackle this problem with some good ol' polymorphism.
Output = Struct.new(:body, :method, :headers, :url) do
def initialize
self.method = 'GET'
self.headers = {}
self.url = ''
self.body = ''
end
def sort_headers
self.headers = headers.sort.to_h
end
def convert
raise NotImplementedError
end
end
class HTTPOutput < Output
def convert
output = "#{method} #{url} HTTP/1.1\n"
headers.each do |key, value|
output += "#{key}: #{value}\n"
end
output += "\n#{body}"
output
end
end
class JSONOutput < Output
def convert
to_h.to_json
end
end
class HashOutput < Output
def convert
to_h
end
end
class Parser
def self.parse(command, format: nil)
self.output = create_output(format)
output.sort_headers
output.convert
end
private
OUTPUTS = {
http: HTTPOutput,
json: JSONOutput,
hash: HashOutput
}.freeze
def create_output(format)
OUTPUTS.fetch(format) { HashOutput }.new
end
end
Notice how we know have a nice hash of all the formats we support and how easy it is to introduce another format.