ask-token-usage-rails 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 34b1d95794c074506226ba84ddbc01130d7ba026663751741d0c13d497949b55
4
+ data.tar.gz: 1d6764ef111ded783089f8703182dede3b0dbeadf8b3be5e106179c6f797efc6
5
+ SHA512:
6
+ metadata.gz: d4f4d4219eb29625d4b962c981da5677216a150c972f27b73fc90eafbb64526e12abb6302883c514e50d039654faf60b26d81127e44fbd044d71c828c3f3ed79
7
+ data.tar.gz: 9a55d29615156f17d6baef130f7607c20d4cce8d49f843c97bbddeff869f71fc7c14c712b25decfadc4f7ea54e149539a8b66f3ad8e2de470925730461bb046e
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-08-19
4
+
5
+ ### Added
6
+ - `has_token_wallet` concern for any ActiveRecord model.
7
+ - `Ask::TokenUsage::TokenWallet` (polymorphic owner + cached balance).
8
+ - `Ask::TokenUsage::TokenTransaction` append-only ledger (immutable).
9
+ - `Ask::TokenUsage::Rails::ActiveRecordStore` implementing the core Store port.
10
+ - `rails g ask_token_usage:install` — migration (`token_wallets`, `token_transactions`) + initializer.
11
+ - `SweepExpiredTokensJob` for expiring past-due grant entries.
12
+ - Automatic store swap via Railtie — no manual wiring required.
13
+ - Standard ask-gem infrastructure: CI, rubocop, overcommit.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaka Ruto
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.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # ask-token-usage-rails
2
+
3
+ ActiveRecord persistence for the [ask-token-usage](https://github.com/ask-rb/ask-token-usage) wallet engine.
4
+
5
+ Ships an ActiveRecord store adapter, a `has_token_wallet` concern, install generator with migrations, and an expiry sweep job.
6
+
7
+ ## Installation
8
+
9
+ ```ruby
10
+ # Gemfile
11
+ gem "ask-token-usage-rails"
12
+ ```
13
+
14
+ ```sh
15
+ bundle install
16
+ rails g ask_token_usage:install
17
+ rails db:migrate
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ class User < ApplicationRecord
24
+ has_token_wallet
25
+ end
26
+
27
+ # Grant tokens
28
+ user.grant_tokens!(10_000, reason: :trial, expires_at: 7.days.from_now)
29
+
30
+ # Spend on an activity (charges only if the block succeeds)
31
+ user.spend_tokens_on!(:chat_message, input: "hi", output: "yo") do
32
+ LLM.chat(...)
33
+ end
34
+
35
+ # Check balance
36
+ user.token_balance # => 9_998
37
+
38
+ # Use the PORO wallet directly
39
+ wallet = user.ask_token_wallet
40
+ wallet.entries
41
+ wallet.used_since(30.days.ago)
42
+ ```
43
+
44
+ ## Scheduled jobs
45
+
46
+ `SweepExpiredTokensJob` expires grant entries whose `expires_at` has passed.
47
+
48
+ ```yaml
49
+ # config/recurring.yml (Solid Queue)
50
+ ask_token_usage_sweep:
51
+ class: Ask::TokenUsage::Rails::SweepExpiredTokensJob
52
+ schedule: "every 1 hour"
53
+ ```
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Ask
6
+ module TokenUsage
7
+ module Rails
8
+ # Include this concern in any ActiveRecord model to give it a token
9
+ # wallet backed by ask-token-usage-rails.
10
+ #
11
+ # class User < ApplicationRecord
12
+ # has_token_wallet
13
+ # end
14
+ #
15
+ # Adds:
16
+ # user.token_balance # Integer
17
+ # user.token_wallet # Ask::TokenUsage::TokenWallet (lazy-created)
18
+ # user.token_transactions # AR scope for the ledger
19
+ # user.grant_tokens!(...) # convenience
20
+ # user.spend_tokens!(...) # convenience
21
+ # user.spend_tokens_on!(...)# convenience (activity name + block)
22
+ #
23
+ module HasTokenWallet
24
+ extend ActiveSupport::Concern
25
+
26
+ included do
27
+ has_one :token_wallet, as: :owner,
28
+ class_name: "Ask::TokenUsage::TokenWallet",
29
+ dependent: :destroy,
30
+ inverse_of: :owner
31
+
32
+ has_many :token_transactions, through: :token_wallet
33
+ end
34
+
35
+ def ensure_token_wallet!
36
+ # Force wallet row creation by probing balance through the AR store
37
+ Ask::TokenUsage.wallet_for(self).balance
38
+ end
39
+
40
+ def token_balance
41
+ Ask::TokenUsage.wallet_for(self).balance
42
+ end
43
+
44
+ # Build an Ask::TokenUsage::Wallet PORO backed by this model's
45
+ # AR wallet — use for one-off operations outside the concern API.
46
+ def ask_token_wallet
47
+ Ask::TokenUsage.wallet_for(self)
48
+ end
49
+
50
+ # Grant +amount+ tokens.
51
+ def grant_tokens!(amount, reason:, expires_at: nil, metadata: {})
52
+ ask_token_wallet.grant!(amount, reason: reason, expires_at: expires_at, metadata: metadata)
53
+ end
54
+
55
+ # Deduct +amount+ tokens, raising InsufficientTokens on failure.
56
+ def deduct_tokens!(amount, reason:, metadata: {})
57
+ ask_token_wallet.deduct!(amount, reason: reason, metadata: metadata)
58
+ end
59
+
60
+ # Deduct +amount+ tokens; returns false instead of raising on
61
+ # insufficient balance.
62
+ def try_deduct_tokens!(amount, reason:, metadata: {})
63
+ deduct_tokens!(amount, reason: reason, metadata: metadata)
64
+ rescue Ask::TokenUsage::InsufficientTokens
65
+ false
66
+ end
67
+
68
+ # Spend the estimated cost of +activity+ with +params+, charging
69
+ # only when the block succeeds.
70
+ def spend_tokens_on!(activity_name, params = {}, &block)
71
+ ask_token_wallet.spend!(activity_name, params, &block)
72
+ end
73
+
74
+ # Spend an explicit +amount+ of tokens, charging only on block success.
75
+ def spend_tokens!(amount, reason:, metadata: {}, &block)
76
+ ask_token_wallet.deduct!(amount, reason: reason, metadata: metadata, &block)
77
+ end
78
+
79
+ def has_tokens_for?(amount)
80
+ ask_token_wallet.has?(amount)
81
+ end
82
+
83
+ def token_usage_since(time)
84
+ ask_token_wallet.used_since(time)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_job"
4
+
5
+ module Ask
6
+ module TokenUsage
7
+ module Rails
8
+ # Expire grant entries whose +expires_at+ has passed. Scheduled as a
9
+ # recurring job (Solid Queue cron, Sidekiq Cron, etc.):
10
+ #
11
+ # # config/recurring.yml
12
+ # ask_token_usage_sweep:
13
+ # class: Ask::TokenUsage::Rails::SweepExpiredTokensJob
14
+ # schedule: "every 1 hour"
15
+ #
16
+ # For each expired grant, the actual amount removed is capped to the
17
+ # wallet's current balance (myrr approximation — correct for most
18
+ # grant-heavy usage patterns; perfect accuracy requires FIFO allocation
19
+ # which is a future option).
20
+ #
21
+ class SweepExpiredTokensJob < ActiveJob::Base
22
+ queue_as :default
23
+
24
+ def perform(now: Time.current)
25
+ Ask::TokenUsage::TokenTransaction
26
+ .grants
27
+ .where("expires_at IS NOT NULL AND expires_at <= ?", now)
28
+ .where.not(entry_type: "expiry")
29
+ .where("id NOT IN (SELECT source_transaction_id FROM token_transactions WHERE entry_type = ?)", "expiry")
30
+ .find_each do |grant|
31
+ sweep_grant(grant)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def sweep_grant(grant)
38
+ wallet = grant.token_wallet
39
+ return unless wallet
40
+
41
+ wallet.with_lock do
42
+ remaining = grant.amount - already_expired_amount(grant)
43
+ return if remaining <= 0
44
+
45
+ actual = [remaining, wallet.balance].min
46
+ return if actual <= 0
47
+
48
+ Ask::TokenUsage::TokenTransaction.create!(
49
+ token_wallet_id: wallet.id,
50
+ entry_type: "expiry",
51
+ amount: -actual,
52
+ reason: "token_expiry",
53
+ balance: wallet.balance - actual,
54
+ metadata: {
55
+ source_transaction_id: grant.id,
56
+ expired_amount: remaining,
57
+ actual_expired: actual
58
+ },
59
+ created_at: now
60
+ )
61
+
62
+ wallet.update_column(:balance, wallet.balance - actual)
63
+ end
64
+ end
65
+
66
+ def already_expired_amount(grant)
67
+ Ask::TokenUsage::TokenTransaction
68
+ .where(token_wallet_id: grant.token_wallet_id, entry_type: "expiry")
69
+ .where("metadata->>'source_transaction_id' = ?", grant.id.to_s)
70
+ .sum(:amount)
71
+ .abs
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Ask
6
+ module TokenUsage
7
+ # Append-only ledger row. Every grant, debit, adjustment, or expiry is
8
+ # written here as an immutable record. Never updated or deleted.
9
+ class TokenTransaction < ::ActiveRecord::Base
10
+ self.table_name = "token_transactions"
11
+
12
+ belongs_to :token_wallet, class_name: "Ask::TokenUsage::TokenWallet",
13
+ foreign_key: :token_wallet_id,
14
+ inverse_of: :token_transactions
15
+
16
+ ENTRY_TYPES = %w[grant debit adjustment expiry].freeze
17
+ validates :entry_type, presence: true, inclusion: { in: ENTRY_TYPES }
18
+ validates :amount, presence: true, numericality: { only_integer: true }
19
+ validates :reason, presence: true
20
+
21
+ scope :grants, -> { where(entry_type: "grant") }
22
+ scope :debits, -> { where(entry_type: "debit") }
23
+ scope :adjustments, -> { where(entry_type: "adjustment") }
24
+ scope :expiries, -> { where(entry_type: "expiry") }
25
+ scope :since, ->(time) { where("created_at >= ?", time) }
26
+ scope :newest_first, -> { order(created_at: :desc) }
27
+
28
+ validate :immutable_after_creation, on: :update
29
+
30
+ private
31
+
32
+ def immutable_after_creation
33
+ errors.add(:base, "Token transactions are append-only")
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Ask
6
+ module TokenUsage
7
+ # Polymorphic wallet record. One per owner. Holds the cached balance.
8
+ class TokenWallet < ::ActiveRecord::Base
9
+ self.table_name = "token_wallets"
10
+
11
+ has_many :token_transactions, class_name: "Ask::TokenUsage::TokenTransaction",
12
+ foreign_key: :token_wallet_id,
13
+ dependent: :destroy,
14
+ inverse_of: :token_wallet
15
+
16
+ belongs_to :owner, polymorphic: true, optional: true
17
+
18
+ validates :balance, numericality: { only_integer: true }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/railtie"
4
+
5
+ module Ask
6
+ module TokenUsage
7
+ module Rails
8
+ class Railtie < ::Rails::Railtie
9
+ initializer "ask_token_usage.active_record_store" do
10
+ require "ask/token_usage/rails/models/token_wallet"
11
+ require "ask/token_usage/rails/models/token_transaction"
12
+ require "ask/token_usage/rails/stores/active_record_store"
13
+ require "ask/token_usage/rails/concerns/has_token_wallet"
14
+
15
+ Ask::TokenUsage.configure do |c|
16
+ c.store = Ask::TokenUsage::Rails::ActiveRecordStore.new
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ask/token_usage/stores/store"
4
+
5
+ module Ask
6
+ module TokenUsage
7
+ module Rails
8
+ # ActiveRecord-backed wallet store. Each mutation (grant/deduct) does
9
+ # its own read-write under a single DB call. The wallet row is created
10
+ # lazily on first access. No outer transaction wrapping — each AR
11
+ # create/update handles its own atomicity.
12
+ class ActiveRecordStore < Ask::TokenUsage::Stores::Store
13
+ def balance(owner)
14
+ resolve_wallet(owner)&.balance || 0
15
+ end
16
+
17
+ def entries(owner, kind: nil, since: nil)
18
+ row = resolve_wallet(owner)
19
+ return [] unless row
20
+
21
+ scope = row.token_transactions.order(:created_at)
22
+ scope = scope.where(entry_type: kind.to_s) if kind
23
+ scope = scope.where("created_at >= ?", since) if since
24
+ scope.map { |t| to_entry(t) }
25
+ end
26
+
27
+ # No-op for in-process locking. For production with PostgreSQL,
28
+ # this could use row-level locks. SQLite uses file-level locks
29
+ # which are handled by the OS.
30
+ def with_lock(owner)
31
+ yield
32
+ end
33
+
34
+ def append(owner, entry)
35
+ row = find_or_create_wallet!(owner)
36
+ txn = Ask::TokenUsage::TokenTransaction.create!(
37
+ token_wallet_id: row.id,
38
+ entry_type: entry.kind.to_s,
39
+ amount: entry.amount,
40
+ reason: entry.reason,
41
+ metadata: entry.metadata || {},
42
+ expires_at: entry.expires_at,
43
+ balance: entry.balance,
44
+ created_at: entry.created_at
45
+ )
46
+ entry.with(id: txn.id)
47
+ end
48
+
49
+ def write_balance(owner, amount)
50
+ row = find_or_create_wallet!(owner)
51
+ row.update_column(:balance, amount)
52
+ end
53
+
54
+ private
55
+
56
+ def find_or_create_wallet!(owner)
57
+ owner_type, owner_id = resolve_owner(owner)
58
+ Ask::TokenUsage::TokenWallet.find_by(owner_type: owner_type, owner_id: owner_id) ||
59
+ begin
60
+ Ask::TokenUsage::TokenWallet.create!(owner_type: owner_type, owner_id: owner_id, balance: 0)
61
+ rescue ActiveRecord::RecordNotUnique
62
+ Ask::TokenUsage::TokenWallet.find_by!(owner_type: owner_type, owner_id: owner_id)
63
+ end
64
+ end
65
+
66
+ def resolve_wallet(owner)
67
+ owner_type, owner_id = resolve_owner(owner)
68
+ Ask::TokenUsage::TokenWallet.find_by(owner_type: owner_type, owner_id: owner_id)
69
+ end
70
+
71
+ def resolve_owner(owner)
72
+ if owner.respond_to?(:id) && owner.respond_to?(:class) && owner.class.respond_to?(:polymorphic_name)
73
+ [owner.class.polymorphic_name, owner.id]
74
+ else
75
+ [owner.class.name, owner.respond_to?(:id) ? owner.id : owner.to_s]
76
+ end
77
+ end
78
+
79
+ def to_entry(txn)
80
+ Ask::TokenUsage::LedgerEntry.new(
81
+ id: txn.id,
82
+ kind: txn.entry_type.to_sym,
83
+ amount: txn.amount,
84
+ reason: txn.reason,
85
+ expires_at: txn.expires_at,
86
+ metadata: txn.metadata,
87
+ balance: txn.balance,
88
+ created_at: txn.created_at
89
+ )
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module TokenUsage
5
+ module Rails
6
+ VERSION = "0.1.0"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ask-token-usage"
4
+ require "ask/token_usage/rails/version"
5
+ require "ask/token_usage/rails/railtie"
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/migration"
5
+
6
+ module AskTokenUsage
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ include Rails::Generators::Migration
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ desc "Creates the ask-token-usage migration and initializer"
13
+
14
+ def self.next_migration_number(_dir)
15
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
16
+ end
17
+
18
+ def create_migration
19
+ migration_template "migration.rb", "db/migrate/create_token_wallets.rb"
20
+ end
21
+
22
+ def create_initializer
23
+ template "initializer.rb", "config/initializers/ask_token_usage.rb"
24
+ end
25
+
26
+ def show_readme
27
+ readme "README.md" if behavior == :invoke
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ask-token-usage configuration.
4
+ # The railtie auto-wires the ActiveRecord store. Override settings here.
5
+
6
+ Ask::TokenUsage.configure do |config|
7
+ # What 1M billing tokens cost (the rate your users pay).
8
+ # config.price_per_1m = Money.from_amount(100, "USD") # $100 / 1M
9
+
10
+ # Rounding strategy for dynamic token costs: :ceil (default), :floor, :round.
11
+ # config.rounding = :ceil
12
+
13
+ # Allow negative balances? Default: false.
14
+ # config.negatives = false
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateTokenWallets < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :token_wallets do |t|
6
+ t.references :owner, polymorphic: true, null: false
7
+ t.bigint :balance, null: false, default: 0
8
+ t.timestamps
9
+ end
10
+ add_index :token_wallets, %i[owner_type owner_id], unique: true, name: "idx_token_wallets_owner"
11
+
12
+ create_table :token_transactions do |t|
13
+ t.references :token_wallet, null: false, foreign_key: true
14
+ t.string :entry_type, null: false
15
+ t.bigint :amount, null: false
16
+ t.string :reason, null: false
17
+ t.jsonb :metadata, null: false, default: {}
18
+ t.bigint :balance, null: false
19
+ t.datetime :expires_at
20
+ t.datetime :created_at, null: false
21
+ end
22
+ add_index :token_transactions, %i[token_wallet_id created_at]
23
+ add_index :token_transactions, :entry_type
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ask-token-usage-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kaka Ruto
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ask-token-usage
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.1.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 0.1.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: rails
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: minitest
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.25'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5.25'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '13.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '13.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: sqlite3
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ description: Adds an ActiveRecord-backed wallet store, has_token_wallet concern, token
83
+ wallet/transaction models, an install generator with migrations, and an expiry sweep
84
+ job to any Rails app using ask-token-usage.
85
+ email:
86
+ - kaka@myrrlabs.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - CHANGELOG.md
92
+ - LICENSE
93
+ - README.md
94
+ - lib/ask-token-usage-rails.rb
95
+ - lib/ask/token_usage/rails/concerns/has_token_wallet.rb
96
+ - lib/ask/token_usage/rails/jobs/sweep_expired_tokens_job.rb
97
+ - lib/ask/token_usage/rails/models/token_transaction.rb
98
+ - lib/ask/token_usage/rails/models/token_wallet.rb
99
+ - lib/ask/token_usage/rails/railtie.rb
100
+ - lib/ask/token_usage/rails/stores/active_record_store.rb
101
+ - lib/ask/token_usage/rails/version.rb
102
+ - lib/generators/ask_token_usage/install/install_generator.rb
103
+ - lib/generators/ask_token_usage/install/templates/initializer.rb
104
+ - lib/generators/ask_token_usage/install/templates/migration.rb
105
+ homepage: https://github.com/ask-rb/ask-token-usage-rails
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ homepage_uri: https://github.com/ask-rb/ask-token-usage-rails
110
+ source_code_uri: https://github.com/ask-rb/ask-token-usage-rails
111
+ changelog_uri: https://github.com/ask-rb/ask-token-usage-rails/blob/main/CHANGELOG.md
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '3.2'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubygems_version: 4.0.18
127
+ specification_version: 4
128
+ summary: ActiveRecord persistence for the ask-token-usage wallet engine
129
+ test_files: []