├── example ├── consumer │ ├── db │ │ ├── seeds.rb │ │ └── cache_schema.rb │ ├── log │ │ └── .keep │ ├── tmp │ │ ├── .keep │ │ ├── cache │ │ │ └── .keep │ │ ├── pids │ │ │ └── .keep │ │ └── storage │ │ │ └── .keep │ ├── lib │ │ └── tasks │ │ │ └── .keep │ ├── script │ │ └── .keep │ ├── storage │ │ └── .keep │ ├── vendor │ │ └── .keep │ ├── app │ │ ├── models │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── application_record.rb │ │ │ └── person.rb │ │ ├── controllers │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── application_controller.rb │ │ ├── views │ │ │ └── layouts │ │ │ │ ├── mailer.text.erb │ │ │ │ └── mailer.html.erb │ │ ├── mailers │ │ │ └── application_mailer.rb │ │ └── jobs │ │ │ └── application_job.rb │ ├── bin │ │ ├── dev │ │ ├── rake │ │ ├── thrust │ │ ├── jobs │ │ ├── rails │ │ ├── brakeman │ │ ├── rubocop │ │ ├── docker-entrypoint │ │ ├── kamal │ │ ├── setup │ │ └── bundle │ ├── .kamal │ │ ├── hooks │ │ │ ├── docker-setup.sample │ │ │ ├── post-proxy-reboot.sample │ │ │ ├── pre-proxy-reboot.sample │ │ │ ├── post-deploy.sample │ │ │ ├── pre-connect.sample │ │ │ ├── pre-build.sample │ │ │ └── pre-deploy.sample │ │ └── secrets │ ├── public │ │ └── robots.txt │ ├── config │ │ ├── boot.rb │ │ ├── environment.rb │ │ ├── database.yml │ │ ├── recurring.yml │ │ ├── cache.yml │ │ ├── queue.yml │ │ ├── routes.rb │ │ ├── initializers │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── cors.rb │ │ │ └── inflections.rb │ │ ├── credentials.yml.enc │ │ ├── application.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── puma.rb │ │ └── environments │ │ │ ├── test.rb │ │ │ └── development.rb │ ├── config.ru │ ├── Rakefile │ ├── .gitattributes │ ├── Gemfile │ ├── .dockerignore │ ├── .gitignore │ └── Dockerfile └── provider │ ├── log │ └── .keep │ ├── tmp │ ├── .keep │ ├── pids │ │ └── .keep │ └── storage │ │ └── .keep │ ├── lib │ └── tasks │ │ └── .keep │ ├── script │ └── .keep │ ├── storage │ └── .keep │ ├── vendor │ └── .keep │ ├── app │ ├── models │ │ ├── concerns │ │ │ └── .keep │ │ ├── address.rb │ │ ├── company.rb │ │ ├── application_record.rb │ │ └── person.rb │ ├── controllers │ │ ├── concerns │ │ │ └── .keep │ │ ├── application_controller.rb │ │ └── people_controller.rb │ ├── views │ │ └── layouts │ │ │ ├── mailer.text.erb │ │ │ └── mailer.html.erb │ ├── mailers │ │ └── application_mailer.rb │ └── jobs │ │ └── application_job.rb │ ├── bin │ ├── dev │ ├── rake │ ├── thrust │ ├── jobs │ ├── rails │ ├── brakeman │ ├── rubocop │ ├── docker-entrypoint │ ├── kamal │ └── setup │ ├── config │ ├── routes.rb │ ├── boot.rb │ ├── environment.rb │ ├── recurring.yml │ ├── cache.yml │ ├── queue.yml │ ├── database.yml │ ├── initializers │ │ ├── filter_parameter_logging.rb │ │ ├── cors.rb │ │ └── inflections.rb │ ├── credentials.yml.enc │ ├── locales │ │ └── en.yml │ ├── application.rb │ ├── puma.rb │ └── environments │ │ ├── test.rb │ │ └── development.rb │ ├── .kamal │ ├── hooks │ │ ├── docker-setup.sample │ │ ├── post-proxy-reboot.sample │ │ ├── pre-proxy-reboot.sample │ │ ├── post-deploy.sample │ │ ├── pre-connect.sample │ │ ├── pre-build.sample │ │ └── pre-deploy.sample │ └── secrets │ ├── public │ └── robots.txt │ ├── config.ru │ ├── Rakefile │ ├── db │ ├── migrate │ │ ├── 20241202183937_create_people.rb │ │ ├── 20241202183955_create_addresses.rb │ │ └── 20241202184017_create_companies.rb │ ├── seeds.rb │ ├── cache_schema.rb │ └── schema.rb │ ├── Gemfile │ ├── .gitattributes │ ├── .gitignore │ ├── .dockerignore │ └── Dockerfile ├── .env ├── .standard.yml ├── .rspec ├── sorbet ├── rbi │ ├── dsl │ │ ├── .gitattributes │ │ └── active_support │ │ │ └── callbacks.rbi │ ├── gems │ │ ├── .gitattributes │ │ ├── mail@2.8.1.rbi │ │ ├── marcel@1.0.4.rbi │ │ ├── nio4r@2.7.4.rbi │ │ ├── rails@8.0.1.rbi │ │ ├── globalid@1.2.1.rbi │ │ ├── net-imap@0.5.2.rbi │ │ ├── net-pop@0.1.2.rbi │ │ ├── net-smtp@0.5.0.rbi │ │ ├── stringio@3.1.2.rbi │ │ ├── zeitwerk@2.7.1.rbi │ │ ├── mini_mime@1.1.5.rbi │ │ ├── useragent@0.16.11.rbi │ │ ├── actiontext@8.0.1.rbi │ │ ├── io-console@0.8.0.rbi │ │ ├── actionmailbox@8.0.1.rbi │ │ ├── actionmailer@8.0.1.rbi │ │ ├── activestorage@8.0.1.rbi │ │ ├── ruby2_keywords@0.0.5.rbi │ │ ├── connection_pool@2.4.1.rbi │ │ ├── standard-custom@1.0.2.rbi │ │ ├── websocket-driver@0.7.6.rbi │ │ ├── rubocop-performance@1.23.0.rbi │ │ ├── standard-performance@1.6.0.rbi │ │ ├── websocket-extensions@0.1.5.rbi │ │ ├── mocha@2.7.1.rbi │ │ ├── simplecov_json_formatter@0.1.4.rbi │ │ ├── date@3.4.1.rbi │ │ ├── securerandom@0.4.1.rbi │ │ ├── bigdecimal@3.1.8.rbi │ │ ├── activejob@8.0.1.rbi │ │ └── rspec@3.13.0.rbi │ └── annotations │ │ └── .gitattributes ├── config └── tapioca │ ├── config.yml │ └── require.rb ├── .yardopts ├── lib ├── activeresource │ ├── lib │ │ ├── activeresource.rb │ │ ├── active_resource │ │ │ ├── associations │ │ │ │ └── builder │ │ │ │ │ ├── has_one.rb │ │ │ │ │ ├── has_many.rb │ │ │ │ │ ├── belongs_to.rb │ │ │ │ │ └── association.rb │ │ │ ├── formats │ │ │ │ ├── xml_format.rb │ │ │ │ └── json_format.rb │ │ │ ├── callbacks.rb │ │ │ ├── active_job_serializer.rb │ │ │ ├── railtie.rb │ │ │ ├── inheriting_hash.rb │ │ │ ├── formats.rb │ │ │ ├── log_subscriber.rb │ │ │ ├── schema.rb │ │ │ ├── reflection.rb │ │ │ ├── threadsafe_attributes.rb │ │ │ └── exceptions.rb │ │ └── active_resource.rb │ ├── test │ │ ├── fixtures │ │ │ ├── post.rb │ │ │ ├── customer.rb │ │ │ ├── comment.rb │ │ │ ├── pet.rb │ │ │ ├── proxy.rb │ │ │ ├── street_address.rb │ │ │ ├── subscription_plan.rb │ │ │ ├── sound.rb │ │ │ ├── inventory.rb │ │ │ ├── product.rb │ │ │ ├── beast.rb │ │ │ ├── person.rb │ │ │ ├── weather.rb │ │ │ ├── address.rb │ │ │ ├── project.rb │ │ │ └── fixtures.rbi │ │ ├── cases │ │ │ ├── inheritence_test.rb │ │ │ ├── inheriting_hash_test.rb │ │ │ ├── associations │ │ │ │ └── builder │ │ │ │ │ ├── has_one_test.rb │ │ │ │ │ ├── has_many_test.rb │ │ │ │ │ └── belongs_to_test.rb │ │ │ ├── base │ │ │ │ └── equality_test.rb │ │ │ ├── active_job_serializer_test.rb │ │ │ ├── log_subscriber_test.rb │ │ │ ├── reflection_test.rb │ │ │ └── validations_test.rb │ │ ├── setter_trap.rb │ │ └── threadsafe_attributes_test.rb │ ├── .gitignore │ └── examples │ │ └── performance.rb ├── active_cached_resource │ ├── version.rb │ ├── constants.rb │ ├── caching_strategies │ │ ├── sql_cache.rb │ │ └── active_support_cache.rb │ ├── logger.rb │ ├── model.rb │ ├── collection.rb │ └── configuration.rb ├── active_cached_resource.rb └── generators │ └── active_cached_resource │ ├── templates │ └── migration.erb │ └── active_record_generator.rb ├── bin ├── setup ├── console └── tapioca ├── spec ├── active_cached_resource │ ├── caching_strategies │ │ ├── active_support_cache_spec.rb │ │ ├── sql_cache_spec.rb │ │ └── base_spec.rb │ ├── logger_spec.rb │ └── configuration_spec.rb ├── spec_helper.rb └── support │ └── shared_examples │ └── caching_strategies_examples.rb ├── docker-compose.yml ├── .rubocop.yml ├── .github └── workflows │ ├── ci.yml │ ├── lint.yml │ ├── test.yml │ └── release.yml ├── Rakefile ├── Gemfile ├── LICENSE ├── scripts └── run_tests.sh ├── active_cached_resource.gemspec ├── README.md ├── Dockerfile └── CHANGELOG.md /example/consumer/db/seeds.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/script/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/storage/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/tmp/cache/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/tmp/pids/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/script/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/storage/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/tmp/pids/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/tmp/storage/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/tmp/storage/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/consumer/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | JUNIT_REPORTS_PATH='tmp/junit_test_reports' -------------------------------------------------------------------------------- /example/consumer/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/provider/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.standard.yml: -------------------------------------------------------------------------------- 1 | ruby_version: 3.2 2 | format: progress -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format progress 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /example/consumer/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /example/provider/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.rbi linguist-generated=true 2 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.rbi linguist-generated=true 2 | -------------------------------------------------------------------------------- /sorbet/rbi/annotations/.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.rbi linguist-vendored=true 2 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --exclude lib/activeresource/test 2 | --exclude lib/activeresource/examples -------------------------------------------------------------------------------- /example/consumer/bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | exec "./bin/rails", "server", *ARGV 3 | -------------------------------------------------------------------------------- /example/provider/bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | exec "./bin/rails", "server", *ARGV 3 | -------------------------------------------------------------------------------- /example/provider/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | resources :people 4 | end 5 | -------------------------------------------------------------------------------- /example/consumer/.kamal/hooks/docker-setup.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Docker set up on $KAMAL_HOSTS..." 4 | -------------------------------------------------------------------------------- /example/provider/.kamal/hooks/docker-setup.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Docker set up on $KAMAL_HOSTS..." 4 | -------------------------------------------------------------------------------- /example/provider/app/models/address.rb: -------------------------------------------------------------------------------- 1 | class Address < ApplicationRecord 2 | belongs_to :person 3 | end 4 | -------------------------------------------------------------------------------- /example/provider/app/models/company.rb: -------------------------------------------------------------------------------- 1 | class Company < ApplicationRecord 2 | belongs_to :person 3 | end 4 | -------------------------------------------------------------------------------- /lib/activeresource/lib/activeresource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "active_resource" 4 | -------------------------------------------------------------------------------- /example/consumer/.kamal/hooks/post-proxy-reboot.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Rebooted kamal-proxy on $KAMAL_HOSTS" 4 | -------------------------------------------------------------------------------- /example/consumer/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::API 2 | end 3 | -------------------------------------------------------------------------------- /example/provider/.kamal/hooks/post-proxy-reboot.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Rebooted kamal-proxy on $KAMAL_HOSTS" 4 | -------------------------------------------------------------------------------- /example/provider/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::API 2 | end 3 | -------------------------------------------------------------------------------- /example/consumer/.kamal/hooks/pre-proxy-reboot.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Rebooting kamal-proxy on $KAMAL_HOSTS..." 4 | -------------------------------------------------------------------------------- /example/provider/.kamal/hooks/pre-proxy-reboot.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Rebooting kamal-proxy on $KAMAL_HOSTS..." 4 | -------------------------------------------------------------------------------- /example/consumer/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /example/provider/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /example/consumer/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /example/consumer/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /example/provider/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /example/provider/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /lib/active_cached_resource/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveCachedResource 4 | VERSION = "0.2.3" 5 | end 6 | -------------------------------------------------------------------------------- /sorbet/config: -------------------------------------------------------------------------------- 1 | --dir 2 | . 3 | --ignore=tmp/ 4 | --ignore=vendor/ 5 | --ignore=example 6 | --suppress-payload-superclass-redefinition-for=Reline::ANSI -------------------------------------------------------------------------------- /example/consumer/bin/thrust: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | load Gem.bin_path("thruster", "thrust") 6 | -------------------------------------------------------------------------------- /example/provider/bin/thrust: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | load Gem.bin_path("thruster", "thrust") 6 | -------------------------------------------------------------------------------- /example/consumer/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /example/consumer/bin/jobs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative "../config/environment" 4 | require "solid_queue/cli" 5 | 6 | SolidQueue::Cli.start(ARGV) 7 | -------------------------------------------------------------------------------- /example/provider/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /example/provider/bin/jobs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative "../config/environment" 4 | require "solid_queue/cli" 5 | 6 | SolidQueue::Cli.start(ARGV) 7 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /example/consumer/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /example/provider/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/post.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Post < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000" 5 | end 6 | -------------------------------------------------------------------------------- /example/consumer/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /example/provider/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/customer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Customer < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000" 5 | end 6 | -------------------------------------------------------------------------------- /example/consumer/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /example/provider/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/comment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Comment < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000/posts/:post_id" 5 | end 6 | -------------------------------------------------------------------------------- /example/consumer/bin/brakeman: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | ARGV.unshift("--ensure-latest") 6 | 7 | load Gem.bin_path("brakeman", "brakeman") 8 | -------------------------------------------------------------------------------- /example/provider/bin/brakeman: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | ARGV.unshift("--ensure-latest") 6 | 7 | load Gem.bin_path("brakeman", "brakeman") 8 | -------------------------------------------------------------------------------- /example/consumer/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /example/provider/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /lib/active_cached_resource/constants.rb: -------------------------------------------------------------------------------- 1 | module ActiveCachedResource 2 | module Constants 3 | GLOBAL_PREFIX = "acr" 4 | PREFIX_SEPARATOR = ":" 5 | RELOAD_PARAM = :_acr_reload 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/pet.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Pet < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000" 5 | self.prefix = "/people/:person_id/" 6 | end 7 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/proxy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ProxyResource < ActiveResource::Base 4 | self.site = "http://localhost" 5 | self.proxy = "http://user:password@proxy.local:3000" 6 | end 7 | -------------------------------------------------------------------------------- /example/provider/app/models/person.rb: -------------------------------------------------------------------------------- 1 | class Person < ApplicationRecord 2 | has_many :addresses, dependent: :destroy 3 | has_one :company, dependent: :destroy 4 | 5 | accepts_nested_attributes_for :addresses, :company 6 | end 7 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/street_address.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class StreetAddress < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000/people/:person_id" 5 | self.element_name = "address" 6 | end 7 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/subscription_plan.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubscriptionPlan < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000" 5 | self.element_name = "plan" 6 | self.primary_key = :code 7 | end 8 | -------------------------------------------------------------------------------- /example/consumer/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /example/provider/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/sound.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Asset 4 | class Sound < ActiveResource::Base 5 | self.site = "http://37s.sunrise.i:3000" 6 | end 7 | end 8 | 9 | # to test namespacing in a module 10 | class Author 11 | end 12 | -------------------------------------------------------------------------------- /spec/active_cached_resource/caching_strategies/active_support_cache_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ActiveCachedResource::CachingStrategies::ActiveSupportCache do 2 | let(:constructor_args) { [ActiveSupport::Cache::MemoryStore.new] } 3 | 4 | it_behaves_like "a caching strategy" 5 | end 6 | -------------------------------------------------------------------------------- /sorbet/tapioca/config.yml: -------------------------------------------------------------------------------- 1 | gem: 2 | # Add your `gem` command parameters here: 3 | # 4 | # exclude: 5 | # - gem_name 6 | # doc: true 7 | # workers: 5 8 | dsl: 9 | # Add your `dsl` command parameters here: 10 | # 11 | # exclude: 12 | # - SomeGeneratorName 13 | # workers: 5 14 | -------------------------------------------------------------------------------- /example/consumer/config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: sqlite3 3 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 4 | timeout: 5000 5 | database: storage/db.sqlite3 6 | 7 | development: 8 | <<: *default 9 | 10 | test: 11 | <<: *default 12 | 13 | production: 14 | <<: *default -------------------------------------------------------------------------------- /example/provider/db/migrate/20241202183937_create_people.rb: -------------------------------------------------------------------------------- 1 | class CreatePeople < ActiveRecord::Migration[8.0] 2 | def change 3 | create_table :people do |t| 4 | t.string :first_name 5 | t.string :last_name 6 | t.integer :age 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /example/consumer/bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | # explicit rubocop config increases performance slightly while avoiding config confusion. 6 | ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) 7 | 8 | load Gem.bin_path("rubocop", "rubocop") 9 | -------------------------------------------------------------------------------- /example/provider/bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "rubygems" 3 | require "bundler/setup" 4 | 5 | # explicit rubocop config increases performance slightly while avoiding config confusion. 6 | ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) 7 | 8 | load Gem.bin_path("rubocop", "rubocop") 9 | -------------------------------------------------------------------------------- /example/consumer/app/models/person.rb: -------------------------------------------------------------------------------- 1 | class Person < ActiveResource::Base 2 | cached_resource({cache_store: Rails.cache, cache_strategy: :active_support_cache}) 3 | 4 | self.site = "http://localhost:3000" 5 | self.include_format_in_path = false 6 | 7 | self.user = "admin" 8 | self.password = "secret" 9 | end 10 | -------------------------------------------------------------------------------- /example/consumer/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /example/consumer/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/provider/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /example/provider/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/active_cached_resource/caching_strategies/sql_cache_spec.rb: -------------------------------------------------------------------------------- 1 | class CacheModel < ActiveRecord::Base 2 | self.table_name = "active_cached_resources" 3 | end 4 | 5 | RSpec.describe ActiveCachedResource::CachingStrategies::SQLCache do 6 | let(:constructor_args) { [CacheModel] } 7 | 8 | it_behaves_like "a caching strategy" 9 | end 10 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "active_cached_resource" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | require "irb" 11 | IRB.start(__FILE__) 12 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | services: 4 | app: 5 | container_name: acr 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | volumes: 10 | - .:/app 11 | test: 12 | build: 13 | context: . 14 | dockerfile: Dockerfile 15 | command: bash scripts/run_tests.sh 16 | volumes: 17 | - .:/app -------------------------------------------------------------------------------- /sorbet/rbi/gems/mail@2.8.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `mail` gem. 5 | # Please instead update this file by running `bin/tapioca gem mail`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/marcel@1.0.4.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `marcel` gem. 5 | # Please instead update this file by running `bin/tapioca gem marcel`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/nio4r@2.7.4.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `nio4r` gem. 5 | # Please instead update this file by running `bin/tapioca gem nio4r`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rails@8.0.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rails` gem. 5 | # Please instead update this file by running `bin/tapioca gem rails`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/globalid@1.2.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `globalid` gem. 5 | # Please instead update this file by running `bin/tapioca gem globalid`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/net-imap@0.5.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `net-imap` gem. 5 | # Please instead update this file by running `bin/tapioca gem net-imap`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/net-pop@0.1.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `net-pop` gem. 5 | # Please instead update this file by running `bin/tapioca gem net-pop`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/net-smtp@0.5.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `net-smtp` gem. 5 | # Please instead update this file by running `bin/tapioca gem net-smtp`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/stringio@3.1.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `stringio` gem. 5 | # Please instead update this file by running `bin/tapioca gem stringio`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/zeitwerk@2.7.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `zeitwerk` gem. 5 | # Please instead update this file by running `bin/tapioca gem zeitwerk`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /lib/activeresource/.gitignore: -------------------------------------------------------------------------------- 1 | # Don't put *.swp, *.bak, etc here; those belong in a global ~/.gitignore. 2 | # Check out http://help.github.com/ignore-files/ for how to set that up. 3 | 4 | debug.log 5 | .Gemfile 6 | /.bundle 7 | /.ruby-version 8 | *.lock 9 | /pkg 10 | /dist 11 | /doc/rdoc 12 | /*/doc 13 | /*/test/tmp 14 | /RDOC_MAIN.rdoc 15 | .rubocop-http* 16 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/mini_mime@1.1.5.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `mini_mime` gem. 5 | # Please instead update this file by running `bin/tapioca gem mini_mime`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/useragent@0.16.11.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `useragent` gem. 5 | # Please instead update this file by running `bin/tapioca gem useragent`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /example/consumer/config/recurring.yml: -------------------------------------------------------------------------------- 1 | # production: 2 | # periodic_cleanup: 3 | # class: CleanSoftDeletedRecordsJob 4 | # queue: background 5 | # args: [ 1000, { batch_size: 500 } ] 6 | # schedule: every hour 7 | # periodic_command: 8 | # command: "SoftDeletedRecord.due.delete_all" 9 | # priority: 2 10 | # schedule: at 5am every day 11 | -------------------------------------------------------------------------------- /example/provider/config/recurring.yml: -------------------------------------------------------------------------------- 1 | # production: 2 | # periodic_cleanup: 3 | # class: CleanSoftDeletedRecordsJob 4 | # queue: background 5 | # args: [ 1000, { batch_size: 500 } ] 6 | # schedule: every hour 7 | # periodic_command: 8 | # command: "SoftDeletedRecord.due.delete_all" 9 | # priority: 2 10 | # schedule: at 5am every day 11 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/actiontext@8.0.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `actiontext` gem. 5 | # Please instead update this file by running `bin/tapioca gem actiontext`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/io-console@0.8.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `io-console` gem. 5 | # Please instead update this file by running `bin/tapioca gem io-console`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/inventory.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Inventory < ActiveResource::Base 4 | include ActiveResource::Singleton 5 | self.site = "http://37s.sunrise.i:3000" 6 | self.prefix = "/products/:product_id/" 7 | 8 | schema do 9 | integer :total 10 | integer :used 11 | 12 | string :status 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/actionmailbox@8.0.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `actionmailbox` gem. 5 | # Please instead update this file by running `bin/tapioca gem actionmailbox`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/actionmailer@8.0.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `actionmailer` gem. 5 | # Please instead update this file by running `bin/tapioca gem actionmailer`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/activestorage@8.0.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `activestorage` gem. 5 | # Please instead update this file by running `bin/tapioca gem activestorage`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/product.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Product < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000" 5 | # X-Inherited-Header is for testing that any subclasses 6 | # include the headers of this class 7 | self.headers["X-Inherited-Header"] = "present" 8 | end 9 | 10 | class SubProduct < Product 11 | end 12 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/ruby2_keywords@0.0.5.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `ruby2_keywords` gem. 5 | # Please instead update this file by running `bin/tapioca gem ruby2_keywords`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/connection_pool@2.4.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `connection_pool` gem. 5 | # Please instead update this file by running `bin/tapioca gem connection_pool`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/standard-custom@1.0.2.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `standard-custom` gem. 5 | # Please instead update this file by running `bin/tapioca gem standard-custom`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/websocket-driver@0.7.6.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `websocket-driver` gem. 5 | # Please instead update this file by running `bin/tapioca gem websocket-driver`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/rubocop-performance@1.23.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `rubocop-performance` gem. 5 | # Please instead update this file by running `bin/tapioca gem rubocop-performance`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /example/provider/db/migrate/20241202183955_create_addresses.rb: -------------------------------------------------------------------------------- 1 | class CreateAddresses < ActiveRecord::Migration[8.0] 2 | def change 3 | create_table :addresses do |t| 4 | t.string :street 5 | t.string :city 6 | t.string :state 7 | t.string :zip 8 | t.references :person, null: false, foreign_key: true 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/standard-performance@1.6.0.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `standard-performance` gem. 5 | # Please instead update this file by running `bin/tapioca gem standard-performance`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/websocket-extensions@0.1.5.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `websocket-extensions` gem. 5 | # Please instead update this file by running `bin/tapioca gem websocket-extensions`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /example/consumer/config/cache.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | store_options: 3 | # Cap age of oldest cache entry to fulfill retention policies 4 | # max_age: <%= 60.days.to_i %> 5 | max_size: <%= 256.megabytes %> 6 | namespace: <%= Rails.env %> 7 | 8 | development: 9 | <<: *default 10 | 11 | test: 12 | <<: *default 13 | 14 | production: 15 | database: cache 16 | <<: *default 17 | -------------------------------------------------------------------------------- /example/consumer/config/queue.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | dispatchers: 3 | - polling_interval: 1 4 | batch_size: 500 5 | workers: 6 | - queues: "*" 7 | threads: 3 8 | processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %> 9 | polling_interval: 0.1 10 | 11 | development: 12 | <<: *default 13 | 14 | test: 15 | <<: *default 16 | 17 | production: 18 | <<: *default 19 | -------------------------------------------------------------------------------- /example/provider/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "rails", "~> 8.0.0" 4 | gem "sqlite3", ">= 2.1" 5 | gem "puma", ">= 5.0" 6 | gem "tzinfo-data", platforms: %i[ windows jruby ] 7 | gem "solid_cache" 8 | gem "solid_queue" 9 | gem "kamal", require: false 10 | gem "thruster", require: false 11 | 12 | group :development, :test do 13 | gem "pry-byebug", "~> 3.10", ">= 3.10.1" 14 | end 15 | -------------------------------------------------------------------------------- /example/provider/config/cache.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | store_options: 3 | # Cap age of oldest cache entry to fulfill retention policies 4 | # max_age: <%= 60.days.to_i %> 5 | max_size: <%= 256.megabytes %> 6 | namespace: <%= Rails.env %> 7 | 8 | development: 9 | <<: *default 10 | 11 | test: 12 | <<: *default 13 | 14 | production: 15 | database: cache 16 | <<: *default 17 | -------------------------------------------------------------------------------- /example/provider/config/queue.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | dispatchers: 3 | - polling_interval: 1 4 | batch_size: 500 5 | workers: 6 | - queues: "*" 7 | threads: 3 8 | processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %> 9 | polling_interval: 0.1 10 | 11 | development: 12 | <<: *default 13 | 14 | test: 15 | <<: *default 16 | 17 | production: 18 | <<: *default 19 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/mocha@2.7.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `mocha` gem. 5 | # Please instead update this file by running `bin/tapioca gem mocha`. 6 | 7 | 8 | # source://mocha//lib/mocha/version.rb#1 9 | module Mocha; end 10 | 11 | # source://mocha//lib/mocha/version.rb#2 12 | Mocha::VERSION = T.let(T.unsafe(nil), String) 13 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `simplecov_json_formatter` gem. 5 | # Please instead update this file by running `bin/tapioca gem simplecov_json_formatter`. 6 | 7 | 8 | # THIS IS AN EMPTY RBI FILE. 9 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem 10 | -------------------------------------------------------------------------------- /example/consumer/.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | # Mark any vendored files as having been vendored. 7 | vendor/* linguist-vendored 8 | config/credentials/*.yml.enc diff=rails_credentials 9 | config/credentials.yml.enc diff=rails_credentials 10 | -------------------------------------------------------------------------------- /example/provider/.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | # Mark any vendored files as having been vendored. 7 | vendor/* linguist-vendored 8 | config/credentials/*.yml.enc diff=rails_credentials 9 | config/credentials.yml.enc diff=rails_credentials 10 | -------------------------------------------------------------------------------- /example/consumer/.kamal/hooks/post-deploy.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A sample post-deploy hook 4 | # 5 | # These environment variables are available: 6 | # KAMAL_RECORDED_AT 7 | # KAMAL_PERFORMER 8 | # KAMAL_VERSION 9 | # KAMAL_HOSTS 10 | # KAMAL_ROLE (if set) 11 | # KAMAL_DESTINATION (if set) 12 | # KAMAL_RUNTIME 13 | 14 | echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds" 15 | -------------------------------------------------------------------------------- /example/provider/.kamal/hooks/post-deploy.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A sample post-deploy hook 4 | # 5 | # These environment variables are available: 6 | # KAMAL_RECORDED_AT 7 | # KAMAL_PERFORMER 8 | # KAMAL_VERSION 9 | # KAMAL_HOSTS 10 | # KAMAL_ROLE (if set) 11 | # KAMAL_DESTINATION (if set) 12 | # KAMAL_RUNTIME 13 | 14 | echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds" 15 | -------------------------------------------------------------------------------- /example/provider/db/migrate/20241202184017_create_companies.rb: -------------------------------------------------------------------------------- 1 | class CreateCompanies < ActiveRecord::Migration[8.0] 2 | def change 3 | create_table :companies do |t| 4 | t.string :name 5 | t.string :street 6 | t.string :city 7 | t.string :state 8 | t.string :zip 9 | t.references :person, null: false, foreign_key: true 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/beast.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class BeastResource < ActiveResource::Base 4 | self.site = "http://beast.caboo.se" 5 | site.user = "foo" 6 | site.password = "bar" 7 | end 8 | 9 | class Forum < BeastResource 10 | # taken from BeastResource 11 | # self.site = 'http://beast.caboo.se' 12 | end 13 | 14 | class Topic < BeastResource 15 | self.site += "/forums/:forum_id" 16 | end 17 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/person.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Person < ActiveResource::Base 4 | self.site = "http://37s.sunrise.i:3000" 5 | end 6 | 7 | module External 8 | class Person < ActiveResource::Base 9 | self.site = "http://atq.caffeine.intoxication.it" 10 | end 11 | 12 | class ProfileData < ActiveResource::Base 13 | self.site = "http://external.profile.data.nl" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/active_cached_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "activeresource/lib/activeresource" 4 | 5 | require_relative "active_cached_resource/constants" 6 | require_relative "active_cached_resource/model" 7 | require_relative "active_cached_resource/version" 8 | 9 | module ActiveCachedResource 10 | end 11 | 12 | module ActiveResource 13 | class Base 14 | include ActiveCachedResource::Model 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/associations/builder/has_one.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource::Associations::Builder 4 | class HasOne < Association 5 | self.macro = :has_one 6 | 7 | def build 8 | validate_options 9 | model.create_reflection(self.class.macro, name, options).tap do |reflection| 10 | model.defines_has_one_finder_method(reflection) 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /example/consumer/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "active_cached_resource", path: "../../" 4 | gem "rails", "~> 8.0.0" 5 | gem "sqlite3", ">= 2.1" 6 | gem "puma", ">= 5.0" 7 | gem "tzinfo-data", platforms: %i[ windows jruby ] 8 | gem "solid_cache" 9 | gem "solid_queue" 10 | gem "kamal", require: false 11 | gem "thruster", require: false 12 | 13 | group :development, :test do 14 | gem "pry-byebug" 15 | gem "brakeman", require: false 16 | end 17 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/associations/builder/has_many.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource::Associations::Builder 4 | class HasMany < Association 5 | self.macro = :has_many 6 | 7 | def build 8 | validate_options 9 | model.create_reflection(self.class.macro, name, options).tap do |reflection| 10 | model.defines_has_many_finder_method(reflection) 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /example/consumer/bin/docker-entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Enable jemalloc for reduced memory usage and latency. 4 | if [ -z "${LD_PRELOAD+x}" ]; then 5 | LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) 6 | export LD_PRELOAD 7 | fi 8 | 9 | # If running the rails server then create or migrate existing database 10 | if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then 11 | ./bin/rails db:prepare 12 | fi 13 | 14 | exec "${@}" 15 | -------------------------------------------------------------------------------- /example/provider/bin/docker-entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Enable jemalloc for reduced memory usage and latency. 4 | if [ -z "${LD_PRELOAD+x}" ]; then 5 | LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) 6 | export LD_PRELOAD 7 | fi 8 | 9 | # If running the rails server then create or migrate existing database 10 | if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then 11 | ./bin/rails db:prepare 12 | fi 13 | 14 | exec "${@}" 15 | -------------------------------------------------------------------------------- /example/provider/db/seeds.rb: -------------------------------------------------------------------------------- 1 | person = Person.create!( 2 | first_name: "John", 3 | last_name: "Doe", 4 | age: 25 5 | ) 6 | 7 | person.addresses.create!([ 8 | { street: "123 Main St", city: "Springfield", state: "IL", zip: "62701" }, 9 | { street: "456 Elm St", city: "Springfield", state: "IL", zip: "62702" } 10 | ]) 11 | 12 | person.create_company!( 13 | name: "Acme", 14 | street: "789 Maple St", 15 | city: "Springfield", 16 | state: "IL", 17 | zip: "62703" 18 | ) -------------------------------------------------------------------------------- /example/provider/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem "sqlite3" 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | database: storage/db.sqlite3 12 | 13 | development: 14 | <<: *default 15 | 16 | test: 17 | <<: *default 18 | 19 | production: 20 | <<: *default 21 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_mode: 2 | merge: 3 | - Exclude 4 | 5 | require: 6 | - standard 7 | - standard-custom 8 | - standard-performance 9 | - rubocop-performance 10 | 11 | inherit_gem: 12 | standard: config/base.yml 13 | standard-performance: config/base.yml 14 | standard-custom: config/base.yml 15 | 16 | AllCops: 17 | SuggestExtensions: false 18 | TargetRubyVersion: 3.2.x 19 | NewCops: enable 20 | Exclude: 21 | - example/**/* 22 | - lib/activeresource/**/* 23 | - sorbet/**/* -------------------------------------------------------------------------------- /example/consumer/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html 3 | 4 | # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. 5 | # Can be used by load balancers and uptime monitors to verify that the app is live. 6 | get "up" => "rails/health#show", as: :rails_health_check 7 | 8 | # Defines the root path route ("/") 9 | # root "posts#index" 10 | end 11 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/associations/builder/belongs_to.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource::Associations::Builder 4 | class BelongsTo < Association 5 | self.valid_options += [:foreign_key] 6 | 7 | self.macro = :belongs_to 8 | 9 | def build 10 | validate_options 11 | reflection = model.create_reflection(self.class.macro, name, options) 12 | model.defines_belongs_to_finder_method(reflection) 13 | reflection 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | workflow_dispatch: 5 | workflow_call: 6 | concurrency: 7 | group: ${{ github.ref }}-${{ github.workflow }}-ci 8 | cancel-in-progress: true 9 | jobs: 10 | lint: 11 | permissions: 12 | checks: write 13 | contents: write 14 | pull-requests: write 15 | uses: ./.github/workflows/lint.yml 16 | test: 17 | permissions: 18 | contents: read 19 | checks: write 20 | pull-requests: write 21 | uses: ./.github/workflows/test.yml 22 | -------------------------------------------------------------------------------- /example/consumer/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 4 | # Use this to limit dissemination of sensitive information. 5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc 8 | ] 9 | -------------------------------------------------------------------------------- /example/provider/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 4 | # Use this to limit dissemination of sensitive information. 5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc 8 | ] 9 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/weather.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Weather < ActiveResource::Base 4 | include ActiveResource::Singleton 5 | self.site = "http://37s.sunrise.i:3000" 6 | 7 | schema do 8 | string :status 9 | string :temperature 10 | end 11 | end 12 | 13 | class WeatherDashboard < ActiveResource::Base 14 | include ActiveResource::Singleton 15 | self.site = "http://37s.sunrise.i:3000" 16 | self.singleton_name = "dashboard" 17 | 18 | schema do 19 | string :status 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /example/consumer/config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | uu65VYomvmWVEWW6SSl5DSXMmMkwCOK5sZf1hhDv++aixw2P6H6FZGq4Z5qszMcnMPn1l2rn2ud/N0WUCozWiaqUG1pDXgI06lrQ6SwiINeIepICNjmJqWaNK9+P9I1SggMnbYFZ5GqYTmpZOtBtQRyX5HTqGuBATmWdcY0MJEkgJ65ShJZ7vBMDrzYNd3OtiFxnBGO4JrgnI+R9gp+lPzijz5FAD+Dmo+Ymq6q2lrNdxGhNLEya2v6muyeXNaXAXob7R0TlBuNG3E2KgVP9mnflJ1HnbgMCEWXRuV6Hr3rUVDjOxVYsmA0UefITb+LZmRQ8gyw7b1uMgIFVPAaChM95MA/I+VU5DL2McXl4KmOvuITV5KFUkKo5Z4W+d8dELS5jxnqJF27cRl+sDbFEtp0aWjYZZI8sREqf+4g15feJ10g5+Ip+03FXmAWjq1Nn0eqhaZWe0vbR9oeHlEtLxOjVXPhIlmXEQnjPrxzzgST2S9Tyszu47mZX--ZP7jipbGIUdfcjcS--4zIO4SwRee5gC86ZqZ8E5A== -------------------------------------------------------------------------------- /example/provider/config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | oLf/r/U+4Xw/JBFp8Z985fyzuzpHSmoLclxhjnOu/qCZTH01VcdOSt2YJKyhADvjlMjrhkK/InDPs5AtY3KdGH4xe8WY06m1a6iclC4OPaoKgiNKeGB9XK+G4yec8SBIGE9VHWu6Thb3IT0HFnxVSncoZqgtbOd5kek3w6HQ1nE2hsqNALlt6349SfyIXDTB+7wum8uvkA9cy+YwZat/jTlXxY86mvTYmam4XB6GbeLX2464ZaQv2W+3B08nubVWtKqh+z9ZNTvUQPfy+6iycZVJDDIJVNLNViiuHwFNisUNJrwCr7rUKN+p72c/sWR5rzcSqAfFxhs7HfY90Y0yae6rD6XwT00yrxIz0o9Pz70aZqWpc6/ot5imdEAgOU3ctjPnl5WGV4/oXcfJr7L4Ofg3PaVOoI79klwKEzt4Y0g9i7ualSUIxIRtu6xwdZlhTj69ffEFrb9AGCTuFCNFzIcN45jLduEUxW42O5RD4yJySNLqH4U5R7P4--STxlRDIaEzWJamko--OR6vwoRNehHeH1+fFTHTpQ== -------------------------------------------------------------------------------- /sorbet/tapioca/require.rb: -------------------------------------------------------------------------------- 1 | # typed: true 2 | # frozen_string_literal: true 3 | 4 | require "active_job/serializers/object_serializer" 5 | require "active_support/all" 6 | require "active_support/concern" 7 | require "active_support/log_subscriber/test_helper" 8 | require "dotenv/load" 9 | require "minitest/reporters" 10 | require "rails/generators" 11 | require "rails/generators/active_record" 12 | require "rails/generators/channel/channel_generator" 13 | require "rails/generators/migration" 14 | require "rake/testtask" 15 | require "rspec/core/rake_task" 16 | require "rubocop/rake_task" 17 | -------------------------------------------------------------------------------- /example/consumer/config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin Ajax requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | # allow do 10 | # origins "example.com" 11 | # 12 | # resource "*", 13 | # headers: :any, 14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | # end 16 | # end 17 | -------------------------------------------------------------------------------- /example/provider/config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin Ajax requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | # allow do 10 | # origins "example.com" 11 | # 12 | # resource "*", 13 | # headers: :any, 14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | # end 16 | # end 17 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/formats/xml_format.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_support/core_ext/hash/conversions" 4 | 5 | module ActiveResource 6 | module Formats 7 | module XmlFormat 8 | extend self 9 | 10 | def extension 11 | "xml" 12 | end 13 | 14 | def mime_type 15 | "application/xml" 16 | end 17 | 18 | def encode(hash, options = {}) 19 | hash.to_xml(options) 20 | end 21 | 22 | def decode(xml) 23 | Formats.remove_root(Hash.from_xml(xml)) 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/address.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # turns everything into the same object 4 | class AddressXMLFormatter 5 | include ActiveResource::Formats::XmlFormat 6 | 7 | def decode(xml) 8 | data = ActiveResource::Formats::XmlFormat.decode(xml) 9 | # process address fields 10 | data.each do |address| 11 | address["city_state"] = "#{address['city']}, #{address['state']}" 12 | end 13 | data 14 | end 15 | end 16 | 17 | class AddressResource < ActiveResource::Base 18 | self.element_name = "address" 19 | self.format = AddressXMLFormatter.new 20 | end 21 | -------------------------------------------------------------------------------- /sorbet/rbi/dsl/active_support/callbacks.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for dynamic methods in `ActiveSupport::Callbacks`. 5 | # Please instead update this file by running `bin/tapioca dsl ActiveSupport::Callbacks`. 6 | 7 | 8 | module ActiveSupport::Callbacks 9 | include GeneratedInstanceMethods 10 | 11 | mixes_in_class_methods GeneratedClassMethods 12 | 13 | module GeneratedClassMethods 14 | def __callbacks; end 15 | def __callbacks=(value); end 16 | end 17 | 18 | module GeneratedInstanceMethods 19 | def __callbacks; end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/active_cached_resource/templates/migration.erb: -------------------------------------------------------------------------------- 1 | class CreateActiveCachedResourceTable < ActiveRecord::Migration<%= migration_version %> 2 | def up 3 | create_table :active_cached_resources, id: false do |t| 4 | t.binary :key, limit: 512, null: false 5 | t.binary :value, null: false 6 | t.datetime :expires_at, null: false 7 | 8 | t.index [:key, :expires_at], unique: true, name: "index_active_cached_resources_on_key_and_expires_at" 9 | t.index :key, name: "index_active_cached_resources_on_key" 10 | end 11 | end 12 | 13 | def down 14 | drop_table :active_cached_resources 15 | end 16 | end -------------------------------------------------------------------------------- /example/consumer/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails" 4 | require "active_model/railtie" 5 | require "active_job/railtie" 6 | require "active_record/railtie" 7 | require "action_controller/railtie" 8 | require "action_mailer/railtie" 9 | require "action_view/railtie" 10 | Bundler.require(*Rails.groups) 11 | 12 | module Consumer 13 | class Application < Rails::Application 14 | config.load_defaults 8.0 15 | config.autoload_lib(ignore: %w[assets tasks]) 16 | config.api_only = true 17 | 18 | config.cache_store = :file_store, Rails.root.join("tmp", "storage"), { expires_in: 1.day } 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/inheritence_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | 5 | require "fixtures/product" 6 | 7 | class InheritenceTest < ActiveSupport::TestCase 8 | def test_sub_class_retains_ancestor_headers 9 | ActiveResource::HttpMock.respond_to do |mock| 10 | mock.get "/sub_products/1.json", 11 | { "Accept" => "application/json", "X-Inherited-Header" => "present" }, 12 | { id: 1, name: "Sub Product" }.to_json, 13 | 200 14 | end 15 | 16 | sub_product = SubProduct.find(1) 17 | assert_equal "SubProduct", sub_product.class.to_s 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/formats/json_format.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_support/json" 4 | 5 | module ActiveResource 6 | module Formats 7 | module JsonFormat 8 | extend self 9 | 10 | def extension 11 | "json" 12 | end 13 | 14 | def mime_type 15 | "application/json" 16 | end 17 | 18 | def encode(hash, options = nil) 19 | ActiveSupport::JSON.encode(hash, options) 20 | end 21 | 22 | def decode(json) 23 | return nil if json.nil? 24 | Formats.remove_root(ActiveSupport::JSON.decode(json)) 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/project.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # used to test validations 4 | class Project < ActiveResource::Base 5 | self.site = "http://37s.sunrise.i:3000" 6 | schema do 7 | string :email 8 | string :name 9 | end 10 | 11 | validates :name, presence: true 12 | validates :description, presence: false, length: { maximum: 10 } 13 | validate :description_greater_than_three_letters 14 | 15 | # to test the validate *callback* works 16 | def description_greater_than_three_letters 17 | errors.add :description, "must be greater than three letters long" if description.length < 3 unless description.blank? 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/activeresource/test/setter_trap.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SetterTrap < BasicObject 4 | class << self 5 | def rollback_sets(obj) 6 | trapped = new(obj) 7 | yield(trapped).tap { trapped.rollback_sets } 8 | end 9 | end 10 | 11 | def initialize(obj) 12 | @cache = {} 13 | @obj = obj 14 | end 15 | 16 | def respond_to?(method) 17 | @obj.respond_to?(method) 18 | end 19 | 20 | def method_missing(method, *args, &proc) 21 | @cache[method] ||= @obj.send($`) if method.to_s =~ /=$/ 22 | @obj.send method, *args, &proc 23 | end 24 | 25 | def rollback_sets 26 | @cache.each { |k, v| @obj.send k, v } 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /example/consumer/db/cache_schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveRecord::Schema[7.2].define(version: 1) do 4 | create_table "solid_cache_entries", force: :cascade do |t| 5 | t.binary "key", limit: 1024, null: false 6 | t.binary "value", limit: 536870912, null: false 7 | t.datetime "created_at", null: false 8 | t.integer "key_hash", limit: 8, null: false 9 | t.integer "byte_size", limit: 4, null: false 10 | t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size" 11 | t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size" 12 | t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /example/provider/db/cache_schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveRecord::Schema[7.2].define(version: 1) do 4 | create_table "solid_cache_entries", force: :cascade do |t| 5 | t.binary "key", limit: 1024, null: false 6 | t.binary "value", limit: 536870912, null: false 7 | t.datetime "created_at", null: false 8 | t.integer "key_hash", limit: 8, null: false 9 | t.integer "byte_size", limit: 4, null: false 10 | t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size" 11 | t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size" 12 | t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/callbacks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_support/core_ext/array/wrap" 4 | 5 | module ActiveResource 6 | module Callbacks 7 | extend ActiveSupport::Concern 8 | 9 | CALLBACKS = [ 10 | :before_validation, :after_validation, :before_save, :around_save, :after_save, 11 | :before_create, :around_create, :after_create, :before_update, :around_update, 12 | :after_update, :before_destroy, :around_destroy, :after_destroy 13 | ] 14 | 15 | included do 16 | extend ActiveModel::Callbacks 17 | include ActiveModel::Validations::Callbacks 18 | 19 | define_model_callbacks :save, :create, :update, :destroy 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /example/consumer/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /example/provider/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/active_job_serializer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource 4 | class ActiveJobSerializer < ActiveJob::Serializers::ObjectSerializer 5 | def serialize(resource) 6 | super( 7 | "class" => resource.class.name, 8 | "persisted" => resource.persisted?, 9 | "prefix_options" => resource.prefix_options.as_json, 10 | "attributes" => resource.attributes.as_json 11 | ) 12 | end 13 | 14 | def deserialize(hash) 15 | hash["class"].constantize.new(hash["attributes"]).tap do |resource| 16 | resource.persisted = hash["persisted"] 17 | resource.prefix_options = hash["prefix_options"] 18 | end 19 | end 20 | 21 | private 22 | def klass 23 | ActiveResource::Base 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/inheriting_hash_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class InheritingHashTest < ActiveSupport::TestCase 4 | def setup 5 | @parent = ActiveResource::InheritingHash.new({ override_me: "foo", parent_key: "parent_value" }) 6 | @child = ActiveResource::InheritingHash.new(@parent) 7 | @child[:override_me] = "bar" 8 | @child[:child_only] = "baz" 9 | end 10 | 11 | def test_child_key_overrides_parent_key 12 | assert_equal "bar", @child[:override_me] 13 | end 14 | 15 | def test_parent_key_available_on_lookup 16 | assert_equal "parent_value", @child[:parent_key] 17 | end 18 | 19 | def test_conversion_to_regular_hash_includes_parent_keys 20 | hash = @child.to_hash 21 | 22 | assert_equal 3, hash.keys.length 23 | assert_equal "parent_value", hash[:parent_key] 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /bin/tapioca: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'tapioca' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("tapioca", "tapioca") 28 | -------------------------------------------------------------------------------- /example/consumer/bin/kamal: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'kamal' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("kamal", "kamal") 28 | -------------------------------------------------------------------------------- /example/provider/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # Temporary files generated by your text editor or operating system 4 | # belong in git's global ignore instead: 5 | # `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore` 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all environment files. 11 | /.env* 12 | 13 | # Ignore all logfiles and tempfiles. 14 | /log/* 15 | /tmp/* 16 | !/log/.keep 17 | !/tmp/.keep 18 | 19 | # Ignore pidfiles, but keep the directory. 20 | /tmp/pids/* 21 | !/tmp/pids/ 22 | !/tmp/pids/.keep 23 | 24 | # Ignore storage (uploaded files in development and any SQLite databases). 25 | /storage/* 26 | !/storage/.keep 27 | /tmp/storage/* 28 | !/tmp/storage/ 29 | !/tmp/storage/.keep 30 | 31 | # Ignore master key for decrypting credentials and more. 32 | /config/master.key 33 | -------------------------------------------------------------------------------- /example/provider/bin/kamal: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'kamal' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("kamal", "kamal") 28 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/railtie.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "../active_resource" 4 | require "rails" 5 | 6 | module ActiveResource 7 | class Railtie < Rails::Railtie 8 | config.active_resource = ActiveSupport::OrderedOptions.new 9 | 10 | initializer "active_resource.set_configs" do |app| 11 | ActiveSupport.on_load(:active_resource) do 12 | app.config.active_resource.each do |k, v| 13 | send "#{k}=", v 14 | end 15 | end 16 | end 17 | 18 | initializer "active_resource.add_active_job_serializer" do |app| 19 | if app.config.try(:active_job).try(:custom_serializers) 20 | require_relative "../active_resource/active_job_serializer" 21 | app.config.active_job.custom_serializers << ActiveResource::ActiveJobSerializer 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/inheriting_hash.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource 4 | class InheritingHash < Hash 5 | def initialize(parent_hash = {}) 6 | # Default hash value must be nil, which allows fallback lookup on parent hash 7 | super(nil) 8 | @parent_hash = parent_hash 9 | end 10 | 11 | def [](key) 12 | super || @parent_hash[key] 13 | end 14 | 15 | # Merges the flattened parent hash (if it's an InheritingHash) 16 | # with ourself 17 | def to_hash 18 | @parent_hash.to_hash.merge(self) 19 | end 20 | 21 | # So we can see the merged object in IRB or the Rails console 22 | def pretty_print(pp) 23 | pp.pp_hash to_hash 24 | end 25 | 26 | def inspect 27 | to_hash.inspect 28 | end 29 | 30 | def to_s 31 | inspect 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/formats.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource 4 | module Formats 5 | autoload :XmlFormat, "#{__dir__}/formats/xml_format.rb" 6 | autoload :JsonFormat, "#{__dir__}/formats/json_format.rb" 7 | 8 | # Lookup the format class from a mime type reference symbol. Example: 9 | # 10 | # ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat 11 | # ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat 12 | def self.[](mime_type_reference) 13 | ActiveResource::Formats.const_get(ActiveSupport::Inflector.camelize(mime_type_reference.to_s) + "Format") 14 | end 15 | 16 | def self.remove_root(data) 17 | if data.is_a?(Hash) && data.keys.size == 1 && data.values.first.is_a?(Enumerable) 18 | data.values.first 19 | else 20 | data 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /example/consumer/.dockerignore: -------------------------------------------------------------------------------- 1 | # See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. 2 | 3 | # Ignore git directory. 4 | /.git/ 5 | /.gitignore 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all environment files. 11 | /.env* 12 | 13 | # Ignore all default key files. 14 | /config/master.key 15 | /config/credentials/*.key 16 | 17 | # Ignore all logfiles and tempfiles. 18 | /log/* 19 | /tmp/* 20 | !/log/.keep 21 | !/tmp/.keep 22 | 23 | # Ignore pidfiles, but keep the directory. 24 | /tmp/pids/* 25 | !/tmp/pids/.keep 26 | 27 | # Ignore storage (uploaded files in development and any SQLite databases). 28 | /storage/* 29 | !/storage/.keep 30 | /tmp/storage/* 31 | !/tmp/storage/.keep 32 | 33 | # Ignore CI service files. 34 | /.github 35 | 36 | # Ignore development files 37 | /.devcontainer 38 | 39 | # Ignore Docker-related files 40 | /.dockerignore 41 | /Dockerfile* 42 | -------------------------------------------------------------------------------- /example/provider/.dockerignore: -------------------------------------------------------------------------------- 1 | # See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. 2 | 3 | # Ignore git directory. 4 | /.git/ 5 | /.gitignore 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all environment files. 11 | /.env* 12 | 13 | # Ignore all default key files. 14 | /config/master.key 15 | /config/credentials/*.key 16 | 17 | # Ignore all logfiles and tempfiles. 18 | /log/* 19 | /tmp/* 20 | !/log/.keep 21 | !/tmp/.keep 22 | 23 | # Ignore pidfiles, but keep the directory. 24 | /tmp/pids/* 25 | !/tmp/pids/.keep 26 | 27 | # Ignore storage (uploaded files in development and any SQLite databases). 28 | /storage/* 29 | !/storage/.keep 30 | /tmp/storage/* 31 | !/tmp/storage/.keep 32 | 33 | # Ignore CI service files. 34 | /.github 35 | 36 | # Ignore development files 37 | /.devcontainer 38 | 39 | # Ignore Docker-related files 40 | /.dockerignore 41 | /Dockerfile* 42 | -------------------------------------------------------------------------------- /example/consumer/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # Temporary files generated by your text editor or operating system 4 | # belong in git's global ignore instead: 5 | # `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore` 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all environment files. 11 | /.env* 12 | 13 | # Ignore all logfiles and tempfiles. 14 | /log/* 15 | /tmp/* 16 | !/log/.keep 17 | !/tmp/.keep 18 | 19 | # Ignore pidfiles, but keep the directory. 20 | /tmp/pids/* 21 | !/tmp/pids/ 22 | !/tmp/pids/.keep 23 | 24 | # Ignore storage (uploaded files in development and any SQLite databases). 25 | /storage/* 26 | !/storage/.keep 27 | /tmp/storage/* 28 | !/tmp/storage/ 29 | !/tmp/storage/.keep 30 | 31 | /tmp/cache/* 32 | !/tmp/cache/ 33 | !/tmp/cache/.keep 34 | 35 | # Ignore master key for decrypting credentials and more. 36 | /config/master.key 37 | -------------------------------------------------------------------------------- /spec/active_cached_resource/logger_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "stringio" 3 | 4 | RSpec.describe ActiveCachedResource::Logger do 5 | let(:model_name) { "TestModel" } 6 | let(:output) { StringIO.new } 7 | let(:logger) { described_class.new(model_name) } 8 | 9 | before do 10 | allow($stdout).to receive(:write).and_wrap_original do |original, message| 11 | output.write(message) 12 | end 13 | end 14 | 15 | described_class::COLORS.excluding(:reset).each do |severity, color_code| 16 | it "logs debug messages with color" do 17 | logger.public_send(severity, "This is a message") 18 | output.rewind 19 | log_output = output.read 20 | 21 | expect(log_output).to include(color_code) 22 | expect(log_output).to include("#{severity.upcase} [CACHE][ACR][#{model_name}] This is a message") 23 | expect(log_output).to include(described_class::COLORS[:reset]) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/associations/builder/has_one_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | 5 | require "fixtures/product" 6 | require "fixtures/inventory" 7 | 8 | class ActiveResource::Associations::Builder::HasOneTest < ActiveSupport::TestCase 9 | def setup 10 | @klass = ActiveResource::Associations::Builder::HasOne 11 | end 12 | 13 | def test_validations_for_instance 14 | object = @klass.new(Product, :inventory, {}) 15 | assert_equal({}, object.send(:validate_options)) 16 | end 17 | 18 | def test_instance_build 19 | object = @klass.new(Product, :inventory, {}) 20 | Product.expects(:defines_has_one_finder_method).with(kind_of(ActiveResource::Reflection::AssociationReflection)) 21 | 22 | reflection = object.build 23 | 24 | assert_kind_of ActiveResource::Reflection::AssociationReflection, reflection 25 | assert_equal :inventory, reflection.name 26 | assert_equal Inventory, reflection.klass 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler" 4 | require "bundler/gem_tasks" 5 | 6 | if (Bundler.definition.groups - Bundler.settings[:without] + Bundler.settings[:with]).include?(:test) 7 | require "rake/testtask" 8 | require "rspec/core/rake_task" 9 | require "rubocop/rake_task" 10 | 11 | RSpec::Core::RakeTask.new(:spec) do |r| 12 | r.rspec_opts = ["--no-color"] 13 | r.verbose = false 14 | end 15 | 16 | RuboCop::RakeTask.new(:rubocop) do |r| 17 | r.options = ["--no-color", "--format", "simple"] 18 | r.verbose = false 19 | end 20 | 21 | # ActiveResource tests 22 | Rake::TestTask.new(:active_resource_test) do |t| 23 | t.libs = ["lib/activeresource/test"] 24 | t.pattern = "lib/activeresource/test/**/*_test.rb" 25 | t.warning = false 26 | t.verbose = false 27 | end 28 | 29 | desc "Type check" 30 | task :tc do 31 | sh "bundle exec srb tc" 32 | end 33 | 34 | task default: [:tc, :rubocop, :spec, :active_resource_test] 35 | end 36 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/associations/builder/association.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource::Associations::Builder 4 | class Association # :nodoc: 5 | # providing a Class-Variable, which will have a different store of subclasses 6 | class_attribute :valid_options 7 | self.valid_options = [:class_name, :foreign_key] 8 | 9 | # would identify subclasses of association 10 | class_attribute :macro 11 | 12 | attr_reader :model, :name, :options, :klass 13 | 14 | def self.build(model, name, options) 15 | new(model, name, options).build 16 | end 17 | 18 | def initialize(model, name, options) 19 | @model, @name, @options = model, name, options 20 | end 21 | 22 | def build 23 | validate_options 24 | model.create_reflection(self.class.macro, name, options) 25 | end 26 | 27 | private 28 | def validate_options 29 | options.assert_valid_keys(self.class.valid_options) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/associations/builder/has_many_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | 5 | require "fixtures/person" 6 | require "fixtures/street_address" 7 | 8 | class ActiveResource::Associations::Builder::HasManyTest < ActiveSupport::TestCase 9 | def setup 10 | @klass = ActiveResource::Associations::Builder::HasMany 11 | end 12 | 13 | def test_validations_for_instance 14 | object = @klass.new(Person, :street_address, {}) 15 | assert_equal({}, object.send(:validate_options)) 16 | end 17 | 18 | def test_instance_build 19 | object = @klass.new(Person, :street_address, {}) 20 | Person.expects(:defines_has_many_finder_method).with(kind_of(ActiveResource::Reflection::AssociationReflection)) 21 | 22 | reflection = object.build 23 | 24 | assert_kind_of ActiveResource::Reflection::AssociationReflection, reflection 25 | assert_equal :street_address, reflection.name 26 | assert_equal StreetAddress, reflection.klass 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/generators/active_cached_resource/active_record_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/active_record" 2 | 3 | module ActiveCachedResource 4 | module Generators 5 | class ActiveRecordGenerator < ::Rails::Generators::Base 6 | include ::Rails::Generators::Migration 7 | desc "Generates migration for cache tables" 8 | 9 | source_paths << File.join(File.dirname(__FILE__), "templates") 10 | 11 | def self.next_migration_number(dirname) 12 | ::ActiveRecord::Generators::Base.next_migration_number(dirname) 13 | end 14 | 15 | def self.migration_version 16 | "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" 17 | end 18 | 19 | def create_migration_file 20 | options = { 21 | migration_version: migration_version 22 | } 23 | migration_template "migration.erb", "db/migrate/create_active_cached_resource_table.rb", options 24 | end 25 | 26 | def migration_version 27 | self.class.migration_version 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "dotenv", "~> 3.1", ">= 3.1.7" 6 | gem "rake", "~> 13.2", ">= 13.2.1" 7 | 8 | group :versioning do 9 | gem "bump", "~> 0.10.0" 10 | end 11 | 12 | group :development, :test do 13 | gem "activejob", ">= 6.0" 14 | gem "activerecord", ">= 6.0" 15 | gem "pry-byebug", "~> 3.10", ">= 3.10.1" 16 | gem "rails", ">= 6.0" 17 | gem "rexml", "~> 3.4" 18 | gem "rubocop-md", "~> 1.2", ">= 1.2.4" 19 | gem "rubocop-minitest", "~> 0.36.0" 20 | gem "rubocop-packaging", "~> 0.5.2" 21 | gem "rubocop-rails", "~> 2.27" 22 | gem "sorbet-static-and-runtime", "~> 0.5.11609", require: false 23 | gem "sqlite3", "~> 2.3", ">= 2.3.1" 24 | gem "standard", "~> 1.40" 25 | gem "tapioca", "~> 0.16.3", require: false 26 | end 27 | 28 | group :test do 29 | gem "minitest-reporters", "~> 1.7", ">= 1.7.1" 30 | gem "mocha", ">= 0.13.0" 31 | gem "rspec_junit_formatter", "~> 0.6.0" 32 | gem "rspec", "~> 3.13" 33 | gem "simplecov", "~> 0.22.0", require: false 34 | end 35 | 36 | gemspec 37 | -------------------------------------------------------------------------------- /example/consumer/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization and 2 | # are automatically loaded by Rails. If you want to use locales other than 3 | # English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t "hello" 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t("hello") %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more about the API, please read the Rails Internationalization guide 20 | # at https://guides.rubyonrails.org/i18n.html. 21 | # 22 | # Be aware that YAML interprets the following case-insensitive strings as 23 | # booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings 24 | # must be quoted to be interpreted as strings. For example: 25 | # 26 | # en: 27 | # "yes": yup 28 | # enabled: "ON" 29 | 30 | en: 31 | hello: "Hello world" 32 | -------------------------------------------------------------------------------- /example/provider/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization and 2 | # are automatically loaded by Rails. If you want to use locales other than 3 | # English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t "hello" 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t("hello") %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more about the API, please read the Rails Internationalization guide 20 | # at https://guides.rubyonrails.org/i18n.html. 21 | # 22 | # Be aware that YAML interprets the following case-insensitive strings as 23 | # booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings 24 | # must be quoted to be interpreted as strings. For example: 25 | # 26 | # en: 27 | # "yes": yup 28 | # enabled: "ON" 29 | 30 | en: 31 | hello: "Hello world" 32 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/log_subscriber.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource 4 | class LogSubscriber < ActiveSupport::LogSubscriber 5 | def request(event) 6 | result = event.payload[:result] 7 | 8 | # When result is nil, the connection could not even be initiated 9 | # with the server, so we log an internal synthetic error response (523). 10 | code = result.try(:code) || 523 # matches CloudFlare's convention 11 | message = result.try(:message) || "ActiveResource connection error" 12 | body = result.try(:body) || "" 13 | 14 | log_level_method = code.to_i < 400 ? :info : :error 15 | 16 | send log_level_method, "#{event.payload[:method].to_s.upcase} #{event.payload[:request_uri]}" 17 | send log_level_method, "--> %d %s %d (%.1fms)" % [code, message, body.to_s.length, event.duration] 18 | end 19 | 20 | def logger 21 | ActiveResource::Base.logger 22 | end 23 | end 24 | end 25 | 26 | ActiveResource::LogSubscriber.attach_to :active_resource 27 | -------------------------------------------------------------------------------- /example/consumer/.kamal/secrets: -------------------------------------------------------------------------------- 1 | # Secrets defined here are available for reference under registry/password, env/secret, builder/secrets, 2 | # and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either 3 | # password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git. 4 | 5 | # Example of extracting secrets from 1password (or another compatible pw manager) 6 | # SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY) 7 | # KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS}) 8 | # RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS}) 9 | 10 | # Use a GITHUB_TOKEN if private repositories are needed for the image 11 | # GITHUB_TOKEN=$(gh config get -h github.com oauth_token) 12 | 13 | # Grab the registry password from ENV 14 | KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD 15 | 16 | # Improve security by using a password manager. Never check config/master.key into git! 17 | RAILS_MASTER_KEY=$(cat config/master.key) 18 | -------------------------------------------------------------------------------- /example/provider/.kamal/secrets: -------------------------------------------------------------------------------- 1 | # Secrets defined here are available for reference under registry/password, env/secret, builder/secrets, 2 | # and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either 3 | # password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git. 4 | 5 | # Example of extracting secrets from 1password (or another compatible pw manager) 6 | # SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY) 7 | # KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS}) 8 | # RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS}) 9 | 10 | # Use a GITHUB_TOKEN if private repositories are needed for the image 11 | # GITHUB_TOKEN=$(gh config get -h github.com oauth_token) 12 | 13 | # Grab the registry password from ENV 14 | KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD 15 | 16 | # Improve security by using a password manager. Never check config/master.key into git! 17 | RAILS_MASTER_KEY=$(cat config/master.key) 18 | -------------------------------------------------------------------------------- /lib/active_cached_resource/caching_strategies/sql_cache.rb: -------------------------------------------------------------------------------- 1 | require_relative "base" 2 | 3 | module ActiveCachedResource 4 | module CachingStrategies 5 | class SQLCache < Base 6 | def initialize(model, options = {}) 7 | super() 8 | @model = model 9 | @batch_clear_size = options.fetch(:batch_clear_size, 1000) 10 | end 11 | 12 | protected 13 | 14 | def delete_raw(key) 15 | @model.where(key: key).delete_all 16 | end 17 | 18 | def read_raw(key) 19 | record = @model.where(key: key).where(@model.arel_table[:expires_at].gt(Time.current)).first 20 | record&.value 21 | end 22 | 23 | def write_raw(key, value, options = {}) 24 | expires_at = Time.current + options.fetch(:expires_in) 25 | 26 | @model.create({key: key, value: value, expires_at: expires_at}) 27 | end 28 | 29 | def clear_raw(pattern) 30 | @model.where(@model.arel_table[:key].matches("#{pattern}%")).in_batches(of: @batch_clear_size) do |batch| 31 | batch.delete_all 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] [Jean Luis Urena] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | 3 | set -e 4 | 5 | export BUNDLE_SILENCE_ROOT_WARNING=1 6 | export BUNDLE_IGNORE_MESSAGES=1 7 | 8 | . "$ASDF_DIR/asdf.sh" 9 | SPEC_RESULTS="coverage/spec_results" 10 | mkdir -p "$SPEC_RESULTS" 11 | 12 | # Ruby versions to test 13 | LINTER_RUBY_VERSION=$(asdf latest ruby 3.2) 14 | RUBY_VERSIONS=$(asdf list ruby) 15 | # Maximum number of concurrent processes 16 | MAX_PARALLEL=3 17 | CURRENT_PARALLEL=0 18 | 19 | run_rspec() { 20 | local ruby_version="$1" 21 | 22 | printf "\n********* Testing with Ruby $ruby_version *********\n\n" 23 | 24 | bundle install --quiet --no-cache 25 | # Run tests 26 | bundle exec rake 27 | } 28 | 29 | # Iterate over Ruby versions 30 | for RUBY_VERSION in $RUBY_VERSIONS; do 31 | ( 32 | asdf reshim ruby $RUBY_VERSION 33 | asdf shell ruby $RUBY_VERSION 34 | 35 | run_rspec "$RUBY_VERSION" "$RAILS_VERSION" 36 | ) & 37 | 38 | CURRENT_PARALLEL=$((CURRENT_PARALLEL + 1)) 39 | while [ $CURRENT_PARALLEL -ge $MAX_PARALLEL ]; do 40 | sleep 1 41 | CURRENT_PARALLEL=$(jobs -r | wc -l) 42 | done 43 | done 44 | 45 | wait 46 | 47 | printf "\n********* DONE *********\n" -------------------------------------------------------------------------------- /example/consumer/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | APP_ROOT = File.expand_path("..", __dir__) 5 | 6 | def system!(*args) 7 | system(*args, exception: true) 8 | end 9 | 10 | FileUtils.chdir APP_ROOT do 11 | # This script is a way to set up or update your development environment automatically. 12 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 13 | # Add necessary setup steps to this file. 14 | 15 | puts "== Installing dependencies ==" 16 | system("bundle check") || system!("bundle install") 17 | 18 | # puts "\n== Copying sample files ==" 19 | # unless File.exist?("config/database.yml") 20 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 21 | # end 22 | 23 | puts "\n== Preparing database ==" 24 | system! "bin/rails db:prepare" 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! "bin/rails log:clear tmp:clear" 28 | 29 | unless ARGV.include?("--skip-server") 30 | puts "\n== Starting development server ==" 31 | STDOUT.flush # flush the output before exec(2) so that it displays 32 | exec "bin/dev" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /example/provider/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | APP_ROOT = File.expand_path("..", __dir__) 5 | 6 | def system!(*args) 7 | system(*args, exception: true) 8 | end 9 | 10 | FileUtils.chdir APP_ROOT do 11 | # This script is a way to set up or update your development environment automatically. 12 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 13 | # Add necessary setup steps to this file. 14 | 15 | puts "== Installing dependencies ==" 16 | system("bundle check") || system!("bundle install") 17 | 18 | # puts "\n== Copying sample files ==" 19 | # unless File.exist?("config/database.yml") 20 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 21 | # end 22 | 23 | puts "\n== Preparing database ==" 24 | system! "bin/rails db:prepare" 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! "bin/rails log:clear tmp:clear" 28 | 29 | unless ARGV.include?("--skip-server") 30 | puts "\n== Starting development server ==" 31 | STDOUT.flush # flush the output before exec(2) so that it displays 32 | exec "bin/dev" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/activeresource/test/fixtures/fixtures.rbi: -------------------------------------------------------------------------------- 1 | # typed: strong 2 | 3 | class Customer 4 | class Friend 5 | class Brother 6 | class Child; end 7 | end 8 | end 9 | end 10 | 11 | class Person 12 | class Books 13 | class UnnamedResource; end 14 | end 15 | 16 | class Address; end 17 | class Street 18 | class State 19 | class NotableRiver; end 20 | end 21 | end 22 | end 23 | 24 | # Defining specific instances based on the structure 25 | class Luis < Customer; end 26 | 27 | class JK < Customer::Friend; end 28 | 29 | class Mateo < Customer::Friend::Brother; end 30 | class Felipe < Customer::Friend::Brother; end 31 | 32 | class Edith < Customer::Friend::Brother::Child; end 33 | class Martha < Customer::Friend::Brother::Child; end 34 | 35 | class Bryan < Customer::Friend::Brother::Child; end 36 | class Luke < Customer::Friend::Brother::Child; end 37 | 38 | class Eduardo < Customer::Friend; end 39 | 40 | class Sebas < Customer::Friend::Brother; end 41 | class Elsa < Customer::Friend::Brother; end 42 | class Milena < Customer::Friend::Brother; end 43 | 44 | class Andres < Customer::Friend::Brother::Child; end 45 | class Jorge < Customer::Friend::Brother::Child; end 46 | class Natacha < Customer::Friend::Brother::Child; end 47 | -------------------------------------------------------------------------------- /example/consumer/.kamal/hooks/pre-connect.sample: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # A sample pre-connect check 4 | # 5 | # Warms DNS before connecting to hosts in parallel 6 | # 7 | # These environment variables are available: 8 | # KAMAL_RECORDED_AT 9 | # KAMAL_PERFORMER 10 | # KAMAL_VERSION 11 | # KAMAL_HOSTS 12 | # KAMAL_ROLE (if set) 13 | # KAMAL_DESTINATION (if set) 14 | # KAMAL_RUNTIME 15 | 16 | hosts = ENV["KAMAL_HOSTS"].split(",") 17 | results = nil 18 | max = 3 19 | 20 | elapsed = Benchmark.realtime do 21 | results = hosts.map do |host| 22 | Thread.new do 23 | tries = 1 24 | 25 | begin 26 | Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) 27 | rescue SocketError 28 | if tries < max 29 | puts "Retrying DNS warmup: #{host}" 30 | tries += 1 31 | sleep rand 32 | retry 33 | else 34 | puts "DNS warmup failed: #{host}" 35 | host 36 | end 37 | end 38 | 39 | tries 40 | end 41 | end.map(&:value) 42 | end 43 | 44 | retries = results.sum - hosts.size 45 | nopes = results.count { |r| r == max } 46 | 47 | puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ] 48 | -------------------------------------------------------------------------------- /example/provider/.kamal/hooks/pre-connect.sample: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # A sample pre-connect check 4 | # 5 | # Warms DNS before connecting to hosts in parallel 6 | # 7 | # These environment variables are available: 8 | # KAMAL_RECORDED_AT 9 | # KAMAL_PERFORMER 10 | # KAMAL_VERSION 11 | # KAMAL_HOSTS 12 | # KAMAL_ROLE (if set) 13 | # KAMAL_DESTINATION (if set) 14 | # KAMAL_RUNTIME 15 | 16 | hosts = ENV["KAMAL_HOSTS"].split(",") 17 | results = nil 18 | max = 3 19 | 20 | elapsed = Benchmark.realtime do 21 | results = hosts.map do |host| 22 | Thread.new do 23 | tries = 1 24 | 25 | begin 26 | Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) 27 | rescue SocketError 28 | if tries < max 29 | puts "Retrying DNS warmup: #{host}" 30 | tries += 1 31 | sleep rand 32 | retry 33 | else 34 | puts "DNS warmup failed: #{host}" 35 | host 36 | end 37 | end 38 | 39 | tries 40 | end 41 | end.map(&:value) 42 | end 43 | 44 | retries = results.sum - hosts.size 45 | nopes = results.count { |r| r == max } 46 | 47 | puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ] 48 | -------------------------------------------------------------------------------- /lib/active_cached_resource/logger.rb: -------------------------------------------------------------------------------- 1 | require "logger" 2 | 3 | module ActiveCachedResource 4 | class Logger < ::Logger 5 | # @!constant COLORS 6 | # @return [Hash] A hash that maps log levels to their corresponding ANSI color codes. 7 | # @example 8 | # COLORS[:debug] # => "\e[36m" (Blue) 9 | # COLORS[:info] # => "\e[0m" (Default) 10 | # COLORS[:warn] # => "\e[33m" (Yellow) 11 | # COLORS[:error] # => "\e[31m" (Red) 12 | # COLORS[:fatal] # => "\e[31m" (Red) 13 | # COLORS[:reset] # => "\e[0m" (Reset) 14 | COLORS = { 15 | debug: "\e[36m", # Blue 16 | info: "\e[0m", # Default 17 | warn: "\e[33m", # Yellow 18 | error: "\e[31m", # Red 19 | fatal: "\e[31m", # Red 20 | reset: "\e[0m" # Reset 21 | } 22 | 23 | # Initializes a new logger instance for the specified model. 24 | # 25 | # @param model_name [String] the name of the model to be logged 26 | # 27 | # @return [void] 28 | def initialize(model_name) 29 | super($stdout) 30 | @model_name = model_name 31 | self.formatter = proc do |severity, datetime, _progname, msg| 32 | "#{COLORS[severity.downcase.to_sym]}#{datetime} -- #{severity} [CACHE][ACR][#{model_name}] #{msg}#{COLORS[:reset]}\n" 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /example/consumer/.kamal/hooks/pre-build.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A sample pre-build hook 4 | # 5 | # Checks: 6 | # 1. We have a clean checkout 7 | # 2. A remote is configured 8 | # 3. The branch has been pushed to the remote 9 | # 4. The version we are deploying matches the remote 10 | # 11 | # These environment variables are available: 12 | # KAMAL_RECORDED_AT 13 | # KAMAL_PERFORMER 14 | # KAMAL_VERSION 15 | # KAMAL_HOSTS 16 | # KAMAL_ROLE (if set) 17 | # KAMAL_DESTINATION (if set) 18 | 19 | if [ -n "$(git status --porcelain)" ]; then 20 | echo "Git checkout is not clean, aborting..." >&2 21 | git status --porcelain >&2 22 | exit 1 23 | fi 24 | 25 | first_remote=$(git remote) 26 | 27 | if [ -z "$first_remote" ]; then 28 | echo "No git remote set, aborting..." >&2 29 | exit 1 30 | fi 31 | 32 | current_branch=$(git branch --show-current) 33 | 34 | if [ -z "$current_branch" ]; then 35 | echo "Not on a git branch, aborting..." >&2 36 | exit 1 37 | fi 38 | 39 | remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1) 40 | 41 | if [ -z "$remote_head" ]; then 42 | echo "Branch not pushed to remote, aborting..." >&2 43 | exit 1 44 | fi 45 | 46 | if [ "$KAMAL_VERSION" != "$remote_head" ]; then 47 | echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2 48 | exit 1 49 | fi 50 | 51 | exit 0 52 | -------------------------------------------------------------------------------- /example/provider/.kamal/hooks/pre-build.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A sample pre-build hook 4 | # 5 | # Checks: 6 | # 1. We have a clean checkout 7 | # 2. A remote is configured 8 | # 3. The branch has been pushed to the remote 9 | # 4. The version we are deploying matches the remote 10 | # 11 | # These environment variables are available: 12 | # KAMAL_RECORDED_AT 13 | # KAMAL_PERFORMER 14 | # KAMAL_VERSION 15 | # KAMAL_HOSTS 16 | # KAMAL_ROLE (if set) 17 | # KAMAL_DESTINATION (if set) 18 | 19 | if [ -n "$(git status --porcelain)" ]; then 20 | echo "Git checkout is not clean, aborting..." >&2 21 | git status --porcelain >&2 22 | exit 1 23 | fi 24 | 25 | first_remote=$(git remote) 26 | 27 | if [ -z "$first_remote" ]; then 28 | echo "No git remote set, aborting..." >&2 29 | exit 1 30 | fi 31 | 32 | current_branch=$(git branch --show-current) 33 | 34 | if [ -z "$current_branch" ]; then 35 | echo "Not on a git branch, aborting..." >&2 36 | exit 1 37 | fi 38 | 39 | remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1) 40 | 41 | if [ -z "$remote_head" ]; then 42 | echo "Branch not pushed to remote, aborting..." >&2 43 | exit 1 44 | fi 45 | 46 | if [ "$KAMAL_VERSION" != "$remote_head" ]; then 47 | echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2 48 | exit 1 49 | fi 50 | 51 | exit 0 52 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/associations/builder/belongs_to_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | 5 | require "fixtures/person" 6 | require "fixtures/beast" 7 | require "fixtures/customer" 8 | 9 | 10 | class ActiveResource::Associations::Builder::BelongsToTest < ActiveSupport::TestCase 11 | def setup 12 | @klass = ActiveResource::Associations::Builder::BelongsTo 13 | end 14 | 15 | 16 | def test_validations_for_instance 17 | object = @klass.new(Person, :customer, {}) 18 | assert_equal({}, object.send(:validate_options)) 19 | end 20 | 21 | def test_instance_build 22 | object = @klass.new(Person, :customer, {}) 23 | Person.expects(:defines_belongs_to_finder_method).with(kind_of(ActiveResource::Reflection::AssociationReflection)) 24 | 25 | reflection = object.build 26 | 27 | assert_kind_of ActiveResource::Reflection::AssociationReflection, reflection 28 | assert_equal :customer, reflection.name 29 | assert_equal Customer, reflection.klass 30 | assert_equal "customer_id", reflection.foreign_key 31 | end 32 | 33 | 34 | def test_valid_options 35 | assert @klass.build(Person, :customer, class_name: "Person") 36 | assert @klass.build(Person, :customer, foreign_key: "person_id") 37 | 38 | assert_raise ArgumentError do 39 | @klass.build(Person, :customer, soo_invalid: true) 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "dotenv/load" 4 | require "simplecov" 5 | 6 | Dir.glob(File.join(__dir__, "support", "**", "*.rb")).each { |f| require_relative f } 7 | 8 | SimpleCov.start do 9 | add_filter "/spec/" 10 | add_filter "/example/" 11 | add_filter "/lib/active_resource/" 12 | end 13 | require "active_cached_resource" 14 | require "active_record" 15 | require "sqlite3" 16 | 17 | ActiveRecord::Schema.verbose = false 18 | 19 | RSpec.configure do |config| 20 | include ActiveSupport::LoggerSilence 21 | # Enable flags like --only-failures and --next-failure 22 | config.example_status_persistence_file_path = ".rspec_status" 23 | 24 | # Disable RSpec exposing methods globally on `Module` and `main` 25 | config.disable_monkey_patching! 26 | 27 | config.expect_with :rspec do |c| 28 | c.syntax = :expect 29 | end 30 | 31 | config.before(:suite) do 32 | ActiveSupport::LoggerSilence.silence do 33 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 34 | 35 | ActiveRecord::Schema.define do 36 | create_table :active_cached_resources, force: true do |t| 37 | t.string :key, null: false 38 | t.binary :value, null: false 39 | t.datetime :expires_at, null: false 40 | 41 | t.index [:key, :expires_at], unique: true, name: "index_active_cached_resources_on_key_and_expires_at" 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | workflow_call: 4 | concurrency: 5 | group: ${{ github.ref }}-${{ github.workflow }}-lint 6 | cancel-in-progress: true 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Get changed files 14 | id: changed-files 15 | uses: tj-actions/changed-files@v46 16 | with: 17 | files: | 18 | **/*.{rb,ru,rake,gemspec} 19 | Gemfile 20 | active_cached_resource.gemspec 21 | files_ignore: | 22 | .bundle/**/* 23 | .git/**/* 24 | sorbet/**/* 25 | Gemfile.lock 26 | tmp/**/* 27 | vendor/**/* 28 | lib/activeresource/**/* 29 | separator: " " 30 | - name: Set up Ruby 31 | if: steps.changed-files.outputs.any_changed == 'true' 32 | uses: ruby/setup-ruby@v1.197.0 33 | with: 34 | ruby-version: 3.2.2 35 | bundler-cache: true 36 | - name: Type Check 37 | if: steps.changed-files.outputs.any_changed == 'true' 38 | run: bundle exec srb tc 39 | - name: Lint Code 40 | if: steps.changed-files.outputs.any_changed == 'true' 41 | uses: wearerequired/lint-action@v2.3.0 42 | with: 43 | auto_fix: true 44 | rubocop: true 45 | rubocop_command_prefix: bundle exec 46 | rubocop_args: --parallel ${{ steps.changed-files.outputs.all_changed_files }} -------------------------------------------------------------------------------- /lib/active_cached_resource/caching_strategies/active_support_cache.rb: -------------------------------------------------------------------------------- 1 | require_relative "base" 2 | 3 | module ActiveCachedResource 4 | module CachingStrategies 5 | class ActiveSupportCache < Base 6 | def initialize(cache_store) 7 | super() 8 | @cache_store = cache_store 9 | end 10 | 11 | protected 12 | 13 | def read_raw(key) 14 | @cache_store.read(key) 15 | end 16 | 17 | def write_raw(key, compressed_value, options) 18 | successful_write = @cache_store.write(key, compressed_value, options) 19 | update_master_key(key, options) if successful_write 20 | 21 | successful_write 22 | end 23 | 24 | def delete_raw(key) 25 | @cache_store.delete(key) 26 | end 27 | 28 | def clear_raw(prefix) 29 | existing_keys = @cache_store.read(prefix) 30 | return if existing_keys.nil? 31 | 32 | existing_keys.add(prefix) 33 | @cache_store.delete_multi(existing_keys.to_a) # Redis implementation does not work with Sets 34 | end 35 | 36 | private 37 | 38 | # Updates the `master` key, which contains keys for a given prefix. 39 | def update_master_key(key, options) 40 | prefix, _ = split_key(key) 41 | 42 | existing_keys = @cache_store.read(prefix) || Set.new 43 | existing_keys.add(key) 44 | 45 | # Maintain the list of keys for twice the expiration time 46 | @cache_store.write(prefix, existing_keys, expires_in: options[:expires_in]) 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | workflow_call: 4 | concurrency: 5 | group: ${{ github.ref }}-${{ github.workflow }}-test 6 | cancel-in-progress: true 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | env: 11 | BUNDLE_WITHOUT: 'versioning:development' 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - ruby: "3.2" 17 | - ruby: "3.3" 18 | - ruby: "3.4" 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Load dotenv into workflow 22 | id: dotenv 23 | uses: falti/dotenv-action@v1.1.4 24 | with: 25 | keys-case: bypass 26 | - name: Set up Ruby 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{ matrix.ruby }} 30 | bundler-cache: true 31 | - name: Test ActiveCachedResource 32 | id: active-cached-resource-test 33 | continue-on-error: true 34 | run: | 35 | bundle exec rspec \ 36 | -f RspecJunitFormatter -o ${{ steps.dotenv.outputs.JUNIT_REPORTS_PATH }}/rspec_ruby${{ matrix.ruby }}.xml 37 | - name: Test ActiveResource 38 | continue-on-error: true 39 | id: active-resource-test 40 | run: | 41 | bundle exec rake active_resource_test 42 | - name: Publish Test Report 43 | uses: mikepenz/action-junit-report@v5.2.0 44 | with: 45 | report_paths: '${{ steps.dotenv.outputs.JUNIT_REPORTS_PATH }}/**/*.xml' 46 | check_name: 'Test Result for Ruby:${{ matrix.ruby }}' 47 | fail_on_failure: true 48 | comment: true 49 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/base/equality_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | require "fixtures/person" 5 | require "fixtures/street_address" 6 | 7 | class BaseEqualityTest < ActiveSupport::TestCase 8 | def setup 9 | @new = Person.new 10 | @one = Person.new(id: 1) 11 | @two = Person.new(id: 2) 12 | @street = StreetAddress.new(id: 2) 13 | end 14 | 15 | def test_should_equal_self 16 | assert @new == @new, "@new == @new" 17 | assert @one == @one, "@one == @one" 18 | end 19 | 20 | def test_shouldnt_equal_new_resource 21 | assert @new != @one, "@new != @one" 22 | assert @one != @new, "@one != @new" 23 | end 24 | 25 | def test_shouldnt_equal_different_class 26 | assert @two != @street, "person != street_address with same id" 27 | assert @street != @two, "street_address != person with same id" 28 | end 29 | 30 | def test_eql_should_alias_equals_operator 31 | assert_equal @new == @new, @new.eql?(@new) 32 | assert_equal @new == @one, @new.eql?(@one) 33 | 34 | assert_equal @one == @one, @one.eql?(@one) 35 | assert_equal @one == @new, @one.eql?(@new) 36 | 37 | assert_equal @one == @street, @one.eql?(@street) 38 | end 39 | 40 | def test_hash_should_be_id_hash 41 | [@new, @one, @two, @street].each do |resource| 42 | assert_equal resource.id.hash, resource.hash 43 | end 44 | end 45 | 46 | def test_with_prefix_options 47 | assert_equal @one == @one, @one.eql?(@one) 48 | assert_equal @one == @one.dup, @one.eql?(@one.dup) 49 | new_one = @one.dup 50 | new_one.prefix_options = { foo: "bar" } 51 | assert_not_equal @one, new_one 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /active_cached_resource.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/active_cached_resource/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "active_cached_resource" 7 | spec.version = ActiveCachedResource::VERSION 8 | spec.authors = ["Jean Luis Urena"] 9 | spec.email = ["eljean@live.com"] 10 | 11 | spec.summary = "ActiveResource, but with a caching layer." 12 | spec.homepage = "https://github.com/jlurena/active_cached_resource" 13 | spec.required_ruby_version = ">= 3.2.0" 14 | spec.platform = Gem::Platform::RUBY 15 | 16 | spec.metadata = { 17 | "changelog_uri" => "https://github.com/jlurena/active_cached_resource/blob/main/CHANGELOG.md", 18 | "documentation_uri" => "https://rubydoc.info/gems/active_cached_resource/", 19 | "source_code_uri" => "https://github.com/jlurena/active_cached_resource", 20 | "homepage_uri" => spec.homepage, 21 | "wiki_uri" => "https://github.com/jlurena/active_cached_resource/wiki" 22 | } 23 | 24 | spec.files = [ 25 | "README.md", 26 | "LICENSE", 27 | "CHANGELOG.md", 28 | "lib/active_cached_resource.rb", 29 | *Dir.glob("lib/active_cached_resource/**/*"), 30 | *Dir.glob("lib/activeresource/lib/**/*"), 31 | "lib/activeresource/README.md", 32 | *Dir.glob("lib/generators/**/*") 33 | ] 34 | 35 | spec.bindir = "exe" 36 | spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } 37 | spec.require_paths = ["lib"] 38 | 39 | spec.add_dependency "activemodel-serializers-xml", "~> 1.0" 40 | spec.add_dependency "activemodel", ">= 6.0" 41 | spec.add_dependency "activesupport", ">= 6.0" 42 | spec.add_dependency "ostruct", "~> 0.6.1" 43 | end 44 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/active_job_serializer_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | 5 | require "fixtures/project" 6 | require "fixtures/person" 7 | require "fixtures/product" 8 | require "active_job" 9 | require "active_job/arguments" 10 | require_relative "../../lib/active_resource/active_job_serializer" 11 | 12 | class ActiveJobSerializerTest < ActiveSupport::TestCase 13 | setup do 14 | @klass = ActiveResource::ActiveJobSerializer 15 | end 16 | 17 | def test_serialize 18 | project = Project.new(id: 1, name: "Ruby on Rails") 19 | project.prefix_options[:person_id] = 1 20 | project_json = { 21 | _aj_serialized: @klass.name, 22 | class: project.class.name, 23 | persisted: project.persisted?, 24 | prefix_options: project.prefix_options, 25 | attributes: project.attributes 26 | }.as_json 27 | serialized_json = @klass.serialize(project) 28 | 29 | assert_equal project_json, serialized_json 30 | end 31 | 32 | def test_deserialize 33 | person = Person.new(id: 2, name: "David") 34 | person.persisted = true 35 | person_json = { 36 | _aj_serialized: @klass.name, 37 | class: person.class.name, 38 | persisted: person.persisted?, 39 | prefix_options: person.prefix_options, 40 | attributes: person.attributes 41 | }.as_json 42 | deserialized_object = @klass.deserialize(person_json) 43 | 44 | assert_equal person, deserialized_object 45 | end 46 | 47 | def test_serialize? 48 | product = Product.new(id: 3, name: "Chunky Bacon") 49 | 50 | assert @klass.serialize?(product) 51 | assert_not @klass.serialize?("not a resource") 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActiveCachedResource 2 | [ActiveResource](https://github.com/rails/activeresource) but with a caching layer. 3 | 4 | ## Installation 5 | 6 | Install the gem and add to the application's Gemfile by executing: 7 | 8 | $ bundle add active_cached_resource 9 | 10 | If bundler is not being used to manage dependencies, install the gem by executing: 11 | 12 | $ gem install active_cached_resource 13 | 14 | ## Usage 15 | 16 | Check out the Wiki! https://github.com/jlurena/active_cached_resource/wiki 17 | 18 | ## Development 19 | 20 | After checking out the repo, run `bin/setup` to install dependencies. 21 | Run `rake` to run the linter and tests. 22 | You can also run `bin/console` for an interactive prompt that will allow you to experiment. 23 | 24 | Additionally, there is an `example` Rails Application you can play with. 25 | In there you'll find two small rails app: 26 | 27 | - Provider 28 | - This application contains the remote data. 29 | - Consumer 30 | - This application consumes a remote resource from Provider. 31 | 32 | 33 | ## Contributing 34 | 35 | Bug reports and pull requests are welcome on GitHub at https://github.com/jlurena/active_cached_resource. 36 | 37 | ## Inspirations & Credits 38 | - [CachedResource](https://github.com/mhgbrown/cached_resource) 39 | 40 | Major differences between this gem and `CachedResource` are: 41 | - This uses a custom, vendored version of the gem [`ActiveResource`](https://github.com/rails/activeresource) that adds the following features 42 | - Lazy `where` chaining 43 | - Flexibility to add your own caching strategies, this gem comes built in with two of them: 44 | - Caching using `ActiveSupport` 45 | - Caching using `SQL` 46 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/date@3.4.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `date` gem. 5 | # Please instead update this file by running `bin/tapioca gem date`. 6 | 7 | 8 | # source://date//lib/date.rb#6 9 | class Date 10 | include ::Comparable 11 | 12 | # call-seq: 13 | # infinite? -> false 14 | # 15 | # Returns +false+ 16 | # 17 | # @return [Boolean] 18 | # 19 | # source://date//lib/date.rb#13 20 | def infinite?; end 21 | end 22 | 23 | # source://date//lib/date.rb#17 24 | class Date::Infinity < ::Numeric 25 | # @return [Infinity] a new instance of Infinity 26 | # 27 | # source://date//lib/date.rb#19 28 | def initialize(d = T.unsafe(nil)); end 29 | 30 | # source://date//lib/date.rb#33 31 | def +@; end 32 | 33 | # source://date//lib/date.rb#32 34 | def -@; end 35 | 36 | # source://date//lib/date.rb#35 37 | def <=>(other); end 38 | 39 | # source://date//lib/date.rb#30 40 | def abs; end 41 | 42 | # source://date//lib/date.rb#51 43 | def coerce(other); end 44 | 45 | # @return [Boolean] 46 | # 47 | # source://date//lib/date.rb#26 48 | def finite?; end 49 | 50 | # @return [Boolean] 51 | # 52 | # source://date//lib/date.rb#27 53 | def infinite?; end 54 | 55 | # @return [Boolean] 56 | # 57 | # source://date//lib/date.rb#28 58 | def nan?; end 59 | 60 | # source://date//lib/date.rb#59 61 | def to_f; end 62 | 63 | # @return [Boolean] 64 | # 65 | # source://date//lib/date.rb#25 66 | def zero?; end 67 | 68 | protected 69 | 70 | # source://date//lib/date.rb#21 71 | def d; end 72 | end 73 | 74 | # source://date//lib/date.rb#7 75 | Date::VERSION = T.let(T.unsafe(nil), String) 76 | -------------------------------------------------------------------------------- /example/provider/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | # require "active_storage/engine" 9 | require "action_controller/railtie" 10 | require "action_mailer/railtie" 11 | # require "action_mailbox/engine" 12 | # require "action_text/engine" 13 | require "action_view/railtie" 14 | # require "action_cable/engine" 15 | # require "rails/test_unit/railtie" 16 | 17 | # Require the gems listed in Gemfile, including any gems 18 | # you've limited to :test, :development, or :production. 19 | Bundler.require(*Rails.groups) 20 | 21 | module Provider 22 | class Application < Rails::Application 23 | # Initialize configuration defaults for originally generated Rails version. 24 | config.load_defaults 8.0 25 | 26 | # Please, add to the `ignore` list any other `lib` subdirectories that do 27 | # not contain `.rb` files, or that should not be reloaded or eager loaded. 28 | # Common ones are `templates`, `generators`, or `middleware`, for example. 29 | config.autoload_lib(ignore: %w[assets tasks]) 30 | 31 | # Configuration for the application, engines, and railties goes here. 32 | # 33 | # These settings can be overridden in specific environments using the files 34 | # in config/environments, which are processed later. 35 | # 36 | # config.time_zone = "Central Time (US & Canada)" 37 | # config.eager_load_paths << Rails.root.join("extras") 38 | 39 | # Only loads a smaller set of middleware suitable for API only apps. 40 | # Middleware like session, flash, cookies can be added back manually. 41 | # Skip views, helpers and assets when generating a new resource. 42 | config.api_only = true 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/provider/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # This file is the source Rails uses to define your schema when running `bin/rails 6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to 7 | # be faster and is potentially less error prone than running all of your 8 | # migrations from scratch. Old migrations may fail to apply correctly if those 9 | # migrations use external dependencies or application code. 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema[8.0].define(version: 2024_12_02_184017) do 14 | create_table "addresses", force: :cascade do |t| 15 | t.string "street" 16 | t.string "city" 17 | t.string "state" 18 | t.string "zip" 19 | t.integer "person_id", null: false 20 | t.datetime "created_at", null: false 21 | t.datetime "updated_at", null: false 22 | t.index ["person_id"], name: "index_addresses_on_person_id" 23 | end 24 | 25 | create_table "companies", force: :cascade do |t| 26 | t.string "name" 27 | t.string "street" 28 | t.string "city" 29 | t.string "state" 30 | t.string "zip" 31 | t.integer "person_id", null: false 32 | t.datetime "created_at", null: false 33 | t.datetime "updated_at", null: false 34 | t.index ["person_id"], name: "index_companies_on_person_id" 35 | end 36 | 37 | create_table "people", force: :cascade do |t| 38 | t.string "first_name" 39 | t.string "last_name" 40 | t.integer "age" 41 | t.datetime "created_at", null: false 42 | t.datetime "updated_at", null: false 43 | end 44 | 45 | add_foreign_key "addresses", "people" 46 | add_foreign_key "companies", "people" 47 | end 48 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye-slim AS base 2 | 3 | ENV LANG C.UTF-8 4 | ENV LC_ALL C.UTF-8 5 | ENV ASDF_DIR=/root/.asdf 6 | 7 | # Install dependencies 8 | RUN apt-get update && apt-get install -y \ 9 | bash \ 10 | build-essential \ 11 | curl \ 12 | git \ 13 | libffi-dev \ 14 | libssl-dev \ 15 | perl \ 16 | libreadline-dev \ 17 | tzdata \ 18 | libyaml-dev \ 19 | zlib1g-dev 20 | 21 | RUN git clone https://github.com/asdf-vm/asdf.git /root/.asdf --branch v0.14.0 && \ 22 | . "$ASDF_DIR/asdf.sh" && \ 23 | asdf plugin add ruby 24 | 25 | # Ruby 3.2 26 | FROM base AS ruby-3.2 27 | RUN . "$ASDF_DIR/asdf.sh" && \ 28 | asdf install ruby $(asdf latest ruby 3.2) && \ 29 | asdf global ruby $(asdf latest ruby 3.2) && \ 30 | gem install bundler 31 | 32 | # Ruby 3.3 33 | FROM base AS ruby-3.3 34 | RUN . "$ASDF_DIR/asdf.sh" && \ 35 | asdf install ruby $(asdf latest ruby 3.3) && \ 36 | asdf global ruby $(asdf latest ruby 3.3) && \ 37 | gem install bundler 38 | 39 | # Ruby 3.4 40 | FROM base AS ruby-3.4 41 | RUN . "$ASDF_DIR/asdf.sh" && \ 42 | asdf install ruby $(asdf latest ruby 3.4) && \ 43 | asdf global ruby $(asdf latest ruby 3.4) && \ 44 | gem install bundler 45 | 46 | # Final Image with Application Code 47 | FROM base AS final 48 | 49 | # Copy and merge installed ASDF directory from ruby versions 50 | COPY --from=ruby-3.2 /root/.asdf /tmp/.asdf-3.2 51 | COPY --from=ruby-3.3 /root/.asdf /tmp/.asdf-3.3 52 | COPY --from=ruby-3.4 /root/.asdf /tmp/.asdf-3.4 53 | RUN cp -r /tmp/.asdf-3.2/* /root/.asdf/ && \ 54 | cp -r /tmp/.asdf-3.3/* /root/.asdf/ && \ 55 | cp -r /tmp/.asdf-3.4/* /root/.asdf/ && \ 56 | rm -rf /tmp/.asdf* 57 | 58 | WORKDIR /app 59 | 60 | COPY bin bin 61 | COPY lib lib 62 | COPY spec spec 63 | COPY scripts scripts 64 | COPY sorbet sorbet 65 | COPY .env .rubocop.yml .standard.yml active_cached_resource.gemspec Gemfile Rakefile . 66 | 67 | CMD ['. "$ASDF_DIR/asdf.sh"', "&&", "tail", "-f", "/dev/null"] -------------------------------------------------------------------------------- /lib/activeresource/examples/performance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rubygems" 4 | require_relative "../lib/active_resource" 5 | require "benchmark" 6 | 7 | TIMES = (ENV["N"] || 10_000).to_i 8 | 9 | # deep nested resource 10 | attrs = { 11 | id: 1, 12 | name: "Luis", 13 | age: 21, 14 | friends: [ 15 | { 16 | name: "JK", 17 | age: 24, 18 | colors: ["red", "green", "blue"], 19 | brothers: [ 20 | { 21 | name: "Mateo", 22 | age: 35, 23 | children: [{ name: "Edith", age: 5 }, { name: "Martha", age: 4 }] 24 | }, 25 | { 26 | name: "Felipe", 27 | age: 33, 28 | children: [{ name: "Bryan", age: 1 }, { name: "Luke", age: 0 }] 29 | } 30 | ] 31 | }, 32 | { 33 | name: "Eduardo", 34 | age: 20, 35 | colors: [], 36 | brothers: [ 37 | { 38 | name: "Sebas", 39 | age: 23, 40 | children: [{ name: "Andres", age: 0 }, { name: "Jorge", age: 2 }] 41 | }, 42 | { 43 | name: "Elsa", 44 | age: 19, 45 | children: [{ name: "Natacha", age: 1 }] 46 | }, 47 | { 48 | name: "Milena", 49 | age: 16, 50 | children: [] 51 | } 52 | ] 53 | } 54 | ] 55 | } 56 | 57 | class Customer < ActiveResource::Base 58 | self.site = "http://37s.sunrise.i:3000" 59 | end 60 | 61 | module Nested 62 | class Customer < ActiveResource::Base 63 | self.site = "http://37s.sunrise.i:3000" 64 | end 65 | end 66 | 67 | Benchmark.bm(40) do |x| 68 | x.report("Model.new (instantiation)") { TIMES.times { Customer.new } } 69 | x.report("Nested::Model.new (instantiation)") { TIMES.times { Nested::Customer.new } } 70 | x.report("Model.new (setting attributes)") { TIMES.times { Customer.new attrs } } 71 | x.report("Nested::Model.new (setting attributes)") { TIMES.times { Nested::Customer.new attrs } } 72 | end 73 | -------------------------------------------------------------------------------- /example/consumer/config/puma.rb: -------------------------------------------------------------------------------- 1 | # This configuration file will be evaluated by Puma. The top-level methods that 2 | # are invoked here are part of Puma's configuration DSL. For more information 3 | # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. 4 | # 5 | # Puma starts a configurable number of processes (workers) and each process 6 | # serves each request in a thread from an internal thread pool. 7 | # 8 | # You can control the number of workers using ENV["WEB_CONCURRENCY"]. You 9 | # should only set this value when you want to run 2 or more workers. The 10 | # default is already 1. 11 | # 12 | # The ideal number of threads per worker depends both on how much time the 13 | # application spends waiting for IO operations and on how much you wish to 14 | # prioritize throughput over latency. 15 | # 16 | # As a rule of thumb, increasing the number of threads will increase how much 17 | # traffic a given process can handle (throughput), but due to CRuby's 18 | # Global VM Lock (GVL) it has diminishing returns and will degrade the 19 | # response time (latency) of the application. 20 | # 21 | # The default is set to 3 threads as it's deemed a decent compromise between 22 | # throughput and latency for the average Rails application. 23 | # 24 | # Any libraries that use a connection pool or another resource pool should 25 | # be configured to provide at least as many connections as the number of 26 | # threads. This includes Active Record's `pool` parameter in `database.yml`. 27 | threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) 28 | threads threads_count, threads_count 29 | 30 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 31 | port ENV.fetch("PORT", 3000) 32 | 33 | # Allow puma to be restarted by `bin/rails restart` command. 34 | plugin :tmp_restart 35 | 36 | # Run the Solid Queue supervisor inside of Puma for single-server deployments 37 | plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] 38 | 39 | # Specify the PID file. Defaults to tmp/pids/server.pid in development. 40 | # In other environments, only set the PID file if requested. 41 | pidfile ENV["PIDFILE"] if ENV["PIDFILE"] 42 | -------------------------------------------------------------------------------- /example/provider/config/puma.rb: -------------------------------------------------------------------------------- 1 | # This configuration file will be evaluated by Puma. The top-level methods that 2 | # are invoked here are part of Puma's configuration DSL. For more information 3 | # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. 4 | # 5 | # Puma starts a configurable number of processes (workers) and each process 6 | # serves each request in a thread from an internal thread pool. 7 | # 8 | # You can control the number of workers using ENV["WEB_CONCURRENCY"]. You 9 | # should only set this value when you want to run 2 or more workers. The 10 | # default is already 1. 11 | # 12 | # The ideal number of threads per worker depends both on how much time the 13 | # application spends waiting for IO operations and on how much you wish to 14 | # prioritize throughput over latency. 15 | # 16 | # As a rule of thumb, increasing the number of threads will increase how much 17 | # traffic a given process can handle (throughput), but due to CRuby's 18 | # Global VM Lock (GVL) it has diminishing returns and will degrade the 19 | # response time (latency) of the application. 20 | # 21 | # The default is set to 3 threads as it's deemed a decent compromise between 22 | # throughput and latency for the average Rails application. 23 | # 24 | # Any libraries that use a connection pool or another resource pool should 25 | # be configured to provide at least as many connections as the number of 26 | # threads. This includes Active Record's `pool` parameter in `database.yml`. 27 | threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) 28 | threads threads_count, threads_count 29 | 30 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 31 | port ENV.fetch("PORT", 3000) 32 | 33 | # Allow puma to be restarted by `bin/rails restart` command. 34 | plugin :tmp_restart 35 | 36 | # Run the Solid Queue supervisor inside of Puma for single-server deployments 37 | plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] 38 | 39 | # Specify the PID file. Defaults to tmp/pids/server.pid in development. 40 | # In other environments, only set the PID file if requested. 41 | pidfile ENV["PIDFILE"] if ENV["PIDFILE"] 42 | -------------------------------------------------------------------------------- /lib/activeresource/test/cases/log_subscriber_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | require "fixtures/person" 5 | require "active_support/log_subscriber/test_helper" 6 | require "active_support/core_ext/hash/conversions" 7 | 8 | require_relative "../../lib/active_resource/log_subscriber" 9 | 10 | class LogSubscriberTest < ActiveSupport::TestCase 11 | include ActiveSupport::LogSubscriber::TestHelper 12 | 13 | def setup 14 | super 15 | 16 | @matz = { person: { id: 1, name: "Matz" } }.to_json 17 | ActiveResource::HttpMock.respond_to do |mock| 18 | mock.get "/people/1.json", {}, @matz 19 | mock.get "/people/2.json", {}, nil, 404 20 | mock.get "/people/3.json", {}, nil, 502 21 | end 22 | 23 | ActiveResource::LogSubscriber.attach_to :active_resource 24 | end 25 | 26 | def set_logger(logger) 27 | ActiveResource::Base.logger = logger 28 | end 29 | 30 | def test_request_notification 31 | Person.find(1) 32 | wait 33 | assert_equal 2, @logger.logged(:info).size 34 | assert_equal "GET http://37s.sunrise.i:3000/people/1.json", @logger.logged(:info)[0] 35 | assert_match(/--> 200 200 33/, @logger.logged(:info)[1]) 36 | end 37 | 38 | def test_failure_error_log 39 | Person.find(2) 40 | rescue 41 | wait 42 | assert_equal 2, @logger.logged(:error).size 43 | assert_equal "GET http://37s.sunrise.i:3000/people/2.json", @logger.logged(:error)[0] 44 | assert_match(/--> 404 404 0/, @logger.logged(:error)[1]) 45 | end 46 | 47 | def test_server_error_log 48 | Person.find(3) 49 | rescue 50 | wait 51 | assert_equal 2, @logger.logged(:error).size 52 | assert_equal "GET http://37s.sunrise.i:3000/people/3.json", @logger.logged(:error)[0] 53 | assert_match(/--> 502 502 0/, @logger.logged(:error)[1]) 54 | end 55 | 56 | def test_connection_failure 57 | Person.find(99) 58 | rescue 59 | wait 60 | assert_equal 2, @logger.logged(:error).size 61 | assert_equal "GET http://37s.sunrise.i:3000/people/99.json", @logger.logged(:error)[0] 62 | assert_match(/--> 523 ActiveResource connection error 0/, @logger.logged(:error)[1]) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /sorbet/rbi/gems/securerandom@0.4.1.rbi: -------------------------------------------------------------------------------- 1 | # typed: true 2 | 3 | # DO NOT EDIT MANUALLY 4 | # This is an autogenerated file for types exported from the `securerandom` gem. 5 | # Please instead update this file by running `bin/tapioca gem securerandom`. 6 | 7 | 8 | # == Secure random number generator interface. 9 | # 10 | # This library is an interface to secure random number generators which are 11 | # suitable for generating session keys in HTTP cookies, etc. 12 | # 13 | # You can use this library in your application by requiring it: 14 | # 15 | # require 'securerandom' 16 | # 17 | # It supports the following secure random number generators: 18 | # 19 | # * openssl 20 | # * /dev/urandom 21 | # * Win32 22 | # 23 | # SecureRandom is extended by the Random::Formatter module which 24 | # defines the following methods: 25 | # 26 | # * alphanumeric 27 | # * base64 28 | # * choose 29 | # * gen_random 30 | # * hex 31 | # * rand 32 | # * random_bytes 33 | # * random_number 34 | # * urlsafe_base64 35 | # * uuid 36 | # 37 | # These methods are usable as class methods of SecureRandom such as 38 | # +SecureRandom.hex+. 39 | # 40 | # If a secure random number generator is not available, 41 | # +NotImplementedError+ is raised. 42 | # 43 | # source://securerandom//lib/securerandom.rb#41 44 | module SecureRandom 45 | extend ::Random::Formatter 46 | 47 | class << self 48 | # source://securerandom//lib/securerandom.rb#55 49 | def alphanumeric(n = T.unsafe(nil), chars: T.unsafe(nil)); end 50 | 51 | # Returns a random binary string containing +size+ bytes. 52 | # 53 | # See Random.bytes 54 | # 55 | # source://securerandom//lib/securerandom.rb#50 56 | def bytes(n); end 57 | 58 | # source://securerandom//lib/securerandom.rb#70 59 | def gen_random(n); end 60 | 61 | private 62 | 63 | # Implementation using OpenSSL 64 | # 65 | # source://securerandom//lib/securerandom.rb#65 66 | def gen_random_openssl(n); end 67 | 68 | # Implementation using system random device 69 | # 70 | # source://securerandom//lib/securerandom.rb#70 71 | def gen_random_urandom(n); end 72 | end 73 | end 74 | 75 | # The version 76 | # 77 | # source://securerandom//lib/securerandom.rb#44 78 | SecureRandom::VERSION = T.let(T.unsafe(nil), String) 79 | -------------------------------------------------------------------------------- /lib/activeresource/lib/active_resource/schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveResource # :nodoc: 4 | class Schema # :nodoc: 5 | # attributes can be known to be one of these types. They are easy to 6 | # cast to/from. 7 | KNOWN_ATTRIBUTE_TYPES = %w( string text integer float decimal datetime timestamp time date binary boolean ) 8 | 9 | # An array of attribute definitions, representing the attributes that 10 | # have been defined. 11 | attr_accessor :attrs 12 | 13 | # The internals of an Active Resource Schema are very simple - 14 | # unlike an Active Record TableDefinition (on which it is based). 15 | # It provides a set of convenience methods for people to define their 16 | # schema using the syntax: 17 | # schema do 18 | # string :foo 19 | # integer :bar 20 | # end 21 | # 22 | # The schema stores the name and type of each attribute. That is then 23 | # read out by the schema method to populate the schema of the actual 24 | # resource. 25 | def initialize 26 | @attrs = {} 27 | end 28 | 29 | def attribute(name, type, options = {}) 30 | raise ArgumentError, "Unknown Attribute type: #{type.inspect} for key: #{name.inspect}" unless type.nil? || Schema::KNOWN_ATTRIBUTE_TYPES.include?(type.to_s) 31 | 32 | the_type = type.to_s 33 | # TODO: add defaults 34 | # the_attr = [type.to_s] 35 | # the_attr << options[:default] if options.has_key? :default 36 | @attrs[name.to_s] = the_type 37 | self 38 | end 39 | 40 | # The following are the attribute types supported by Active Resource 41 | # migrations. 42 | KNOWN_ATTRIBUTE_TYPES.each do |attr_type| 43 | # def string(*args) 44 | # options = args.extract_options! 45 | # attr_names = args 46 | # 47 | # attr_names.each { |name| attribute(name, 'string', options) } 48 | # end 49 | class_eval <<-EOV, __FILE__, __LINE__ + 1 50 | # frozen_string_literal: true 51 | def #{attr_type}(*args) 52 | options = args.extract_options! 53 | attr_names = args 54 | 55 | attr_names.each { |name| attribute(name, '#{attr_type}', options) } 56 | end 57 | EOV 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /example/consumer/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | # check=error=true 3 | 4 | # This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand: 5 | # docker build -t consumer . 6 | # docker run -d -p 80:80 -e RAILS_MASTER_KEY=