Created
September 15, 2021 11:44
-
-
Save forsbergplustwo/8f2fdb8b1d2b3dea39631d881010c3f2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# # USAGE | |
# | |
# # Include the concern in the class | |
# class Settings::CreateSetting | |
# include SimpleServiceObject | |
# | |
# # optional: include validation | |
# validates :shop, presence: true | |
# | |
# # mandatory: initialize the command with keyword arguments | |
# def initialize(shop) | |
# @shop = shop | |
# end | |
# | |
# # mandatory: define a #call method. | |
# # Return value is available through #payload | |
# # Errors are available through #errors (uses ActiveModel for compatibility with view helpers, I18n etc.) | |
# def call | |
# create_setting | |
# end | |
# | |
# private | |
# | |
# attr_reader :shop | |
# | |
# def create_setting | |
# languages = [] | |
# languages << shop.default_locale ||= "en" | |
# | |
# Setting.create(shop: shop, languages: languages) | |
# end | |
# end | |
# | |
# #In your locale file you can setup custom errors | |
# | |
# #config/locales/en.yml | |
# en: | |
# activemodel: | |
# errors: | |
# models: | |
# settings | |
# create_setting: | |
# failure: "Could not create setting!" | |
# | |
# | |
# # In your Controller: | |
# | |
# class SettingsController < ApplicationController | |
# def create | |
# # initialize and execute the service | |
# # NOTE: `.call` is a shortcut for `.new(args).call` | |
# service = Settings::CreateSetting.call(current_shop) | |
# | |
# # service.payload will contain the return value (even if invalid) | |
# | |
# @setting = service.payload | |
# | |
# # check service outcome | |
# if service.success? | |
# redirect_to @setting | |
# else | |
# flash.now[:alert] = t(service.errors.full_messages.to_sentence) | |
# render :new | |
# # in the view it will have access to @setting and can | |
# # show the validation errors normally @setting.errors... | |
# # and generate forms: <%= form_with model: @setting do |form| %> | |
# end | |
# end | |
# end | |
# | |
# # TEST WITH RSPEC | |
# | |
# require "rails_helper" | |
# | |
# RSpec.describe Settings::CreateSetting do | |
# describe ".call" do | |
# it "creates setting with default language" do | |
# shop = create(:shop) | |
# expect(shop.setting).to be nil | |
# | |
# service = Settings::CreateSetting.call(shop: shop) | |
# | |
# expect(service.errors).to be_empty | |
# expect(service.success?).to be true | |
# expect(service.payload).to be_instance_of Setting | |
# | |
# expect(shop.setting).not_to be nil | |
# expect(shop.setting.languages).to eq(["en"]) | |
# end | |
# end | |
# end | |
# | |
# # Inspired by https://github.com/nebulab/simple_command | |
# | |
module SimpleServiceObject | |
extend ActiveSupport::Concern | |
include ActiveModel::Validations | |
included do | |
attr_reader :payload | |
end | |
class_methods do | |
def call(*args, **kwargs) | |
new(*args, **kwargs)._call | |
end | |
end | |
def _call | |
fail NotImplementedError unless defined?(call) | |
@called = true | |
if valid? | |
@payload = call | |
# If an invalid ActiveRecord/ActiveModel record is returned by :call, add a default | |
# :failure error, which also sets :success? to false. Allows us to return invalid | |
# records for easier use in views and forms. | |
errors.add(:base, :failure) if @payload&.errors&.any? | |
end | |
self | |
end | |
def success? | |
called? && !failure? | |
end | |
alias successful? success? | |
def failure? | |
called? && errors.any? | |
end | |
# Provided by ActiveModel::Validations, fail loudly if removed. | |
def errors | |
fail NotImplementedError unless defined?(super) | |
super | |
end | |
private | |
def called? | |
@called ||= false | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment