diff --git a/CHANGELOG.md b/CHANGELOG.md index 29fdd61..ccfd2ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## 2.4.0 (2023-12-22) +* Added: You can implement an `#after_cast` that is called with the original record when calling `ActiveType.cast`. + Thanks to @MaximilianoGarciaRoe. + ## 2.3.4 (2023-05-11) * Fixed: ActiveType.cast preserves `.strict_loading?` and `.readonly?` diff --git a/Gemfile.5.2.mysql2.lock b/Gemfile.5.2.mysql2.lock index 82007ac..56fa360 100644 --- a/Gemfile.5.2.mysql2.lock +++ b/Gemfile.5.2.mysql2.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/Gemfile.5.2.pg.lock b/Gemfile.5.2.pg.lock index 2a71fc0..8499ae9 100644 --- a/Gemfile.5.2.pg.lock +++ b/Gemfile.5.2.pg.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/Gemfile.5.2.sqlite3.lock b/Gemfile.5.2.sqlite3.lock index 21438a1..eda2f89 100644 --- a/Gemfile.5.2.sqlite3.lock +++ b/Gemfile.5.2.sqlite3.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/Gemfile.6.0.sqlite3.lock b/Gemfile.6.0.sqlite3.lock index c3792cb..caeb195 100644 --- a/Gemfile.6.0.sqlite3.lock +++ b/Gemfile.6.0.sqlite3.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/Gemfile.6.1.pg.lock b/Gemfile.6.1.pg.lock index b1f857e..f823d6b 100644 --- a/Gemfile.6.1.pg.lock +++ b/Gemfile.6.1.pg.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/Gemfile.6.1.sqlite3.lock b/Gemfile.6.1.sqlite3.lock index 94cb9a6..7437462 100644 --- a/Gemfile.6.1.sqlite3.lock +++ b/Gemfile.6.1.sqlite3.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/Gemfile.7.0.pg.lock b/Gemfile.7.0.pg.lock index 4bbb6ec..25dad7c 100644 --- a/Gemfile.7.0.pg.lock +++ b/Gemfile.7.0.pg.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/Gemfile.7.0.sqlite3.lock b/Gemfile.7.0.sqlite3.lock index 6c042a6..5bd8ec8 100644 --- a/Gemfile.7.0.sqlite3.lock +++ b/Gemfile.7.0.sqlite3.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - active_type (2.3.4) + active_type (2.4.0) activerecord (>= 3.2) GEM diff --git a/README.md b/README.md index 8d82a4e..953e9de 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,13 @@ Examples for use cases are models to support sign in: class SignIn < ActiveType::Object # this is not backed by a db table - + attribute :username, :string attribute :password, :string validates :username, presence: true validates :password, presence: true - + # ... end @@ -38,15 +38,15 @@ class SignUp < ActiveType::Record[User] # this inherits from User validates :password, confirmation: true - + after_create :send_confirmation_email - + def send_confirmation_email # this should happen on sign-up, but not when creating a user in tests etc. end - + # ... - + end ``` @@ -86,12 +86,12 @@ You can define "columns" by saying `attribute`: ```ruby class SignIn < ActiveType::Object - + attribute :email, :string attribute :date_of_birth, :date attribute :accepted_terms, :boolean attribute :account_type - + end ``` @@ -103,7 +103,7 @@ sign_in.date_of_birth.class # Date sign_in.accepted_terms? # true ``` -ActiveType knows all the types that are allowed in migrations (i.e. `:string`, `:integer`, `:float`, `:decimal`, `:datetime`, `:time`, `:date`, `:boolean`). You can also skip the type to have a virtual attribute without typecasting. +ActiveType knows all the types that are allowed in migrations (i.e. `:string`, `:integer`, `:float`, `:decimal`, `:datetime`, `:time`, `:date`, `:boolean`). You can also skip the type to have a virtual attribute without typecasting. **`ActiveType::Object` actually inherits from `ActiveRecord::Base`, but simply skips all database access, inspired by [ActiveRecord Tableless](https://github.com/softace/activerecord-tableless).** @@ -197,15 +197,15 @@ class SignIn < ActiveType::Object attribute :email, :string attribute :nickname, :string - + def email super.downcase end - + def nickname=(value) super(value.titleize) end - + end ``` @@ -379,6 +379,16 @@ sign_up.is_a?(SignUp) # => true ``` +If you need to add some special logic after casting you can add an `after_cast` method: + +class SignUp < ActiveType::Record[User] + def after_cast(user) + # Called with the original user upon casting. + end +end + + + Associations ------------ diff --git a/lib/active_type/util.rb b/lib/active_type/util.rb index a8cbc1a..b3812ed 100644 --- a/lib/active_type/util.rb +++ b/lib/active_type/util.rb @@ -62,9 +62,12 @@ def cast_record(record, klass, force: false) casted[klass.inheritance_column] = klass.sti_name if using_single_table_inheritance + casted.after_cast(record) if casted.respond_to?(:after_cast) + if !force make_record_unusable(record) end + casted end end diff --git a/lib/active_type/version.rb b/lib/active_type/version.rb index 52b4075..0018c55 100644 --- a/lib/active_type/version.rb +++ b/lib/active_type/version.rb @@ -1,3 +1,3 @@ module ActiveType - VERSION = '2.3.4' + VERSION = '2.4.0' end diff --git a/spec/active_type/util_spec.rb b/spec/active_type/util_spec.rb index d8fb38a..60ea486 100644 --- a/spec/active_type/util_spec.rb +++ b/spec/active_type/util_spec.rb @@ -3,8 +3,17 @@ module UtilSpec class BaseRecord < ActiveRecord::Base + self.table_name = 'records' has_many :associated_records + + attr_reader :after_cast_called_with + + def after_cast(*args) + # will expect this to be called + @after_cast_called_with = args + end + end class ExtendedRecord < ActiveType::Record[BaseRecord] @@ -79,6 +88,12 @@ class AssociatedRecord < ActiveRecord::Base expect(extended_record.persisted_string).to eq('foo') end + it 'calls "after_cast" with the original record after casting' do + base_record = UtilSpec::BaseRecord.create! + extended_record = ActiveType::Util.cast(base_record, UtilSpec::BaseRecord) + expect(extended_record.after_cast_called_with).to eq([base_record]) + end + context 'casting without copying the @association cache' do # When casting, the @association_cache is not copied, because of Issues #146, #147 and #148. # This may be unexpected to a user of Active Type, so we decided to not allow