diff --git a/Gemfile b/Gemfile index 1a20984..1d4bbbe 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,7 @@ gemspec gem "rexml", ">= 3.3.6" group :development, :test do + gem "activejob", ">= 7.0", require: false gem "debug" gem "rake", "~> 13.0" gem "rspec", "~> 3.0" diff --git a/Gemfile.lock b/Gemfile.lock index a878eb0..e0a0a29 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,6 +25,9 @@ GEM erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) + activejob (7.1.3.4) + activesupport (= 7.1.3.4) + globalid (>= 0.3.6) activemodel (7.1.3.4) activesupport (= 7.1.3.4) activesupport (7.1.3.4) @@ -52,6 +55,8 @@ GEM drb (2.2.1) erb (5.0.1) erubi (1.13.0) + globalid (1.2.1) + activesupport (>= 6.1) i18n (1.14.5) concurrent-ruby (~> 1.0) io-console (0.8.0) @@ -151,6 +156,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + activejob (>= 7.0) activemodel-entity! debug rake (~> 13.0) diff --git a/lib/active_model/entity.rb b/lib/active_model/entity.rb index b66fbf9..a9abca8 100644 --- a/lib/active_model/entity.rb +++ b/lib/active_model/entity.rb @@ -18,6 +18,9 @@ require_relative "entity/validations/validates_nested" require_relative "entity/pattern_matcheable" +# Load Railtie if Rails is defined +require_relative "entity/railtie" if defined?(Rails::Railtie) + module ActiveModel # Main module providing all neccesary includes to bring missing functionality to ActiveModel instances. module Entity diff --git a/lib/active_model/entity/active_job.rb b/lib/active_model/entity/active_job.rb new file mode 100644 index 0000000..df9461f --- /dev/null +++ b/lib/active_model/entity/active_job.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# This file is only loaded when explicitly required or when Rails/ActiveJob is present +# It provides ActiveJob serialization support for ActiveModel::Entity objects + +if defined?(ActiveJob) + module ActiveJob + module Serializers + # Handles serialization of all ActiveModel::Entity classes. + class ActiveModelSerializer < ActiveJob::Serializers::ObjectSerializer + def serialize?(argument) + argument.is_a?(ActiveModel::Entity) + end + + def serialize(argument) + super( + "type" => argument.class.name, + "json" => argument.class.represent(argument), + ) + end + + def deserialize(hash) + hash["type"].constantize.from_json(hash["json"]) + end + end + end + end +end diff --git a/lib/active_model/entity/railtie.rb b/lib/active_model/entity/railtie.rb new file mode 100644 index 0000000..98c40ea --- /dev/null +++ b/lib/active_model/entity/railtie.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module ActiveModel + module Entity + # Railtie to integrate with Rails and ActiveJob + class Railtie < Rails::Railtie + initializer "activemodel_entity.configure_active_job_serializer" do + require "active_model/entity/active_job" + + if defined?(ActiveJob::Serializers::ActiveModelSerializer) + Rails.application.config.to_prepare do + Rails.application.config.active_job.custom_serializers << ActiveJob::Serializers::ActiveModelSerializer + end + end + end + end + end +end diff --git a/spec/active_model/entity/active_job_spec.rb b/spec/active_model/entity/active_job_spec.rb new file mode 100644 index 0000000..aea0fad --- /dev/null +++ b/spec/active_model/entity/active_job_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "spec_helper" + +require "active_job" +require "active_model/entity/active_job" +require "active_job/arguments" + +ActiveJob::Serializers.add_serializers(ActiveJob::Serializers::ActiveModelSerializer) + +RSpec.describe ActiveJob::Serializers::ActiveModelSerializer do + subject(:entity) { klass.new(text: "hello", number: 1) } + + let(:klass) do + Class.new do + include ActiveModel::Entity + + attribute :text, :string + attribute :number, :integer + end + end + + before do + stub_const("MyClass", klass) + end + + it "serializes things back and forth" do + expect(ActiveJob::Arguments.deserialize(ActiveJob::Arguments.serialize([entity]))).to eq([entity]) + end +end