Skip to content

More cleanup and improvements - #25

Merged
skunkworker merged 10 commits into
masterfrom
aug26_fix_ci
Aug 27, 2026
Merged

More cleanup and improvements#25
skunkworker merged 10 commits into
masterfrom
aug26_fix_ci

Conversation

@skunkworker

Copy link
Copy Markdown
Contributor

No description provided.

John Bolliger and others added 10 commits August 26, 2026 23:32
ArgumentKeys joined the argument values with no separator and dropped the
field names, so three different finders shared one cache entry:

  Collide.cached_find_by_alpha_and_beta("x", "y")   # => {alpha: "x", beta: "y"}
  Collide.cached_find_by_delta_and_gamma("x", "y")  # => {alpha: "x", beta: "y"}
  Collide.cached_find_by_guid("xy")                 # => {alpha: "x", beta: "y"}

The second and third calls returned the first record.

ArgumentKeys.for_fields now names each field and escapes the separators
inside a value, so the key is one-to-one with the arguments. The separators
survive both the remove-characters and the replace-characters option.

This changes every cache key. Existing entries become a miss.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two problems in the generated cached_search_by_*! method:

  raise ::ActiveRemote::RemoteRecordNotFound.new(self.class) if results.first.nil?

self.class is Class inside a `def self.` body, so the message read
"Class does not exist" and error.remote_record_class returned Class.

results.first raised NoMethodError when search returned nil, where the
caller expects RemoteRecordNotFound.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each generated method built its options through Hash#to_s:

  .merge(#{cached_finder_options})

The result has to parse as Ruby source, and a common value does not:

  cached_finders_for :guid, :expires_in => 5.minutes   # => SyntaxError

5.minutes.to_s produces "5 minutes". Any object without a literal form
failed the same way.

The failure also left the class broken. cached_methods << method_name ran
before class_eval, so the name was registered with no method behind it.
method_missing then dispatched to that same missing name and recursed until
Ruby raised SystemStackError.

The options now live in a per-class registry that the generated method reads
at call time, and the name is registered only after class_eval returns. The
lookup walks the superclass chain, so a subclass keeps the declared options.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_args_in_sorted_order looked each field name up by index and passed the
result through without a check:

  index = orignal_args_name.index(arg_name)
  args_in_order << args[index]

A missing positional argument became nil with no error, and the finder then
cached a record under a key built from that nil:

  E.cached_find_by_beta_and_alpha("only_one")  # => {alpha: nil, beta: "only_one"}
  E.cached_find_by_alpha_and_beta("only_one")  # => ArgumentError

The same call raised in the defined order and passed nil in the reordered
order. A field name absent from the called name raised TypeError from
args[nil] rather than a useful error.

Also corrects the orignal_args_name typo and the match_1/match_2 names, which
drops the Naming/VariableNumber and Metrics/AbcSize suppressions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cache#fetch let the provider write the value, then deleted it when the value
was not valid. Three searches that returned nil produced this call sequence:

  [:fetch, :write, :delete, :fetch, :write, :delete, :fetch, :write, :delete]

Two of every three calls were waste. Against Redis or Memcached that is two
network round trips per request, on the cache-miss path.

The provider now receives :skip_nil unless the caller passed :allow_nil, so
the nil is never written and needs no delete. The empty-value case still
writes and deletes, because ActiveSupport has no matching option.

The generated delete method also built the same ArgumentKeys twice. It now
builds the key once and uses it for both the find and the search key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validate_provider_method_present raised a bare String, so callers got a
RuntimeError whose message carried the heredoc indentation. It now raises
ActiveRemote::Cached::Cache::InvalidCacheProvider with a single-line message.

The raise was also indented two columns past the return above it. That one
line was why .rubocop.yml disabled Layout/IndentationConsistency for the whole
repository.

Six cop exclusions are now dead and removed. I forced each one on and got no
offenses: Layout/AccessModifierIndentation, Layout/CommentIndentation,
Layout/IndentationConsistency, Style/AccessModifierDeclarations,
Style/MissingRespondToMissing (respond_to_missing? is defined), and
Naming/VariableNumber. The global Metrics/MethodLength disable becomes a
per-file exclusion, so a long method in a new file is still flagged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- homepage was an empty string; set the GitHub URL.
- No license was declared, though LICENSE.txt says MIT. gem build warned.
- test_files is deprecated; removed.
- files called git ls-files with no fallback, so a build from a released
  tarball produced an empty file list.
- activesupport had no version floor, though the code calls
  ActiveSupport::Cache::NullStore and reads ActiveSupport::VERSION::STRING.
  Set >= 6.1 to match the active_remote floor.
- Added metadata with rubygems_mfa_required.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ActiveRemote::Cached.cache(provider) builds a new Cache, and a new Cache
starts with a NullStore as its nested provider. A nested cache that
enable_nested_caching! had turned on was silently lost:

  cache.enable_nested_caching!
  ActiveRemote::Cached.cache(other_store)
  # nested provider is a NullStore again

The Railtie calls enable_nested_caching! after it sets the provider, so Rails
was unaffected. An application that swapped the store later was not.

Adds Cache#nested_caching? and carries the setting to the new Cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
REMOVE_CHARACTERS uses [[:space:]], so it strips a tab and a newline.
REPLACE_MAP listed only a plain space, so it left them in the key:

  remove  "a\tb"  # => "ab"
  replace "a\tb"  # => "a\tb"    tab survives into the key

A raw tab or newline in a cache key breaks the Memcached protocol.

REPLACE_MAP now covers tab, newline, carriage return, form feed, and vertical
tab. It is also a Hash rather than an array of pairs, so #cache_key makes one
gsub pass over the string instead of 13.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The cache key format changed, so every existing entry becomes a miss. A
dynamic finder called with too few arguments now raises ArgumentError rather
than caching a record under a nil, and the cache provider validator raises
InvalidCacheProvider rather than a bare RuntimeError.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@skunkworker
skunkworker merged commit 8cb9eda into master Aug 27, 2026
20 checks passed
@skunkworker
skunkworker deleted the aug26_fix_ci branch August 27, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant