From 609b412a2af4a51244af6951e9c8144f79e41fbe Mon Sep 17 00:00:00 2001 From: MaximilianoGarciaRoe Date: Thu, 20 Jul 2023 22:38:38 -0400 Subject: [PATCH 1/5] add after_cast method to call ActiveType cast --- lib/active_type/util.rb | 4 ++++ lib/active_type/util/active_record.rb | 9 +++++++++ spec/active_type/util_spec.rb | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 lib/active_type/util/active_record.rb diff --git a/lib/active_type/util.rb b/lib/active_type/util.rb index a8cbc1a..906096e 100644 --- a/lib/active_type/util.rb +++ b/lib/active_type/util.rb @@ -1,5 +1,6 @@ require 'active_type/not_castable_error' require 'active_type/util/unmutable_attributes' +require 'active_type/util/active_record' module ActiveType module Util @@ -65,6 +66,9 @@ def cast_record(record, klass, force: false) if !force make_record_unusable(record) end + + casted.after_cast(record) + casted end end diff --git a/lib/active_type/util/active_record.rb b/lib/active_type/util/active_record.rb new file mode 100644 index 0000000..aad9c84 --- /dev/null +++ b/lib/active_type/util/active_record.rb @@ -0,0 +1,9 @@ +module ActiveType + module Util + + class ActiveRecord::Base + + def after_cast(record) end + end + end +end diff --git a/spec/active_type/util_spec.rb b/spec/active_type/util_spec.rb index d8fb38a..3a9d72f 100644 --- a/spec/active_type/util_spec.rb +++ b/spec/active_type/util_spec.rb @@ -5,6 +5,13 @@ module UtilSpec class BaseRecord < ActiveRecord::Base self.table_name = 'records' has_many :associated_records + + attr_accessor :persisted_attribute + + def after_cast(record) + @persisted_attribute = record.persisted_attribute + end + end class ExtendedRecord < ActiveType::Record[BaseRecord] @@ -79,6 +86,18 @@ class AssociatedRecord < ActiveRecord::Base expect(extended_record.persisted_string).to eq('foo') end + it 'casts a base record persist attr_reader' do + base_record = UtilSpec::BaseRecord.create!(:persisted_string => 'foo') + base_record.persisted_attribute = 'bar' + extended_record = ActiveType::Util.cast(base_record, UtilSpec::BaseRecord) + expect(extended_record).to be_a(UtilSpec::BaseRecord) + expect(extended_record).to be_persisted + expect(extended_record.id).to be_present + expect(extended_record.id).to eq(base_record.id) + expect(extended_record.persisted_string).to eq('foo') + expect(extended_record.persisted_attribute).to eq('bar') + 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 From d5376753d82e201ae86f9b1d62dc68b3c36a9b3e Mon Sep 17 00:00:00 2001 From: Tobias Kraze Date: Fri, 22 Dec 2023 16:51:36 +0100 Subject: [PATCH 2/5] clean up #after_cast implementation a bit --- lib/active_type/util.rb | 5 ++--- lib/active_type/util/active_record.rb | 9 --------- spec/active_type/util_spec.rb | 20 ++++++++------------ 3 files changed, 10 insertions(+), 24 deletions(-) delete mode 100644 lib/active_type/util/active_record.rb diff --git a/lib/active_type/util.rb b/lib/active_type/util.rb index 906096e..b3812ed 100644 --- a/lib/active_type/util.rb +++ b/lib/active_type/util.rb @@ -1,6 +1,5 @@ require 'active_type/not_castable_error' require 'active_type/util/unmutable_attributes' -require 'active_type/util/active_record' module ActiveType module Util @@ -63,12 +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.after_cast(record) - casted end end diff --git a/lib/active_type/util/active_record.rb b/lib/active_type/util/active_record.rb deleted file mode 100644 index aad9c84..0000000 --- a/lib/active_type/util/active_record.rb +++ /dev/null @@ -1,9 +0,0 @@ -module ActiveType - module Util - - class ActiveRecord::Base - - def after_cast(record) end - end - end -end diff --git a/spec/active_type/util_spec.rb b/spec/active_type/util_spec.rb index 3a9d72f..60ea486 100644 --- a/spec/active_type/util_spec.rb +++ b/spec/active_type/util_spec.rb @@ -3,13 +3,15 @@ module UtilSpec class BaseRecord < ActiveRecord::Base + self.table_name = 'records' has_many :associated_records - attr_accessor :persisted_attribute + attr_reader :after_cast_called_with - def after_cast(record) - @persisted_attribute = record.persisted_attribute + def after_cast(*args) + # will expect this to be called + @after_cast_called_with = args end end @@ -86,16 +88,10 @@ class AssociatedRecord < ActiveRecord::Base expect(extended_record.persisted_string).to eq('foo') end - it 'casts a base record persist attr_reader' do - base_record = UtilSpec::BaseRecord.create!(:persisted_string => 'foo') - base_record.persisted_attribute = 'bar' + 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).to be_a(UtilSpec::BaseRecord) - expect(extended_record).to be_persisted - expect(extended_record.id).to be_present - expect(extended_record.id).to eq(base_record.id) - expect(extended_record.persisted_string).to eq('foo') - expect(extended_record.persisted_attribute).to eq('bar') + expect(extended_record.after_cast_called_with).to eq([base_record]) end context 'casting without copying the @association cache' do From 707af868cb02c506eb189419b87e7e73d78f3611 Mon Sep 17 00:00:00 2001 From: Tobias Kraze Date: Fri, 22 Dec 2023 16:53:53 +0100 Subject: [PATCH 3/5] clean up some whitespace in README --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8d82a4e..4d9daf7 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 ``` From df0cd55fd7f266510f64df1c93c9426c8b3b1bf1 Mon Sep 17 00:00:00 2001 From: Tobias Kraze Date: Fri, 22 Dec 2023 16:54:26 +0100 Subject: [PATCH 4/5] add documentation for #after_cast --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 4d9daf7..953e9de 100644 --- a/README.md +++ b/README.md @@ -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 ------------ From 380a279a4e2759f2bd257ca583f5f79e3892460c Mon Sep 17 00:00:00 2001 From: Tobias Kraze Date: Fri, 22 Dec 2023 16:55:27 +0100 Subject: [PATCH 5/5] release version 2.4.0 --- CHANGELOG.md | 4 ++++ Gemfile.5.2.mysql2.lock | 2 +- Gemfile.5.2.pg.lock | 2 +- Gemfile.5.2.sqlite3.lock | 2 +- Gemfile.6.0.sqlite3.lock | 2 +- Gemfile.6.1.pg.lock | 2 +- Gemfile.6.1.sqlite3.lock | 2 +- Gemfile.7.0.pg.lock | 2 +- Gemfile.7.0.sqlite3.lock | 2 +- lib/active_type/version.rb | 2 +- 10 files changed, 13 insertions(+), 9 deletions(-) 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/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