In my Indieweb Syndication code I was looking for a way to DRY-up two similar methods. I knew what I wanted to do, but wasn’t sure how to do it. I wanted to have some kind of template method (you’ll have to excuse me if my terminology is wrong) with placeholders for the parts where code diverged. I.e. I was thinking something along the lines of liquid or erb templates/placeholders for web pages but for actual code generation.

Here is a simplified example of what I started with:

def get(item)
	succeeded = false
	failed = false

	until succeeded | failed
		response = open("http://somewhere.com/?get=#{item}")
		response_json = JSON.parse(response.string)
		if (response.status[0] == "200") & response_json["posts"].any?
			#Same code here for get and add
			succeeded = true
		elsif (response.status[0] == "200") & response_json["posts"].empty?
			#Same code here for get and add
			failed = true
		end
	end
	succeeded
end

def add(item)
	succeeded = false
	failed = false

	until succeeded | failed
		response = open("http://somewhere.com/?add=#{item}")
		response_json = JSON.parse(response.string)
		if (response.status[0] == "200") & (response_json["result_code"] == "done")
			#Same code here for get and add
			succeeded = true
		elsif (response.status[0] == "200") & (response_json["result_code"] == "item already exists")
			#Same code here for get and add
			failed = true
		end
	end
	succeeded
end

You can see the two methods share an overall structure and also pieces of code within that structure. It would have been easy to write methods for the shared pieces of code within the conditionals, but that would have still left me with duplicated structures. After lots of reading around (it’s odd how all useful Ruby posts seem to have been written around 2006-2008) I came up with this idea:

{ 
	"get" => {
		"lambda_1" => lambda { |response_json| response_json["posts"].any? },
		"lambda_2" => lambda { |response_json| response_json["posts"].empty? },
	},
	"add" => {
		"lambda_1" =>  lambda { |response_json| (response_json["result_code"] == "done") },
		"lambda_2" => lambda { |response_json| (response_json["result_code"] == "item already exists") },
	}
}.each do |method, lambdas|
	define_method(method) do |item|

		succeeded = false
		failed = false

		until succeeded | failed
			response = open("http://somewhere.com/?#{method}=#{item}")
			response_json = JSON.parse(response.string)
			if (response.status[0] == "200") & lambdas["lambda_1"].call(response_json)
				#Same things here
				succeeded = true
			elsif (response.status[0] == "200") & lambdas["lambda_2"].call(response_json)
				#Same things here
				failed = true
			end
		end
		succeeded
	end
end

This uses a hash to hold the code differences for each method, via the use of a lambda. This hash is then looped through and define_method used to build the methods, with the appropriate lambda “replacing” the “placeholders”.

This is perhaps a bit messy, but I don’t mind it as it is just for me. I could tidy up the visual appearance of it somewhat just by not attaching the .each do on the hash definition directly and instead using a variable.

There are pros and cons of this approach. The lambdas are a bit verbose. I.e I have to have lamba { |response_json| response_json["posts"].any? } whereas if this was just a text placeholder it would only be response_json["posts"].any?. Also, the placeholders aren’t true placeholders because you have to remember to call the required variable in the lambda. But, on the plus side there is no use of eval and all code is proper code; it probably would have been possible to use liquid/erb to achieve the same end result, but I’m pretty sure I would have not been able to escape using eval or having to define certain parts of code as text.

You almost certainly don’t want to write code like I do, but if you are interested here is the actual before and after code.