Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dealing with Service Objects

Dealing with Service Objects

Conversation starter about working with service objects in Rails, given at Ruby/Rails Sofia meetup

Radoslav Stankov

November 24, 2016
Tweet

More Decks by Radoslav Stankov

Other Decks in Technology

Transcript

  1. Dealing
    with
    Service Objects
    Radoslav Stankov 23/11/2016

    View Slide

  2. Radoslav Stankov
    @rstankov

    http://rstankov.com

    http://github.com/rstankov

    View Slide

  3. https://www.youtube.com/watch?v=p5EIrSM8dCA

    View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. https://www.youtube.com/watch?v=v6UhbI84nmA

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. ActiveRecord
    Objects

    View Slide

  16. ActiveRecord
    Objects
    Other

    Objects
    Other

    Objects
    Other

    Objects
    Other

    Objects
    Other

    Objects
    Other

    Objects
    Other

    Objects

    View Slide

  17. Service Objects

    View Slide

  18. UseCase Objects

    View Slide

  19. Interactors Objects

    View Slide

  20. Operation Objects

    View Slide

  21. module DoStuff
    extend self
    def call(...)
    ...
    end
    private
    ...
    end

    View Slide

  22. module UserDeactivator
    extend self
    def call(user, active: active)
    return if user.active == active
    user.update! active: active
    user.posts.update_all active: active
    user.votes.update_all active: active
    user.comments.update_all active: active
    user.following_association.update_all active: active
    user.followers_association.update_all active: active
    end
    end

    View Slide

  23. Validations?

    View Slide

  24. module SendPrivateMessage
    extend self
    def call(message, recipient, sender)
    return :invalid unless recipient.present?
    return :invalid unless recipient.receive_private_messages?
    message = Message.new message, sender, recipient
    return :not_saved unless message.save
    MessageMailer.user_message(message).deliver_later
    Metrics.track_create message, by: sender
    :success
    end
    end

    View Slide

  25. Forms?

    View Slide

  26. class RegistrationController < ApplicationController
    def create
    @user = User.new(user_params)
    @user.account = Account.new(account_params)
    @user.account.owner = @user
    if @user.valid? && @user.account.valid?
    @user.save!
    @user.account.save!
    AccountWasCreated.perform_later(account)
    redirect_to user_path(@user)
    else
    render :new
    end
    end
    end

    View Slide

  27. class RegistrationController < ApplicationController
    def create
    @registration = RegistrationForm.new(registration_params)
    if @registration.save
    redirect_to user_path(@registration.user)
    else
    render :new
    end
    end
    end

    View Slide

  28. class RegistrationController < ApplicationController
    def create
    @registration = RegistrationForm.new(registration_params)
    @registration.save
    respond_with @registration
    end
    end

    View Slide

  29. class RegistrationForm
    include ActiveModel::Model
    attr_reader :user, :account
    attr_accessor :first_name, :last_name, :email, :name, :plan, :te
    # user validation
    validates :first_name, presence: true
    validates :last_name, presence: true
    validates :email, presence: true, email: true
    # account validation
    validates :account_name, presence: true
    # form custom validation
    validates :plan, inclusion: {in AccountPlan::VALUES}
    validates :terms_of_service, acceptance: true
    # ensure uniqueness

    View Slide

  30. validates :account_name, presence: true
    # form custom validation
    validates :plan, inclusion: {in AccountPlan::VALUES}
    validates :terms_of_service, acceptance: true
    # ensure uniqueness
    validate :ensure_unique_user_email
    validate :ensure_unique_account_name
    def initialize
    @user = User.new
    @account = Account.new owner: @user
    end
    def update(attributes)
    attributes.each do |name, value|
    public_send "#{name}=", value
    end
    if valid? & user.update(user_attributes) && account.update(acco
    account.users << user
    AccountWasCreated.perform_later(account)
    else

    View Slide

  31. def update(attributes)
    attributes.each do |name, value|
    public_send "#{name}=", value
    end
    if valid? & user.update(user_attributes) && account.update(acco
    account.users << user
    AccountWasCreated.perform_later(account)
    else
    false
    end
    end
    private
    def ensure_unique_user_email
    errors.add :email, 'already taken' if User.where(email: email).
    end
    def ensure_unique_account_name
    errors.add :name, 'already taken' if Account.where(name: name).
    end

    View Slide

  32. else
    false
    end
    end
    private
    def ensure_unique_user_email
    errors.add :email, 'already taken' if User.where(email: email).
    end
    def ensure_unique_account_name
    errors.add :name, 'already taken' if Account.where(name: name).
    end
    def user_attributes
    {first_name: first_name, last_name: last_name, email: email}
    end
    def account_attributes
    {plan: plan, name: name}
    end
    end

    View Slide

  33. https://github.com/RStankov/MiniForm

    View Slide

  34. class RegistrationForm
    include MiniForm::Model
    model :user, attributes: %i(first_name last_name email), save: t
    model :account, attributes: %i(name plan), save: true
    attributes :terms_of_service
    validates :plan, inclusion: {in AccountPlan::VALUES}
    validates :terms_of_service, acceptance: true
    def initialize
    @user = User.new
    @account = Account.new owner: @user
    end
    def perform
    account.users << user
    AccountWasCreated.perform_later(account)
    end
    end

    View Slide

  35. Readability?

    View Slide

  36. Authorization?

    View Slide

  37. Pipelines?

    View Slide

  38. Trailbrazer?
    http://trailblazer.to/gems/operation/2.0/

    View Slide

  39. View Slide