Tag: Devise

Ruby 2.5.0 upgrade remarks

There's a lot of articles already on the new shiny features of Ruby 2.5.0. But here are some problems and challenges you may encounter during the upgrade process.

Devise SyntaxError

Note: Devise team already fixed this one with the new Devise release.

If you encounter this error:

SyntaxError: /.../devise-3.5.5/app/controllers/devise/sessions_controller.rb:5: syntax error, unexpected '{', expecting keyword_end
...ter only: [:create, :destroy] { request.env["devise.skip_tim...
...                              ^

the only thing you can do for now, is to edit the devise/sessions_controller.rb in your Devise local location and change:

prepend_before_filter only: [:create, :destroy] { request.env["devise.skip_timeout"] = true }

to (note the brackets):

prepend_before_filter(only: [:create, :destroy]) { request.env["devise.skip_timeout"] = true }

Note: to determine your local Devise path, use following command:

bundle show devise

Probably in few days, there will be a Devise gem release that will fix that for RubyGems as well.

ActionCable Argument Error

If you use ActionCable, then it is a good to postpone the upgrade until this issue is fixed and the upgraded version of ActionCable has been released.

#<Thread:0x00007fbb44c01b90@/Users/petercopter/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/actioncable-5.1.4/lib/action_cable/connection/stream_event_loop.rb:73 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
	5: from /Users/petercopter/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/actioncable-5.1.4/lib/action_cable/connection/stream_event_loop.rb:73:in `block (2 levels) in spawn'
	4: from /Users/petercopter/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/actioncable-5.1.4/lib/action_cable/connection/stream_event_loop.rb:84:in `run'
	3: from /Users/petercopter/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/actioncable-5.1.4/lib/action_cable/connection/stream_event_loop.rb:84:in `loop'
	2: from /Users/petercopter/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/actioncable-5.1.4/lib/action_cable/connection/stream_event_loop.rb:94:in `block in run'
	1: from /Users/petercopter/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/actioncable-5.1.4/lib/action_cable/connection/stream_event_loop.rb:94:in `select'
/Users/petercopter/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/actioncable-5.1.4/lib/action_cable/connection/stream_event_loop.rb:94:in `lock': wrong number of arguments (given 283856384, expected 0) (ArgumentError)
WebSocket error occurred: Broken pipe

OpenSSL problem

SecureRandom now prefers OS-provided sources over OpenSSL. It also means, that OpenSSL is not required by default, so if you use it in a gem, you will have to add:

require 'openssl'

or you will end up with an error like this one:

NameError:
  uninitialized constant OpenSSL
  Did you mean?  Open3

Travis does not yet support 2.5.0

Note: Travis already fixed this one.

If you manage a multi-ruby version library, don't update Travis yet unless you want to compile Ruby yourself. If you list 2.5.0 as one of the versions, Travis will pick the preview1 instead of the final release and you might end up with an error similar to this one:

Traceback (most recent call last):
	1: from /home/travis/.rvm/gems/ruby-2.5.0-preview1@global/bin/ruby_executable_hooks:15:in `<main>'
/home/travis/.rvm/gems/ruby-2.5.0-preview1@global/bin/ruby_executable_hooks:15:in `eval': /home/travis/.rvm/rubies/ruby-2.5.0-preview1/bin/bundle:4: syntax error, unexpected tSTRING_BEG, expecting keyword_do or '{' or '(' (SyntaxError)
exec "$bindir/ruby" -x "$0" "$@"
                       ^
/home/travis/.rvm/rubies/ruby-2.5.0-preview1/bin/bundle:9: syntax error, unexpected keyword_do_block, expecting end-of-input
Signal.trap("INT") do
                   ^~

yield_self as an incompatibility

Again, if you are a maintained of a multi-ruby version library, don't forget to backport the yield_self into your code-base if you are planning to use it:

class Object
  def yield_self(*args)
    yield(self, *args)
  end
end

Dir::Tmpname#make_tmpname is no longer available

If you use Dir::Tmpname#make_tmpname, it is no longer available. Long story short: you need to generate unique names on your own. Click here to see how Rails core team did it.

Summary

There aren't many problems with 2.5.0. All of the things that I've encountered are either easy to fix or things that will disappear after few weeks of an adoption time. Long live Ruby core team! :)

Credits

Cover photo by: Victoria Pickering on Attribution-NonCommercial-NoDerivs 2.0 Generic (CC BY-NC-ND 2.0) license. Changes made: added an overlay layer with an article title on top of the original picture.

Trailblazer + Devise: Integrating Devise validatable model with Trailblazer operation + error propagation

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

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑