Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit ports `ActiveRecord::AttributeMethods::BeforeTypeCast` to `ActiveModel::BeforeTypeCast` and includes it in `ActiveModel::Attributes`. Thus, classes that include `ActiveModel::Attributes` will now automatically define methods such as `*_before_type_cast`, just as Active Record models do. The `ActiveRecord::AttributeMethods::BeforeTypeCast` module is kept for backward compatibility, but it now merely includes `ActiveModel::BeforeTypeCast`. Co-authored-by: Petrik <petrik@deheus.net>
- Loading branch information
1 parent
7d525d3
commit cfb72c9
Showing
8 changed files
with
240 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# frozen_string_literal: true | ||
|
||
module ActiveModel | ||
# This module provides a way to read the value of attributes before type | ||
# casting and deserialization. It uses ActiveModel::AttributeMethods to define | ||
# attribute methods with the following suffixes: | ||
# | ||
# * +_before_type_cast+ | ||
# * +_for_database+ | ||
# * +_came_from_user?+ | ||
# | ||
# ==== Examples | ||
# | ||
# class Task | ||
# include ActiveModel::Attributes | ||
# | ||
# attribute :completed_at, :datetime | ||
# end | ||
# | ||
# task = Task.new | ||
# task.completed_at # => nil | ||
# task.completed_at_before_type_cast # => nil | ||
# task.completed_at_for_database # => nil | ||
# task.completed_at_came_from_user? # => false | ||
# | ||
# task.completed_at = "1999-12-31T23:59:59-0500" | ||
# task.completed_at # => 1999-12-31 23:59:59 -0500 | ||
# task.completed_at_before_type_cast # => "1999-12-31T23:59:59-0500" | ||
# task.completed_at_for_database # => 2000-01-01 04:59:59 UTC | ||
# task.completed_at_came_from_user? # => true | ||
# | ||
module BeforeTypeCast | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
attribute_method_suffix "_before_type_cast", "_for_database", "_came_from_user?", parameters: false | ||
end | ||
|
||
# Returns the value of the specified attribute before type casting and | ||
# deserialization. | ||
# | ||
# class Task | ||
# include ActiveModel::Attributes | ||
# | ||
# attribute :completed_at, :datetime | ||
# end | ||
# | ||
# task = Task.new | ||
# task.completed_at = "1999-12-31T23:59:59-0500" | ||
# | ||
# task.completed_at # => 1999-12-31 23:59:59 -0500 | ||
# task.read_attribute_before_type_cast("completed_at") # => "1999-12-31T23:59:59-0500" | ||
# | ||
def read_attribute_before_type_cast(attribute_name) | ||
attribute_before_type_cast(self.class.resolve_attribute_name(attribute_name)) | ||
end | ||
|
||
# Returns the value of the specified attribute after serialization. | ||
# | ||
# class Task | ||
# include ActiveModel::Attributes | ||
# | ||
# attribute :completed_at, :datetime | ||
# end | ||
# | ||
# task = Task.new | ||
# task.completed_at = "1999-12-31T23:59:59-0500" | ||
# | ||
# task.completed_at # => 1999-12-31 23:59:59 -0500 | ||
# task.read_attribute_for_database("completed_at") # => 2000-01-01 04:59:59 UTC | ||
# | ||
def read_attribute_for_database(attribute_name) | ||
attribute_for_database(self.class.resolve_attribute_name(attribute_name)) | ||
end | ||
|
||
# Returns a Hash of attributes before type casting and deserialization. | ||
# | ||
# class Task | ||
# include ActiveModel::Attributes | ||
# | ||
# attribute :completed_at, :datetime | ||
# end | ||
# | ||
# task = Task.new | ||
# task.completed_at = "1999-12-31T23:59:59-0500" | ||
# | ||
# task.attributes # => {"completed_at"=>1999-12-31 23:59:59 -0500} | ||
# task.attributes_before_type_cast # => {"completed_at"=>"1999-12-31T23:59:59-0500"} | ||
# | ||
def attributes_before_type_cast | ||
@attributes.values_before_type_cast | ||
end | ||
|
||
# Returns a Hash of attributes for persisting. | ||
# | ||
# class Task | ||
# include ActiveModel::Attributes | ||
# | ||
# attribute :completed_at, :datetime | ||
# end | ||
# | ||
# task = Task.new | ||
# task.completed_at = "1999-12-31T23:59:59-0500" | ||
# | ||
# task.attributes # => {"completed_at"=>1999-12-31 23:59:59 -0500} | ||
# task.attributes_for_database # => {"completed_at"=>2000-01-01 04:59:59 UTC} | ||
# | ||
def attributes_for_database | ||
@attributes.values_for_database | ||
end | ||
|
||
private | ||
# Dispatch target for <tt>*_before_type_cast</tt> attribute methods. | ||
def attribute_before_type_cast(attr_name) | ||
@attributes[attr_name].value_before_type_cast | ||
end | ||
|
||
# Dispatch target for <tt>*_for_database</tt> attribute methods. | ||
def attribute_for_database(attr_name) | ||
@attributes[attr_name].value_for_database | ||
end | ||
|
||
# Dispatch target for <tt>*_came_from_user?</tt> attribute methods. | ||
def attribute_came_from_user?(attr_name) | ||
@attributes[attr_name].came_from_user? | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# frozen_string_literal: true | ||
|
||
require "cases/helper" | ||
|
||
class BeforeTypeCastTest < ActiveModel::TestCase | ||
class Developer | ||
include ActiveModel::Attributes | ||
|
||
attribute :name, :string | ||
attribute :salary, :integer | ||
attribute :active, :boolean | ||
alias_attribute :compensation, :salary | ||
|
||
def initialize(attributes = {}) | ||
super() | ||
attributes.each { |name, value| public_send("#{name}=", value) } | ||
end | ||
end | ||
|
||
setup do | ||
@before_type_cast = { name: 1234, salary: "56789", active: "0" } | ||
@after_type_cast = { name: "1234", salary: 56789, active: false } | ||
@developer = Developer.new(@before_type_cast) | ||
end | ||
|
||
test "#read_attribute_before_type_cast" do | ||
assert_equal @before_type_cast[:salary], @developer.read_attribute_before_type_cast(:salary) | ||
end | ||
|
||
test "#read_attribute_before_type_cast with aliased attribute" do | ||
assert_equal @before_type_cast[:salary], @developer.read_attribute_before_type_cast(:compensation) | ||
end | ||
|
||
test "#read_attribute_for_database" do | ||
assert_equal @after_type_cast[:salary], @developer.read_attribute_for_database(:salary) | ||
end | ||
|
||
test "#read_attribute_for_database with aliased attribute" do | ||
assert_equal @after_type_cast[:salary], @developer.read_attribute_for_database(:compensation) | ||
end | ||
|
||
test "#attributes_before_type_cast" do | ||
assert_equal @before_type_cast.transform_keys(&:to_s), @developer.attributes_before_type_cast | ||
end | ||
|
||
test "#attributes_before_type_cast with missing attributes" do | ||
assert_equal @before_type_cast.to_h { |key, value| [key.to_s, nil] }, Developer.new.attributes_before_type_cast | ||
end | ||
|
||
test "#attributes_for_database" do | ||
assert_equal @after_type_cast.transform_keys(&:to_s), @developer.attributes_for_database | ||
end | ||
|
||
test "#*_before_type_cast" do | ||
assert_equal @before_type_cast[:salary], @developer.salary_before_type_cast | ||
end | ||
|
||
test "#*_before_type_cast with aliased attribute" do | ||
assert_equal @before_type_cast[:salary], @developer.compensation_before_type_cast | ||
end | ||
|
||
test "#*_for_database" do | ||
assert_equal @after_type_cast[:salary], @developer.salary_for_database | ||
end | ||
|
||
test "#*_for_database with aliased attribute" do | ||
assert_equal @after_type_cast[:salary], @developer.compensation_for_database | ||
end | ||
|
||
test "#*_came_from_user?" do | ||
assert_predicate @developer, :salary_came_from_user? | ||
assert_not_predicate Developer.new, :salary_came_from_user? | ||
end | ||
|
||
test "#*_came_from_user? with aliased attribute" do | ||
assert_predicate @developer, :compensation_came_from_user? | ||
assert_not_predicate Developer.new, :compensation_came_from_user? | ||
end | ||
end |
Oops, something went wrong.