-
Implement ActiveModel::Type::Binary::Data#as_json
Delegates JSON conversion to the underlying binary data value (instead of falling back to
Object#as_jsonand exposing ivar name).Tiago Cardoso
-
Fix
normalizesto run before the underlying type validates an assigned value.When
normalizeswas combined with another type that rejects invalid input (such as an Active Recordenum), the underlying type'sassert_valid_valueran against the raw, un-normalized value and raised before normalization had a chance to run. The normalization is now applied first, so a value like" Pending "is normalized to"pending"and accepted by the enum.Gabriel Quaresma
-
Fix
LengthValidatorraisingNoMethodErrorwhen a:minimum(or:is) constraint is given as a proc and the validated value isnil.The proc was resolved only when the value was present, so for a
nilvalue it leaked unresolved into the error message and was invoked with the message options hash instead of the record. It is now resolved before the error is built, producing the expected"is too short"message.validates_length_of :title, minimum: ->(record) { record.min_length } # title = nil now yields "is too short (minimum is N characters)" # instead of raising NoMethodErrorBen Younes
-
Fix
normalizesre-applying normalizations on every validation of an unpersisted record, and speed up validation of normalized attributes.The in-place mutation check re-ran the normalizer on every
valid?of an unpersisted record: wasteful for idempotent normalizers and compounded the result for non-idempotent ones. Normalizations are now re-applied only on a genuine in-place mutation.Yaroslav Markin
-
Limit the size of strings
::ActiveModel::Type::Integerwill coerce withto_i.Calling
to_ion very long strings can take a long time and could be used as a DoS vector. Integer casting now only considers the first_limit * 4bytes of a string (16 bytes for a default 4-byte integer, 32 bytes for an 8-byte bigint), which is enough to hold the maximum representable value plus a sign or a short slug suffix.Aaron Patterson, Jean Boussier
-
Support proc and symbol for
NumericalityValidators:inoptionvalidates_numericality_of :price, in: ->(o) { 0..o.max_price }or
validates_numericality_of :price, in: :price_range def price_range 0..max_price endThomas Sevestre
-
Combine
:if,:unless, and:onoptions when specified at both thevalidateslevel and the per-validator level, instead of the per-validator options silently replacing the top-level ones.Before,
validates :title, presence: { if: :local? }, if: :global?would only checklocal?, ignoringglobal?entirely. Now both conditions must pass for the validation to run.Fixes #55761.
Denis Savchuk
-
Add
has_jsonandhas_delegated_jsonto provide schema-enforced access to JSON attributes.class Account < ApplicationRecord has_json :settings, restrict_creation_to_admins: true, max_invites: 10, greeting: "Hello!" has_delegated_json :flags, beta: false, staff: :boolean end a = Account.new a.settings.restrict_creation_to_admins? # => true a.settings.max_invites = "100" # => Set to integer 100 a.settings = { "restrict_creation_to_admins" => "false", "max_invites" => "500", "greeting" => "goodbye" } a.settings.greeting # => "goodbye" a.staff # => nil a.staff = true a.staff? # => trueDHH
-
Changes ActiveModel::Validations#read_attribute_for_validation to return
nilif the record doesn't respond to the attribute instead of raising an error.This change allows adding errors to custom attributes with symbol messages.
user = User.new # User model has no `address` attribute user.errors.add(:address, :invalid) user.errors.Previously, calling
messageswould raise an error becauseaddressattribute can't be read. Now it returns the localized error message.Lovro Bikić
-
Add built-in Argon2 support for
has_secure_password.has_secure_passwordnow supports Argon2 as a built-in algorithm:class User < ActiveRecord::Base has_secure_password algorithm: :argon2 endTo use Argon2, add
gem "argon2", "~> 2.3"to your Gemfile.Argon2 has no password length limit, unlike BCrypt's 72-byte restriction.
Justin Bull, Guillermo Iguaran
-
Add ActiveModel::SecurePassword.register_algorithm to register new algorithms for
has_secure_passwordby symbol:ActiveModel::SecurePassword.register_algorithmcan be used to register new algorithms:ActiveModel::SecurePassword.register_algorithm :custom_password, CustomPasswordclass User < ActiveRecord::Base has_secure_password algorithm: :custom_password endBCrypt is pre-registered as
:bcryptin the algorithms registry.Justin Bull, Guillermo Iguaran
-
has_secure_passwordcan support different password hashing algorithms (if defined) using the:algorithmoption:class CustomPassword def hash_password(unencrypted_password) CustomHashingLibrary.create(unencrypted_password) end def verify_password(password, digest) CustomHashingLibrary.verify(password, digest) end def password_salt(digest) CustomHashingLibrary.salt(digest) end def validate(record, attribute) # ... end def algorithm_name :custom end endclass User < ActiveRecord::Base has_secure_password algorithm: CustomPassword.new endJustin Bull, Lucas Mazza
-
Allow passing method name or proc to
allow_nilandallow_blankclass EnrollmentForm include ActiveModel::Validations attr_accessor :course validates :course, inclusion: { in: :open_courses }, allow_nil: :saving_progress? endRichard Lynch
-
Add error type support arguments to ActiveModel::Errors#messages_for and ActiveModel::Errors#full_messages_for
person = Person.create() person.errors.(:name, :invalid) # => ["Name is invalid"] person.errors.(:name, :invalid) # => ["is invalid"]Eugene Bezludny
-
Make ActiveModel::Serializers::JSON#from_json compatible with
#assign_attributesSean Doyle
Please check 8-1-stable for previous changes.