Devise is one of those gems that are tightly bound to the Rails stack. It means that as long as you follow the "Rails way" and you do things the recommended way, you should not have any problems.
However, Trailblazer is not the recommended way and the way it works, not always makes it painless to integrate with external gems (note: it's not a Trailblazer fault, more often poorly designed external gems). Unfortunately Devise is one of those libraries. Models have validations, things happen magically in the controllers and so on.
Most of the time, you can leave it that way, as the scope of what Devise does is pretty isolated (authentication + authorization). But what if you want to integrate it with Trailblazer, for example to provide a custom built password change page? You can do this by following those steps
- Provide a contract with similar (or the same) validation rules as Devise (this will make it easier to migrate if you decide to drop model validations)
- Create an operation that will save the contract and propagate changes to the model
- Copy model errors (if any) into contract errors
Here's the code you need (I removed more complex validation rules to simplify things):
# Contract object class Contracts::Update < Reform::Form include Reform::Form::ActiveRecord # Devise validatable model model User property :password property :password_confirmation validates :password, presence: true, confirmation: true validates :password_confirmation, presence: true end
Operation is fairly simple as well:
class Operations::Update < Trailblazer::Operation include Trailblazer::Operation::Model contract Contracts::Update # Devise validatable model model User def process(params) validate(params[:user]) do # When our validations passes, we can try to save contract contract.save do |hash| # update our user instance model.update(hash) # and propagate any model based errors to our contract and operation model.errors.each { |name, desc| errors.add(name, desc) } end end end
And the best part - controller:
class PasswordsController < BaseController def edit respond_with(form Operations::Update) end def update respond_with(run Operations::Update) end