There is more to this subject than what is currently in this post but, for now, this is a cool design approach that I lynched from the community-driven Ruby Style Guide.
For a long time I have been quite the fan of Service Objects.
However, for a while I’ve been uncomfortable with how I’ve been using them; they were doing more than one thing and they never needed to be instantiated. Therefore, they would probably be better off as Modules. However, I didn’t want to relinquish being able to use the class method - I liked being able to do ServiceObject.call
.
This is one of my ‘Service Objects’ that I wasn’t happy with being a Service Object:
# lib/services/get_data.rb
require 'curb'
require 'json'
class GetData
class << self
def open_pull_requests(repo_name, repo_query="pulls?state=open")
data = Curl.get("https://api.github.com/repos/sky-uk/" + repo_name + "/" + repo_query) do |http|
http.headers['Authorization'] = 'token ' + ENV['GH_PULLS_ACCESS_TOKEN']
http.headers['User-Agent'] = 'curl/7.43.0'
http.headers['Accept'] = '*/*'
end
data = JSON.parse(data.body_str)
!any_open_pull_requests?(data) ? (return nil) : data
end
private
def any_open_pull_requests?(data)
(data.class == Array) && (data.count > 0)
end
end
end
This is useful because you can do GetData.open_pull_requests(args)
from anywhere in a file that you’ve required get_data.rb
in.
However, it’s arguably not appropriate being a Service Object; arguably, it does more than one ‘thing’.
module_function
to the Rescue
require 'curb'
require 'json'
module GetData
module_function
def open_pull_requests(repo_name, repo_query="pulls?state=open")
data = Curl.get("https://api.github.com/repos/sky-uk/" + repo_name + "/" + repo_query) do |http|
http.headers['Authorization'] = 'token ' + ENV['GH_PULLS_ACCESS_TOKEN']
http.headers['User-Agent'] = 'curl/7.43.0'
http.headers['Accept'] = '*/*'
end
data = JSON.parse(data.body_str)
!any_open_pull_requests?(data) ? (return nil) : data
end
def any_open_pull_requests?(data)
(data.class == Array) && (data.count > 0)
end
end
Benefits:
- Class-Method-Like functionality maintained (
GetData.open_pull_requests(args)
) - More readable
- No need to
include
it anywhere in order to use it.