Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR #829 breaks "define_enum_for" matcher for enums that do not actually use integer columns #912

Closed
begedin opened this issue Mar 9, 2016 · 15 comments
Milestone

Comments

@begedin
Copy link

begedin commented Mar 9, 2016

Say we want a status enum that looks like

enum status: { active: "active", pending: "pending", rejected: "rejected" }

With the changes made in PR #829, we can no longer spec for that enum with

it { should define_enum_for(:status).with(active: "active", pending: "pending", rejected: "rejected")

Are active record enums with strings as underlying column types not allowed? I couldn't find any information that would explicitly make it so.

I believe the matcher should only enforce integer type if the provided expected enum value is an array, or if it's a hash with values all being integer.

@guilhermearaujo
Copy link

I agree. There is absolutely no reason to enforce integers.

It started with #827, but in the author's example, he is storing an :integer in a column of type :string, and his enum maps the keys to integers. If he wanted to use integers, he could just use an array of symbols.

Had he done that, than yes, the matcher could enforce integer type in case of array, or enforce nothing otherwise.

@lucasgomide
Copy link

cc: @mcmire

can you review this ?

@mcmire
Copy link
Collaborator

mcmire commented Jun 14, 2016

Sorry for the lack of reply on this, I've been out.

I agree that this is a bug. I'm not sure why we had the matcher assume integers except that that was the original use case.

I can't say when I'll get to this, but I've at least seen it. :)

@tiii
Copy link

tiii commented Jul 14, 2016

I don't think this is a bug. Using strings as keys is not the way to go with ActiveRecord::Enum. There is no mention about using something else than integer columns and integer values in the official documentation.

4.1.8: http://apidock.com/rails/ActiveRecord/Enum
5.0.0: http://api.rubyonrails.org/classes/ActiveRecord/Enum.html

If you use a string type column and non string enum values (integers or symbols) you lose the ability to retrieve the key by calling the getter method on your model (shown here #827 (comment)).

In my opinion rails should enforce the use of an integer column, but as long as rails does not I don't mind that should-matchers enforces it.

If we still want to allow strings as keys we should check the enum values too:
Integer column: only allow integer enum values
string column: only allow string enum values

@guilhermearaujo
Copy link

(@tiii I suppose that by 'key' in your message you mean 'value', as in the value held by the key being a string, and not the key itself. Is that correct?)

I have to disagree. If your database has a varchar column to store your enum data and your enum maps a symbol to an integer, IMHO you are doing it wrong.
On the other hand, if your column is of type integer and your enum maps a symbol to a string, you are doing it wrong too.

In my opinion, if you have:

enum status: { active: 'active', pending: 'pending', rejected: 'rejected' }

you should create a column of type varchar;

Accordingly, if you have:

enum status: { active: 0, pending: 1, rejected: 2 } # or
enum status: [:active, :pending, :rejected]

then your column should be of type integer. As simple as that.

Should/can Rails be smart enough to make the required type conversions? Possibly yes.
Should all enums have integer values? This is debatable.
Is it up to shoulda-matchers decide whether they should be integer or string? My opinion is: definitely not.

@tiii
Copy link

tiii commented Jul 18, 2016

@guilhermearaujo yeah, that's what i meant, sorry for that.

Going through the commit history for the enum module, i finally found a test for strings as enum values. (rails/rails@67c1719)

Since there was never any mention about using string as values in the documentation or code I assumed that the behaviour of using strings for values is rather a coincidence.

Therefore I agree, that shoulda should not decide if the values used for enums should be integer or string.

@foucist
Copy link

foucist commented Jul 25, 2016

Just chiming in, we're using a legacy database with a variety of enumerable-like columns based on varchar strings rather than integers. It works fine in rails 4.2 & 5, and I was successfully using define_enum_for with shoulda-matcher 2.8.0, but it seems it's been broken since then.

@ZeroInputCtrl
Copy link

with what @foucist said, we have a similar issue where when we created the database we intended a field to be a string but instead decided to use it as an enum. This has worked til recently. Luckily we have a small database so it won't require any downtime to run a migration for it but I can see the need to still support this kind of functionality.

@valscion
Copy link

valscion commented Jan 3, 2018

Seems like #1063 would fix this second-most 👍-reactioned bug issue. Would there be any chance in getting it in at some point? Anything I could do to help with it?

@mcmire
Copy link
Collaborator

mcmire commented Jan 24, 2018

@valscion Yes! Sorry this took me so long. A fix for this is now in the master branch. To address @begedin's original problem, you'd be able to fix this by saying:

it do
  should define_enum_for(:status).
    with(active: "active", pending: "pending", rejected: "rejected").
    backed_by_column_of_type(:string)
end

@mcmire mcmire closed this as completed Jan 24, 2018
@mcmire mcmire added this to the v4.0 milestone Jan 24, 2018
@valscion
Copy link

Thank you! It's understandable that sometimes it takes a while to get time for open source projects, thank you for your work here!

@rafaeldev
Copy link

The method backed_by_column_of_type was refactored? I try use it but an error is raised:
undefined method 'backed_by_column_of_type' for #<Shoulda::Matchers::ActiveRecord::DefineEnumForMatcher:xxx>

@mcmire
Copy link
Collaborator

mcmire commented Feb 12, 2019

@rafaeldev What version are you using?

@rafaeldev
Copy link

Hi! Sorry for late... The versions:

  • shoulda-matchers (3.1.3)
  • rails (5.2.2)

The spec example:
should define_enum_for(:kind).with(a: 0, b: 1, c: 2).backed_by_column_of_type(:string)

@mcmire
Copy link
Collaborator

mcmire commented Feb 22, 2019

@rafaeldev The backed_by_column_of_type qualifier is available in 4.0.0! Upgrade to get it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants