Skip to content

Latest commit

 

History

History
57 lines (49 loc) · 3.16 KB

2015-05-04.md

File metadata and controls

57 lines (49 loc) · 3.16 KB

The code options[:repo_name].split(","), in call_contributors is parsing command-line arguments. We'd like this method to be callable from anywhere in our app, so we want to push things like dealing with commandline arguments up higher. In this case, we'd push it all the way up to parse, on line 9. We probably need to do argv[2].to_s.split(","), so that it works correctly whether there are arguments or not.

nil.to_s.split(',')            # => []
'repo1'.to_s.split(',')        # => ["repo1"]
'repo1,repo2'.to_s.split(',')  # => ["repo1", "repo2"]

We'd want to make the option name plural, since it's now an array. And so the check on line 15, argv[2] == nil would become options[:repo_names].empty?, which allows us to think in terms of the names, making it easier to reason about.

This means that now, down in our call_contributors method, we don't need to have two different code paths. The case where there is only one repo name is now the same as having multiple repo names, in both cases, we'll just iterate over the repo names, however many there happen to be, and perform the appropriate job.

To handle some of the complexity of the code in contributors, we can push it down into methods that contain that complexity, and give a name to it, so that we can think in more abstract terms. For example, we have the line number_of_pages = first_page.headers["link"].split(",")[1].match(/&page=(\d+)/)[1].to_i This could be changed to be something like number_of_pages = num_pages(first_page.headers["link"]) Which we would push that code down into:

def self.num_pages(link_header)
  link_header.split(",")[1].match(/&page=(\d+)/)[1].to_i
end

It is possible that there could be several methods extracted from contributors, and we might also do a similar thing for one of the other methods. If this happens, it could be there are sets of methods all on our Github::Json object, that are called together, and independent of each other. For example, we can see that contributors_url_for is only called from this method, so we would have at least two methods pulled out of this. If this seems to be true, that's a good sign that we should separate them, so that we can take each idea independently, and prevent its changes from messing with the rest of the code. A common way to do this is to make an object, though, if it seems pointless to have an object (ie we don't find ourselves feeling a need to store instance variables) then we could make them all singleton methods, like we currently have, but just placed on a different object than Json.

Here is how that could wind up looking. Currently we have response = contributors(options). If we made it into an object, it might look like response = Contributors.new(options).call, which would then imply a renaming like contributors = GetContributors.new(options).call. And if we did something like that, and decided there was no real value in making it an object, we could do it entirely in class methods like this: contributors = GetContributors.call(options)