api_keys 0.3.0 → 0.4.0

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 +51 -0
  3. data/README.md +101 -39
  4. data/SECURITY.md +33 -0
  5. data/app/controllers/api_keys/application_controller.rb +58 -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 +374 -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 +32 -0
  38. data/lib/generators/api_keys/templates/create_api_keys_table.rb.erb +2 -3
  39. data/lib/generators/api_keys/templates/initializer.rb +36 -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.0"
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,32 @@
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
+
25
+ private
26
+
27
+ def migration_version
28
+ major = ActiveRecord::VERSION::MAJOR
29
+ minor = ActiveRecord::VERSION::MINOR
30
+ major >= 5 ? "[#{major}.#{minor}]" : ""
31
+ end
32
+ 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
@@ -106,4 +105,4 @@ class CreateApiKeysTable < ActiveRecord::Migration<%= migration_version %>
106
105
  ""
107
106
  end
108
107
  end
109
- end
108
+ 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)
@@ -84,7 +90,11 @@ ApiKeys.configure do |config|
84
90
  # When key_types IS configured and you specify a key_type, this setting
85
91
  # is IGNORED - the prefix comes from the key type's configuration instead.
86
92
  #
87
- # WARNING: Once set, do NOT change or existing keys will fail authentication!
93
+ # Changing this later is SAFE for existing keys: every key stores its own
94
+ # prefix, so authentication keeps finding keys minted under retired
95
+ # prefixes (sha256 looks up by pure token digest; bcrypt uses a bounded
96
+ # stored-prefix lookup with an indexed last-four fallback for deployments
97
+ # with many retired prefixes). Only NEW keys wear the new prefix.
88
98
  # Default: -> { "ak_" }
89
99
  # config.token_prefix = -> { "myapp_" }
90
100
 
@@ -113,7 +123,8 @@ ApiKeys.configure do |config|
113
123
  # - limit: Max keys of this type per owner per environment (nil = unlimited)
114
124
  # - public: If true AND revocable: false, stores plaintext token in metadata
115
125
  # so it can be viewed again in the dashboard. Use ONLY for publishable
116
- # keys designed to be embedded in distributed apps. (default: false)
126
+ # keys designed to be embedded in distributed apps. Public types must
127
+ # use a finite, non-empty permissions array (never :all). (default: false)
117
128
  # SECURITY: NEVER set public: true on secret keys!
118
129
  #
119
130
  # config.key_types = {
@@ -227,6 +238,7 @@ ApiKeys.configure do |config|
227
238
  # - Includes salt, computationally expensive by design
228
239
  # - 10x-50x slower than SHA256 - impacts every API request
229
240
  # - Better for low-entropy secrets (passwords), overkill for random API keys
241
+ # - Prefix + encoded random token must fit bcrypt's 72-byte input limit
230
242
  #
231
243
  # Default: :sha256
232
244
  # config.hash_strategy = :sha256
@@ -240,10 +252,10 @@ ApiKeys.configure do |config|
240
252
  # Default: true
241
253
  # config.https_only_production = true
242
254
 
243
- # Raise an error (instead of just warning) for HTTP in production.
255
+ # Reject authentication (instead of just warning) over HTTP in production.
244
256
  # Only applies when https_only_production is true.
245
- # Default: false
246
- # config.https_strict_mode = false
257
+ # Default: true
258
+ # config.https_strict_mode = true
247
259
 
248
260
  # ============================================================================
249
261
  # BACKGROUND JOBS & CALLBACKS
@@ -263,34 +275,41 @@ ApiKeys.configure do |config|
263
275
  # config.enable_async_operations = true
264
276
 
265
277
  # Track request counts (increments requests_count on each authentication).
266
- # Note: High-frequency counter updates can impact database performance.
278
+ # Exact counting bypasses last-used debouncing and therefore enqueues one stats
279
+ # job per successful request. High-frequency updates can impact queue/database
280
+ # performance.
267
281
  # Default: false
268
282
  # config.track_requests_count = false
269
283
 
284
+ # Minimum interval between last_used_at jobs when exact request counting is
285
+ # disabled. Set to 0 or nil to update on every successful authentication.
286
+ # Default: 1.minute
287
+ # config.stats_update_interval = 1.minute
288
+
270
289
  # Queue names for background jobs.
271
290
  # Default: :default
272
291
  # config.stats_job_queue = :default
273
292
  # config.callbacks_job_queue = :default
274
293
 
275
- # Callbacks executed on authentication attempts.
276
- # before_authentication: receives the request object
277
- # after_authentication: receives the Result object (success/failure info)
294
+ # Callbacks enqueued on authentication attempts. Contexts are serializable
295
+ # hashes and never contain the token, request, result, or ApiKey object.
296
+ # before_authentication: { request_uuid: String }
297
+ # after_authentication: { success:, error_code:, api_key_id:,
298
+ # required_scope_check: (optional) }
278
299
  #
279
300
  # Default: empty procs (no-op)
280
- # config.before_authentication = ->(request) { Rails.logger.info "Auth: #{request.uuid}" }
281
- # config.after_authentication = ->(result) { Analytics.track(result) }
301
+ # config.before_authentication = ->(context) { Rails.logger.info "Auth request: #{context[:request_uuid]}" }
302
+ # config.after_authentication = ->(context) { Analytics.track_auth(context) }
282
303
 
283
304
  # ============================================================================
284
305
  # PERFORMANCE
285
306
  # ============================================================================
286
307
 
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
308
+ # Time-to-live (TTL) for caching API key ID lookup hints in Rails.cache.
291
309
  #
292
- # Trade-off: A revoked key may still work for up to `cache_ttl` seconds
293
- # until the cache expires.
310
+ # Cache entries are never authorization authority. Every hit reloads the
311
+ # current database row and cryptographically re-verifies the token, so
312
+ # revocation, expiration, and permission changes remain immediate.
294
313
  #
295
314
  # Set to 0 or nil to disable caching entirely.
296
315
  # 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.0
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: "../"