Skip to content

Instantly share code, notes, and snippets.

@seanpdoyle
Created November 14, 2025 17:02
Show Gist options
  • Select an option

  • Save seanpdoyle/a2937746b99da599ae42d887260cd84a to your computer and use it in GitHub Desktop.

Select an option

Save seanpdoyle/a2937746b99da599ae42d887260cd84a to your computer and use it in GitHub Desktop.
Active Resource in 2025

Active Resource

The 19 year old gem. This dog can still hunt!

Exploration

To execute the script, download the demo.rb file locally and execute it with the ruby command:

ruby demo.rb

The script will execute, and the test suite will pass.

To experiment with different tests at different points in time, insert binding.irb commands to pause execution.

Following up

There have been recent improvements:

There are some still some proposed improvements:

( 🤞 )

require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "activeresource", github: "rails/activeresource"
gem "rails"
gem "sqlite3"
end
require "active_record/railtie"
require "minitest/autorun"
# This connection will do for database-independent bug reports.
ENV["DATABASE_URL"] = "sqlite3::memory:"
class DemoApp < Rails::Application
config.load_defaults Rails::VERSION::STRING.to_f
config.eager_load = false
config.logger = Logger.new($stdout)
config.secret_key_base = "secret_key_base"
config.active_record.encryption.primary_key = "primary_key"
config.active_record.encryption.deterministic_key = "deterministic_key"
config.active_record.encryption.key_derivation_salt = "key_derivation_salt"
end
Rails.application.initialize!
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
t.json :cms_post, null: false
t.virtual :cms_post_id, type: :string, stored: true, null: false, as: <<~SQL
cms_post ->> 'id'
SQL
t.index :cms_post_id, unique: true
end
end
module Cms
class UrlEncodedFormat
include ActiveResource::Formats::UrlEncodedFormat
def initialize(&transform)
@transform = transform
end
def encode(params, options = nil)
params = params.deep_transform_keys(&@transform)
super
end
end
class ApplicationResource < ActiveResource::Base
self.site = "https://cms.dev"
self.query_format = UrlEncodedFormat.new { |key| key.to_s.camelcase(:lower) }
end
class Post < ApplicationResource
schema do
attribute :name, :string
attribute :body, :string
end
end
end
class Post < ActiveRecord::Base
serialize :cms_post, coder: ActiveResource::Coder.new(Cms::Post, :serializable_hash)
delegate_missing_to :cms_post
end
require "rails/test_help"
class PostTest < ActiveSupport::TestCase
test "GET find interface" do
ActiveResource::HttpMock.respond_to.get "/posts/abc123.json", {}, {
id: "abc123", name: "Hello World", body: "From the API", created_on: "2025-11-14"
}.to_json
resource = Cms::Post.find("abc123")
assert_kind_of Cms::Post, resource
assert_equal "abc123", resource.id
assert_equal "Hello World", resource.name
assert_equal "From the API", resource.body
assert_equal "2025-11-14", resource.created_on
end
test "GET query param interface" do
ActiveResource::HttpMock.respond_to.get "/posts.json?sortBy=created_at", {}, [
{ id: "abc123", name: "Hello World", created_at: 10.minutes.ago },
{ id: "def456", name: "Hello Rails", created_at: 5.minutes.ago }
].to_json
resources = Cms::Post.where(sort_by: "created_at")
assert_equal [ "Hello World", "Hello Rails" ], resources.map(&:name)
end
test "POST can map response with Errors to " do
ActiveResource::HttpMock.respond_to.post "/posts.json" do
response = { errors: [ "Name is taken" ] }
ActiveResource::Response.new(response.to_json, 422)
end
resource = Cms::Post.new(name: "Let's Not")
assert_raises ActiveResource::ResourceInvalid do
resource.save!
end
assert_equal [ "is taken" ], resource.errors[:name]
end
test "stores Cms::Post instances as JSON" do
cms_post = Cms::Post.new id: "abc123", name: "Hello", body: "World"
Post.create!(cms_post:)
# queries by synthesized column
post = Post.find_by! cms_post_id: cms_post.id
# uniqueness constraints enforced on synthesized column
assert_raises(ActiveRecord::RecordNotUnique) { Post.create!(cms_post:) }
# reads column to object instance
assert_kind_of Cms::Post, post.cms_post
assert_equal cms_post, post.cms_post
# access through instance
assert_equal cms_post.id, post.cms_post.id
assert_equal cms_post.name, post.cms_post.name
assert_equal cms_post.body, post.cms_post.body
# access through delegation
assert_equal cms_post.name, post.name
assert_equal cms_post.body, post.body
# virtual column access synthesized from JSON
assert_equal cms_post.id, post.cms_post_id
end
test "Post can reload Cms::Post instance" do
ActiveResource::HttpMock.respond_to.get "/posts/abc123.json", {}, {
id: "abc123", name: "Goodbye"
}.to_json
cms_post = Cms::Post.new id: "abc123", name: "Hello"
post = Post.create!(cms_post:)
assert_changes -> { post.reload.name }, from: "Hello", to: "Goodbye" do
post.cms_post.reload
post.save!
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment