Skip to content

Instantly share code, notes, and snippets.

@kyrylo
Last active August 7, 2024 06:11
Show Gist options
  • Save kyrylo/563a8cb6f44532b58ccb48bf0d30651f to your computer and use it in GitHub Desktop.
Save kyrylo/563a8cb6f44532b58ccb48bf0d30651f to your computer and use it in GitHub Desktop.
How to define service objects in Rails: the simple way
# frozen_string_literal: true
class ApplicationService
def self.call(...)
new(...).call
end
def initialize(...)
end
end
class CurrentIpService < ApplicationService
def initialize(request)
super
@request = request
end
def call
ip_headers = [
@request.env["HTTP_CF_CONNECTING_IP"],
@request.env["HTTP_CLIENT_IP"],
@request.env["HTTP_X_FORWARDED_FOR"],
@request.env["HTTP_X_FORWARDED"],
@request.env["HTTP_FORWARDED_FOR"],
@request.env["HTTP_FORWARDED"],
@request.env["REMOTE_ADDR"]
]
(ip_headers.find(&:present?) || "127.0.0.1").split(",").first
end
end
CurrentIpService.call(request)
@kyrylo
Copy link
Author

kyrylo commented Aug 5, 2024

I use service objects in all of my Rails projects.

👷‍♂️ This is how I engineer them:

  1. Define the ApplicationService class with the call class method that does nothing but accepts any params and initializes a service, then calls the call instance method immediately

  2. Define the initialize instance method that does nothing but accepts any params

  3. Define a service that inherits from ApplicationService and defines the call method. If a service requires params, I define the initialize method, call super, then use the params. If you don't need params, then you don't need to define initialize at all

  4. Implement the call method.

That's all!

https://x.com/kyrylosilin/status/1820473128365019139

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment