It's a short description of the project I've done recently. Focused on presenting stack, best practices, and patterns used. This our typical approach to Ruby backend at Netguru.
Detailed info about used patterns and solutions • Known Issues • How to get into staging? • How to get into production? • Deployments explained • Default seeds
ChangePassword mount Me mount Register route_param :id, type: Integer do before { @user = User.find(params[:id]) } mount Delete mount Show mount Update end end # app/controllers/api/v1/users/base.rb
< Base desc "Change user password using current one" helpers Params helpers API::V1::Helpers::JSONAPIParams before { doorkeeper_authorize! } params do use :jsonapi_data_attributes, required: [{ name: "current-password", type: String, desc: "Current user password" }], use: [{ predefined: :_password_confirmable }] end patch "change-password" do authorize current_user, :change_password? assure_rightness ::UserServices::ChangePasswordUsingCurrent.call(current_user, jsonapi_attributes(params)) end end # app/controllers/api/v1/users/change_password.rb
{ |attrs| ::GeneralServices::InjectModelIdsFromListInAttributes.call(attrs, :city) } .bind(method(:create_user)) end private def ensure_agreements(attributes) return Right(attributes) if required_agreements?(attributes) Left(I18n.t("user.registration.no_required_agreements")) end (...) end end # app/services/user_services/register.rb
a series of computations that might return an error object with additional information. The Result mixin has two type constructors: Success and Failure. The Success can be thought of as “everything went success” and the Failure is used when “something has gone wrong”. // http://dry-rb.org/gems/dry-monads/result/
work the same for every service, • chainability, • universal error handling - the same interface for all services • ApplicationServiceInTransaction, • extract logic into shared pieces