jcypret / hashid-rails

Use Hashids (http://hashids.org/ruby/) in your Rails app ActiveRecord models.
MIT License
353 stars 48 forks source link

Hashids::InputError: unable to unhash #14

Closed DannyBen closed 8 years ago

DannyBen commented 8 years ago

Hey,

Trying to see if I can drop this beautiful gem on my project without making too many (or any if possible) changes to it.

At first look, things seemed to be working immediately after adding the gem with zero change.

Only when running my tests I get to a point where this error appears:

Hashids::InputError: unable to unhash

This happens when I try an update operation on a controller that operates on a special case model. Not sure it is related though, but its the only obvious difference between this failing test and other passing tests.

It is a PeopleController that updates a User model.

The error of course originates from the hashids gem, specifically from this line, but to my understanding it is simply getting an invalid input.

Any thought as to why this happens and maybe a fix?

EDIT: The immediate suspect is this line with table_name (seems not...)

jcypret commented 8 years ago

@DannyBen I haven't run into any issue like that personally. If you'd like to share the hashid-rails config you are using along with the params[:id] you are trying to decode, I can see if I can replicate the issue. Does it work when you're testing it in the browser, then break in the tests?

DannyBen commented 8 years ago

I will see if I can pinpoint the problem or provide an easy repro case. (And it also fails in the browser)

jrasanen commented 8 years ago

I'm currently implementing hashids to a project too and running into this issue. I have the default devise user model with rolify + cancancan.

class User < ActiveRecord::Base
  rolify

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable

  after_save :associate_role

  private
    def associate_role
      add_role(:user) if self.roles.blank?
    end
end

and I run into an exception "Hashids::InputError in RegistrationsController#create" and it points to line "add_role(:user) if self.roles.blank?

Edit: Error also traces back to this line

jrasanen commented 8 years ago

Debugging with Byebug, I get this when trying to save the user:

[178, 187] in /home/vagrant/.rvm/gems/ruby-2.2.1/gems/hashids-1.0.2/lib/hashids.rb
   178:   def unhash(input, alphabet)
   179:     num = 0
   180:
   181:     input.length.times do |i|
   182:       byebug
=> 183:       pos = alphabet.index(input[i])
   184:
   185:       raise InputError, "unable to unhash" unless pos
   186:
   187:       num += pos * alphabet.length ** (input.length - i - 1)
(byebug) input
"1]"
(byebug) input[i]
"]"

and this is where it raises:

Hashids::InputError (unable to unhash): app/models/user.rb:11:inassociate_role'`

DannyBen commented 8 years ago

@jrasanen - I can confirm I also got similar error with byebug - it had my id (or part of it) and a closing square bracket.

jrasanen commented 8 years ago

Deeper digging:

[50, 59] in /home/vagrant/.rvm/gems/ruby-2.2.1/gems/hashid-rails-0.3.1/lib/hashid/rails.rb
   50:         hashids.encode(id)
   51:       end
   52:
   53:       def decode_id(id)
   54:  byebug
=> 55:         hashids.decode(id.to_s).first
   56:       end
   57:
   58:       def find(hashid)
   59:         model_reload? ? super(hashid) : super( decode_id(hashid) || hashid )
(byebug) id
[1]

Seems like its trying to convert [1]:Array into string with "to_s"

DannyBen commented 8 years ago

Excellent. This brings up the question - what causes an id be of type Array? Then a solution would either be to solve the source or, if the source case is valid, handle array type in this function.

jrasanen commented 8 years ago

Hello,

sorry for the noise.

But this works as expected if you change

hashid-rails-0.3.1/lib/hashid/rails.rb

hashids.decode(id.first.to_s).first

into

hashids.decode(id.first.to_s)

as I am not familiar with the codebase, I don't know whether this is a typo or why cast an array into string.

Thanks

jrasanen commented 8 years ago

Hi everybody,

Made a fix, but I'm in a hurry so didn't have time to debug yet why its an array in the first place. Hoping to find time to debug this issue more this weekend, but feel free to contribute. Though, this fixes all the issues for my installation.

DannyBen commented 8 years ago

Sweet! I will install later this weekend and test as well.

DannyBen commented 8 years ago

I can confirm the fix is working.

With it, and with a few tweaks to my code where I needed to use SomeModel.decode_id and SomeModel.encode_id the library is pretty much a drop-in ID replacement.

I suggest opening a PR - unless you want to wait for either of us to find the root cause of the ID being an array.

EDIT: I am just a little concerned that it is an array for a reason... and then we are always using the first element of the array only, oblivious to the fact that the array may have more items in it.

allisonphillips commented 3 years ago

Driving by here, but I just came across this issue while looking into why I am seeing this error myself. The root cause in my case is that calling decode on a Hashids config with a hashid value that has a length > 1 && contains letters outside of the scheme's alphabet. Example:

SCHEME = Hashids.new('spaghetti', 10, 'ABCDEFHIJKLMNOPQ').freeze
SCHEME.decode('z')
=> []
# If the value being decoded has length > 1, I get this every time
SCHEME.decode('zz')
Traceback (most recent call last):
        1: from (irb):39
Hashids::InputError (unable to unhash)

SCHEME.decode('AA')
=> []
SCHEME.decode('Az')
Traceback (most recent call last):
        1: from (irb):52
Hashids::InputError (unable to unhash)

This makes sense given the Hashids source code here: https://github.com/peterhellberg/hashids.rb/blob/master/lib/hashids.rb#L182 Which raises this error if the letter being parsed cannot be found in the alphabet.