api_keys 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +147 -38
  4. data/SECURITY.md +33 -0
  5. data/app/controllers/api_keys/application_controller.rb +92 -10
  6. data/app/controllers/api_keys/keys_controller.rb +40 -18
  7. data/app/views/api_keys/keys/_empty_state.html.erb +1 -1
  8. data/app/views/api_keys/keys/_form.html.erb +3 -3
  9. data/app/views/api_keys/keys/_key_actions.html.erb +3 -3
  10. data/app/views/api_keys/keys/_key_badges.html.erb +2 -2
  11. data/app/views/api_keys/keys/_key_row.html.erb +1 -1
  12. data/app/views/api_keys/keys/_key_status.html.erb +3 -3
  13. data/app/views/api_keys/keys/_keys_table.html.erb +1 -4
  14. data/app/views/api_keys/keys/_show_token.html.erb +5 -46
  15. data/app/views/api_keys/keys/_token_display.html.erb +3 -3
  16. data/app/views/api_keys/keys/index.html.erb +2 -2
  17. data/app/views/api_keys/keys/show.html.erb +2 -2
  18. data/app/views/api_keys/security/best_practices.html.erb +7 -7
  19. data/app/views/layouts/api_keys/application.html.erb +159 -12
  20. data/lib/api_keys/authentication.rb +39 -11
  21. data/lib/api_keys/configuration.rb +412 -24
  22. data/lib/api_keys/engine.rb +5 -20
  23. data/lib/api_keys/form_builder_extensions.rb +12 -2
  24. data/lib/api_keys/helpers/expiration_options.rb +11 -3
  25. data/lib/api_keys/helpers/token_session.rb +143 -8
  26. data/lib/api_keys/helpers/view_helpers.rb +5 -1
  27. data/lib/api_keys/jobs/callbacks_job.rb +10 -17
  28. data/lib/api_keys/jobs/update_stats_job.rb +27 -12
  29. data/lib/api_keys/models/api_key.rb +244 -25
  30. data/lib/api_keys/models/concerns/has_api_keys.rb +95 -32
  31. data/lib/api_keys/services/authenticator.rb +263 -118
  32. data/lib/api_keys/services/digestor.rb +76 -13
  33. data/lib/api_keys/services/token_generator.rb +41 -1
  34. data/lib/api_keys/tenant_resolution.rb +2 -4
  35. data/lib/api_keys/version.rb +1 -1
  36. data/lib/generators/api_keys/add_authentication_index_generator.rb +36 -0
  37. data/lib/generators/api_keys/templates/add_authentication_index_to_api_keys.rb.erb +24 -0
  38. data/lib/generators/api_keys/templates/create_api_keys_table.rb.erb +2 -14
  39. data/lib/generators/api_keys/templates/initializer.rb +54 -17
  40. metadata +16 -16
  41. data/.simplecov +0 -36
  42. data/AGENTS.md +0 -5
  43. data/Appraisals +0 -17
  44. data/CLAUDE.md +0 -5
  45. data/Rakefile +0 -37
  46. data/context7.json +0 -4
  47. data/gemfiles/rails_7.2.gemfile +0 -21
  48. data/gemfiles/rails_8.0.gemfile +0 -21
  49. data/gemfiles/rails_8.1.gemfile +0 -21
@@ -7,13 +7,25 @@ module ApiKeys
7
7
  module Services
8
8
  # Generates secure, random API tokens according to configured settings.
9
9
  class TokenGenerator
10
+ MIN_RANDOM_BYTES = 16
11
+ MAX_RANDOM_BYTES = 64
12
+ MAX_PREFIX_BYTESIZE = 64
13
+
10
14
  # Generates a new token string.
11
15
  #
12
16
  # @param length [Integer] The desired byte length of the random part (before encoding).
13
17
  # @param prefix [String] The prefix to prepend to the token.
14
18
  # @param alphabet [Symbol] The encoding alphabet (:base58 or :hex).
15
19
  # @return [String] The generated token including the prefix.
16
- def self.call(length: ApiKeys.configuration.token_length, prefix: ApiKeys.configuration.token_prefix.call, alphabet: ApiKeys.configuration.token_alphabet)
20
+ def self.call(length: nil, prefix: nil, alphabet: nil)
21
+ length ||= ApiKeys.configuration.token_length
22
+ prefix ||= ApiKeys.configuration.resolved_token_prefix
23
+ alphabet ||= ApiKeys.configuration.token_alphabet
24
+
25
+ validate_length!(length)
26
+ validate_prefix!(prefix)
27
+ validate_alphabet!(alphabet)
28
+
17
29
  random_bytes = SecureRandom.bytes(length)
18
30
 
19
31
  random_part = case alphabet
@@ -27,6 +39,34 @@ module ApiKeys
27
39
 
28
40
  "#{prefix}#{random_part}"
29
41
  end
42
+
43
+ def self.validate_length!(length)
44
+ return if length.is_a?(Integer) && length.between?(MIN_RANDOM_BYTES, MAX_RANDOM_BYTES)
45
+
46
+ raise ArgumentError,
47
+ "Token length must be an Integer between #{MIN_RANDOM_BYTES} and #{MAX_RANDOM_BYTES} random bytes."
48
+ end
49
+
50
+ def self.validate_prefix!(prefix)
51
+ valid = prefix.is_a?(String) && prefix.present? && prefix.valid_encoding? &&
52
+ prefix.bytesize <= MAX_PREFIX_BYTESIZE &&
53
+ prefix.each_codepoint.none? { |codepoint| codepoint <= 0x20 || codepoint == 0x7f }
54
+ return if valid
55
+
56
+ raise ArgumentError,
57
+ "Token prefix must be a non-blank string of at most #{MAX_PREFIX_BYTESIZE} bytes without whitespace or control characters."
58
+ rescue ArgumentError
59
+ raise ArgumentError,
60
+ "Token prefix must be a non-blank string of at most #{MAX_PREFIX_BYTESIZE} bytes without whitespace or control characters."
61
+ end
62
+
63
+ def self.validate_alphabet!(alphabet)
64
+ return if %i[base58 hex].include?(alphabet)
65
+
66
+ raise ArgumentError, "Unsupported token alphabet: #{alphabet}. Use :base58 or :hex."
67
+ end
68
+
69
+ private_class_method :validate_length!, :validate_prefix!, :validate_alphabet!
30
70
  end
31
71
  end
32
72
  end
@@ -15,8 +15,6 @@ module ApiKeys
15
15
  alias_method :current_api_key_tenant, :current_api_tenant
16
16
  alias_method :current_api_account, :current_api_tenant
17
17
  alias_method :current_api_key_account, :current_api_tenant
18
- alias_method :current_api_owner, :current_api_tenant
19
- alias_method :current_api_key_owner, :current_api_tenant
20
18
  end
21
19
 
22
20
  # Returns the tenant associated with the current API key.
@@ -31,10 +29,10 @@ module ApiKeys
31
29
 
32
30
  resolver = ApiKeys.configuration.tenant_resolver
33
31
  @current_api_tenant = resolver&.call(current_api_key)
34
- rescue StandardError => e
32
+ rescue StandardError => error
35
33
  # Log error but don't break the request if resolver fails
36
34
  if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
37
- Rails.logger.error "[ApiKeys] Tenant resolution failed: #{e.message}"
35
+ Rails.logger.error "[ApiKeys] Tenant resolution failed (#{error.class})."
38
36
  end
39
37
  @current_api_tenant = nil
40
38
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApiKeys
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.1"
5
5
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+ require "rails/generators/active_record"
5
+
6
+ module ApiKeys
7
+ module Generators
8
+ # Adds the bounded bcrypt authentication lookup index to existing installs.
9
+ class AddAuthenticationIndexGenerator < Rails::Generators::Base
10
+ include ActiveRecord::Generators::Migration
11
+
12
+ source_root File.expand_path("templates", __dir__)
13
+
14
+ def self.next_migration_number(dirname)
15
+ number = current_migration_number(dirname) + 1
16
+ ActiveRecord::Migration.next_migration_number(number)
17
+ end
18
+
19
+ def create_migration_file
20
+ migration_template "add_authentication_index_to_api_keys.rb.erb",
21
+ File.join(db_migrate_path, "add_authentication_index_to_api_keys.rb")
22
+ end
23
+
24
+ def display_post_install_message
25
+ say "\n🔐 API key authentication index migration created.", :green
26
+ say "Run `rails db:migrate` before deploying bcrypt authentication changes."
27
+ end
28
+
29
+ private
30
+
31
+ def migration_version
32
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddAuthenticationIndexToApiKeys < ActiveRecord::Migration<%= migration_version %>
4
+ INDEX_NAME = "index_api_keys_authentication_lookup"
5
+ COLUMNS = %i[prefix last4 digest_algorithm].freeze
6
+
7
+ disable_ddl_transaction!
8
+
9
+ def up
10
+ return if index_exists?(:api_keys, COLUMNS, name: INDEX_NAME)
11
+
12
+ options = { name: INDEX_NAME }
13
+ options[:algorithm] = :concurrently if connection.adapter_name.downcase.include?("postgres")
14
+ add_index :api_keys, COLUMNS, **options
15
+ end
16
+
17
+ def down
18
+ return unless index_exists?(:api_keys, COLUMNS, name: INDEX_NAME)
19
+
20
+ options = { name: INDEX_NAME }
21
+ options[:algorithm] = :concurrently if connection.adapter_name.downcase.include?("postgres")
22
+ remove_index :api_keys, **options
23
+ end
24
+ end
@@ -55,12 +55,11 @@ class CreateApiKeysTable < ActiveRecord::Migration<%= migration_version %>
55
55
 
56
56
  # Index to optimize prefix-based lookups (especially for bcrypt)
57
57
  t.index [:prefix, :digest_algorithm], name: "index_api_keys_on_prefix_and_digest_algorithm"
58
+ t.index [:prefix, :last4, :digest_algorithm], name: "index_api_keys_authentication_lookup"
58
59
 
59
60
  # Optional indexes
60
61
  t.index :prefix
61
62
  t.index :last4 # Index potentially useful for UI lookups/filtering by masked token
62
- t.index :owner_id if foreign_key_type # Index owner_id only if it's a separate column
63
- t.index :owner_type if foreign_key_type # Index owner_type only if it's a separate column
64
63
  t.index :expires_at
65
64
  t.index :revoked_at
66
65
  t.index :last_used_at
@@ -95,15 +94,4 @@ class CreateApiKeysTable < ActiveRecord::Migration<%= migration_version %>
95
94
  # Fallback during initial setup or if connection isn't available
96
95
  :text
97
96
  end
98
-
99
- # Provides the appropriate migration version syntax for the current Rails version.
100
- def migration_version
101
- major = ActiveRecord::VERSION::MAJOR
102
- minor = ActiveRecord::VERSION::MINOR
103
- if major >= 5
104
- "[#{major}.#{minor}]"
105
- else
106
- ""
107
- end
108
- end
109
- end
97
+ end
@@ -23,6 +23,12 @@ ApiKeys.configure do |config|
23
23
  # Configure how the dashboard integrates with your application's auth system.
24
24
  # ============================================================================
25
25
 
26
+ # Controller class that the engine dashboard inherits from. Configure this in
27
+ # the initializer before engine controllers load. Accepts a Class or constant
28
+ # name string.
29
+ # Default: "::ApplicationController"
30
+ # config.parent_controller = "Admin::ApplicationController"
31
+
26
32
  # The method that returns the current API key owner in your controllers.
27
33
  # This should return the model instance that has_api_keys.
28
34
  # Default: :current_user (works with Devise out of the box)
@@ -52,6 +58,24 @@ ApiKeys.configure do |config|
52
58
  # Default: "‹ Home"
53
59
  # config.return_text = "‹ Back to Settings"
54
60
 
61
+ # Content Security Policy for the mounted dashboard pages.
62
+ #
63
+ # :default (default) - Hardened, but compatible with a normal host layout.
64
+ # Blocks framing, plugins, <base> hijacking, and
65
+ # cross-origin form posts, while allowing same-origin
66
+ # scripts, styles, and fonts so your layout's asset
67
+ # tags keep working.
68
+ # :strict - Nonce-only: no script or stylesheet loads unless it
69
+ # carries the engine's per-request nonce. Only use this
70
+ # if your layout serves nothing un-nonced on these pages
71
+ # (Rails only nonces asset tags when you also set
72
+ # config.content_security_policy_nonce_auto = true).
73
+ # false / nil - Declare nothing; your application's own policy applies
74
+ # unchanged. You then have to allow the dashboard's
75
+ # nonced inline <style>/<script> yourself.
76
+ #
77
+ # config.dashboard_content_security_policy = :default
78
+
55
79
  # ============================================================================
56
80
  # TOKEN PREFIXES
57
81
  # ============================================================================
@@ -84,7 +108,11 @@ ApiKeys.configure do |config|
84
108
  # When key_types IS configured and you specify a key_type, this setting
85
109
  # is IGNORED - the prefix comes from the key type's configuration instead.
86
110
  #
87
- # WARNING: Once set, do NOT change or existing keys will fail authentication!
111
+ # Changing this later is SAFE for existing keys: every key stores its own
112
+ # prefix, so authentication keeps finding keys minted under retired
113
+ # prefixes (sha256 looks up by pure token digest; bcrypt uses a bounded
114
+ # stored-prefix lookup with an indexed last-four fallback for deployments
115
+ # with many retired prefixes). Only NEW keys wear the new prefix.
88
116
  # Default: -> { "ak_" }
89
117
  # config.token_prefix = -> { "myapp_" }
90
118
 
@@ -113,7 +141,8 @@ ApiKeys.configure do |config|
113
141
  # - limit: Max keys of this type per owner per environment (nil = unlimited)
114
142
  # - public: If true AND revocable: false, stores plaintext token in metadata
115
143
  # so it can be viewed again in the dashboard. Use ONLY for publishable
116
- # keys designed to be embedded in distributed apps. (default: false)
144
+ # keys designed to be embedded in distributed apps. Public types must
145
+ # use a finite, non-empty permissions array (never :all). (default: false)
117
146
  # SECURITY: NEVER set public: true on secret keys!
118
147
  #
119
148
  # config.key_types = {
@@ -227,6 +256,7 @@ ApiKeys.configure do |config|
227
256
  # - Includes salt, computationally expensive by design
228
257
  # - 10x-50x slower than SHA256 - impacts every API request
229
258
  # - Better for low-entropy secrets (passwords), overkill for random API keys
259
+ # - Prefix + encoded random token must fit bcrypt's 72-byte input limit
230
260
  #
231
261
  # Default: :sha256
232
262
  # config.hash_strategy = :sha256
@@ -240,10 +270,10 @@ ApiKeys.configure do |config|
240
270
  # Default: true
241
271
  # config.https_only_production = true
242
272
 
243
- # Raise an error (instead of just warning) for HTTP in production.
273
+ # Reject authentication (instead of just warning) over HTTP in production.
244
274
  # Only applies when https_only_production is true.
245
- # Default: false
246
- # config.https_strict_mode = false
275
+ # Default: true
276
+ # config.https_strict_mode = true
247
277
 
248
278
  # ============================================================================
249
279
  # BACKGROUND JOBS & CALLBACKS
@@ -263,34 +293,41 @@ ApiKeys.configure do |config|
263
293
  # config.enable_async_operations = true
264
294
 
265
295
  # Track request counts (increments requests_count on each authentication).
266
- # Note: High-frequency counter updates can impact database performance.
296
+ # Exact counting bypasses last-used debouncing and therefore enqueues one stats
297
+ # job per successful request. High-frequency updates can impact queue/database
298
+ # performance.
267
299
  # Default: false
268
300
  # config.track_requests_count = false
269
301
 
302
+ # Minimum interval between last_used_at jobs when exact request counting is
303
+ # disabled. Set to 0 or nil to update on every successful authentication.
304
+ # Default: 1.minute
305
+ # config.stats_update_interval = 1.minute
306
+
270
307
  # Queue names for background jobs.
271
308
  # Default: :default
272
309
  # config.stats_job_queue = :default
273
310
  # config.callbacks_job_queue = :default
274
311
 
275
- # Callbacks executed on authentication attempts.
276
- # before_authentication: receives the request object
277
- # after_authentication: receives the Result object (success/failure info)
312
+ # Callbacks enqueued on authentication attempts. Contexts are serializable
313
+ # hashes and never contain the token, request, result, or ApiKey object.
314
+ # before_authentication: { request_uuid: String }
315
+ # after_authentication: { success:, error_code:, api_key_id:,
316
+ # required_scope_check: (optional) }
278
317
  #
279
318
  # Default: empty procs (no-op)
280
- # config.before_authentication = ->(request) { Rails.logger.info "Auth: #{request.uuid}" }
281
- # config.after_authentication = ->(result) { Analytics.track(result) }
319
+ # config.before_authentication = ->(context) { Rails.logger.info "Auth request: #{context[:request_uuid]}" }
320
+ # config.after_authentication = ->(context) { Analytics.track_auth(context) }
282
321
 
283
322
  # ============================================================================
284
323
  # PERFORMANCE
285
324
  # ============================================================================
286
325
 
287
- # Time-to-live (TTL) for caching API key lookups in Rails.cache.
288
- #
289
- # Higher values = better performance (fewer DB queries)
290
- # Lower values = faster propagation of revocations/changes
326
+ # Time-to-live (TTL) for caching API key ID lookup hints in Rails.cache.
291
327
  #
292
- # Trade-off: A revoked key may still work for up to `cache_ttl` seconds
293
- # until the cache expires.
328
+ # Cache entries are never authorization authority. Every hit reloads the
329
+ # current database row and cryptographically re-verifies the token, so
330
+ # revocation, expiration, and permission changes remain immediate.
294
331
  #
295
332
  # Set to 0 or nil to disable caching entirely.
296
333
  # Default: 5.seconds
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - rameerez
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-07-07 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -41,16 +41,22 @@ dependencies:
41
41
  name: bcrypt
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - "~>"
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.1.22
47
+ - - "<"
45
48
  - !ruby/object:Gem::Version
46
- version: '3.1'
49
+ version: '4'
47
50
  type: :runtime
48
51
  prerelease: false
49
52
  version_requirements: !ruby/object:Gem::Requirement
50
53
  requirements:
51
- - - "~>"
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 3.1.22
57
+ - - "<"
52
58
  - !ruby/object:Gem::Version
53
- version: '3.1'
59
+ version: '4'
54
60
  description: Add secure, production-ready API key authentication to your Rails app
55
61
  in minutes. Handles key generation, hashing, expiration, revocation, per-key scopes;
56
62
  plus a drop-in dashboard for your users to self-issue and manage their own API keys.
@@ -60,14 +66,10 @@ executables: []
60
66
  extensions: []
61
67
  extra_rdoc_files: []
62
68
  files:
63
- - ".simplecov"
64
- - AGENTS.md
65
- - Appraisals
66
69
  - CHANGELOG.md
67
- - CLAUDE.md
68
70
  - LICENSE.txt
69
71
  - README.md
70
- - Rakefile
72
+ - SECURITY.md
71
73
  - api_keys_dashboard.webp
72
74
  - api_keys_permissions.webp
73
75
  - api_keys_token.webp
@@ -92,10 +94,6 @@ files:
92
94
  - app/views/api_keys/security/best_practices.html.erb
93
95
  - app/views/layouts/api_keys/application.html.erb
94
96
  - config/routes.rb
95
- - context7.json
96
- - gemfiles/rails_7.2.gemfile
97
- - gemfiles/rails_8.0.gemfile
98
- - gemfiles/rails_8.1.gemfile
99
97
  - lib/api_keys.rb
100
98
  - lib/api_keys/authentication.rb
101
99
  - lib/api_keys/configuration.rb
@@ -116,8 +114,10 @@ files:
116
114
  - lib/api_keys/services/token_generator.rb
117
115
  - lib/api_keys/tenant_resolution.rb
118
116
  - lib/api_keys/version.rb
117
+ - lib/generators/api_keys/add_authentication_index_generator.rb
119
118
  - lib/generators/api_keys/add_key_types_generator.rb
120
119
  - lib/generators/api_keys/install_generator.rb
120
+ - lib/generators/api_keys/templates/add_authentication_index_to_api_keys.rb.erb
121
121
  - lib/generators/api_keys/templates/add_key_types_to_api_keys.rb.erb
122
122
  - lib/generators/api_keys/templates/create_api_keys_table.rb.erb
123
123
  - lib/generators/api_keys/templates/initializer.rb
@@ -144,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
144
  - !ruby/object:Gem::Version
145
145
  version: '0'
146
146
  requirements: []
147
- rubygems_version: 3.6.2
147
+ rubygems_version: 3.6.9
148
148
  specification_version: 4
149
149
  summary: Gate your Rails API with secure, self-serve API keys in minutes
150
150
  test_files: []
data/.simplecov DELETED
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SimpleCov configuration file (auto-loaded before test suite)
4
- # This keeps test_helper.rb clean and follows best practices
5
-
6
- SimpleCov.start do
7
- # Use SimpleFormatter for terminal-only output (no HTML generation)
8
- formatter SimpleCov::Formatter::SimpleFormatter
9
-
10
- # Track coverage for the lib directory (gem source code)
11
- add_filter "/test/"
12
-
13
- # Track the lib and app directories
14
- track_files "{lib,app}/**/*.rb"
15
-
16
- # Enable branch coverage for more detailed metrics
17
- enable_coverage :branch
18
-
19
- # Set minimum coverage threshold to prevent coverage regression
20
- # Starting at 60% - can be increased as coverage improves
21
- minimum_coverage line: 60, branch: 60
22
-
23
- # Disambiguate parallel test runs
24
- command_name "Job #{ENV['TEST_ENV_NUMBER']}" if ENV['TEST_ENV_NUMBER']
25
- end
26
-
27
- # Print coverage summary to terminal after tests complete
28
- SimpleCov.at_exit do
29
- SimpleCov.result.format!
30
- puts "\n" + "=" * 60
31
- puts "COVERAGE SUMMARY"
32
- puts "=" * 60
33
- puts "Line Coverage: #{SimpleCov.result.covered_percent.round(2)}%"
34
- puts "Branch Coverage: #{SimpleCov.result.coverage_statistics[:branch]&.percent&.round(2) || 'N/A'}%"
35
- puts "=" * 60
36
- end
data/AGENTS.md DELETED
@@ -1,5 +0,0 @@
1
- # AGENTS.md
2
-
3
- This file provides guidance to AI Agents (like OpenAI's Codex, Cursor Agent, Claude Code, etc) when working with code in this repository.
4
-
5
- Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
data/Appraisals DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Test against multiple Rails versions
4
- # This is our most critical dependency point
5
-
6
- appraise "rails-7.2" do
7
- gem "rails", "~> 7.2.3"
8
- end
9
-
10
- appraise "rails-8.0" do
11
- gem "rails", "~> 8.0"
12
- end
13
-
14
- # Test against Rails 8.1 (latest)
15
- appraise "rails-8.1" do
16
- gem "rails", "~> 8.1.2"
17
- end
data/CLAUDE.md DELETED
@@ -1,5 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
data/Rakefile DELETED
@@ -1,37 +0,0 @@
1
- begin
2
- require "bundler/setup"
3
- rescue LoadError
4
- puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
- end
6
-
7
- require "bundler/gem_tasks"
8
-
9
- require "rdoc/task"
10
-
11
- RDoc::Task.new(:rdoc) do |rdoc|
12
- rdoc.rdoc_dir = "rdoc"
13
- rdoc.title = "Pay"
14
- rdoc.options << "--line-numbers"
15
- rdoc.rdoc_files.include("README.md")
16
- rdoc.rdoc_files.include("lib/**/*.rb")
17
- end
18
-
19
- # Removed loading of engine tasks to avoid pulling in dummy app/vendor tests
20
- # APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
21
- # load "rails/tasks/engine.rake"
22
-
23
- # Removed Rails statistics task; not needed for gem tests
24
- # load "rails/tasks/statistics.rake"
25
-
26
- require "rake/testtask"
27
-
28
- # Ensure any test task created by engine.rake is cleared so only this suite runs
29
- Rake::Task[:test].clear if Rake::Task.task_defined?(:test)
30
-
31
- Rake::TestTask.new(:test) do |t|
32
- t.libs << "test"
33
- t.verbose = false
34
- t.test_files = FileList['test/**/*_test.rb'].exclude('test/dummy/**/*')
35
- end
36
-
37
- task default: :test
data/context7.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "url": "https://context7.com/rameerez/api_keys",
3
- "public_key": "pk_HibNJE5rTFvy1txHHXUot"
4
- }
@@ -1,21 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rake", "~> 13.0"
6
- gem "rails", "~> 7.2.3"
7
-
8
- group :development, :test do
9
- gem "appraisal"
10
- gem "minitest", "~> 6.0"
11
- gem "minitest-mock"
12
- gem "minitest-reporters"
13
- gem "rack-test"
14
- gem "simplecov", require: false
15
- gem "sqlite3", ">= 2.1"
16
- gem "bootsnap", require: false
17
- gem "mocha", "~> 2.0"
18
- gem "ostruct"
19
- end
20
-
21
- gemspec path: "../"
@@ -1,21 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rake", "~> 13.0"
6
- gem "rails", "~> 8.0"
7
-
8
- group :development, :test do
9
- gem "appraisal"
10
- gem "minitest", "~> 6.0"
11
- gem "minitest-mock"
12
- gem "minitest-reporters"
13
- gem "rack-test"
14
- gem "simplecov", require: false
15
- gem "sqlite3", ">= 2.1"
16
- gem "bootsnap", require: false
17
- gem "mocha", "~> 2.0"
18
- gem "ostruct"
19
- end
20
-
21
- gemspec path: "../"
@@ -1,21 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rake", "~> 13.0"
6
- gem "rails", "~> 8.1.2"
7
-
8
- group :development, :test do
9
- gem "appraisal"
10
- gem "minitest", "~> 6.0"
11
- gem "minitest-mock"
12
- gem "minitest-reporters"
13
- gem "rack-test"
14
- gem "simplecov", require: false
15
- gem "sqlite3", ">= 2.1"
16
- gem "bootsnap", require: false
17
- gem "mocha", "~> 2.0"
18
- gem "ostruct"
19
- end
20
-
21
- gemspec path: "../"