simple_wallet 0.1.3 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d40dd6572b85931bc243f759cc5ff11c0a59eedc6260bbea55fb7687c28f4d39
4
- data.tar.gz: ff5f84546d807d2e1a2548ba15f92100b051d66f9a9787246d07a924c07cff9c
3
+ metadata.gz: ebc4b7ebda60b08431cef96068379c38a985839111b6a8218135e0e59f1bb849
4
+ data.tar.gz: a7bea423434fbde68665ce565e0b6b391bc3778a283b14336291153d2b147e41
5
5
  SHA512:
6
- metadata.gz: 13ae43936eba505ba853c47c996e4077b0f2c707080cfa2b89f0ce923b477408be49ec8e385e5fe7f96b81188acf400689f5e5d720ea9cd26909f2ce1014b7cc
7
- data.tar.gz: 553dcb7358aad4469d10db3623e382cba8e21f942e4780b1b2647a42a8e5843b3f2a294da76e217f0fbd2ca28e30002af4dd5f320be9c83527f4394b401e01ef
6
+ metadata.gz: e9688a816ff0d6dac7c22437c20f5e6b098fa9bc818fa351730b19e1866ed7284e210808396b06bd87b76e279e00161f337c695bc1d7af8f510089e9b9225f08
7
+ data.tar.gz: 1dc73e2237ef08115901ba9d80a17529a64145b3a4e455d71fd74f6c1e100acfa3640be4149ada0f6c604029536d6e507e4fb09fe033aa33bf66815af9f11220
data/README.md CHANGED
@@ -1,28 +1,258 @@
1
1
  # SimpleWallet
2
- Short description and motivation.
3
2
 
4
- ## Usage
5
- How to use my plugin.
3
+ A lightweight, drop-in wallet engine for Ruby on Rails apps. `simple_wallet`
4
+ adds a balance ledger to any of your ActiveRecord models — users, accounts,
5
+ organisations — letting you credit, debit, and query balances with minimal
6
+ setup.
7
+
8
+ ## Features
9
+
10
+ - Attach a wallet to any ActiveRecord model with a few lines of code
11
+ - Credit and debit operations backed by a double-entry-style transaction log
12
+ - Query current balance and full transaction history per wallet owner
13
+ - Database-backed — no external services required
14
+ - Rails engine: migrations and models are mounted directly into your app
15
+
16
+ ## Requirements
17
+
18
+ - Ruby >= 3.0
19
+ - Rails >= 7.0
20
+ - PostgreSQL
6
21
 
7
22
  ## Installation
8
- Add this line to your application's Gemfile:
9
23
 
24
+ Add the gem to your `Gemfile`:
10
25
  ```ruby
11
26
  gem "simple_wallet"
12
27
  ```
13
28
 
14
- And then execute:
29
+ Install it:
30
+ ```bash
31
+ bundle install
32
+ ```
33
+
34
+ Copy and run the migrations:
15
35
  ```bash
16
- $ bundle
36
+ bin/rails simple_wallet_engine:install:migrations
37
+ bin/rails db:migrate db:test:prepare
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ### Attaching a wallet to a model
43
+
44
+ ```bash
45
+ bin/rails g migration add_account_to_users simple_wallet_account:references:uniq
46
+ ```
47
+
48
+ Tweak the migration to allow null values (if you have existing records):
49
+
50
+ ```ruby
51
+ class AddAccountToUsers < ActiveRecord::Migration[8.0]
52
+ def change
53
+ add_reference :users, :simple_wallet_account, index: {unique: true}, null: true, foreign_key: true
54
+ end
55
+ end
17
56
  ```
18
57
 
19
- Or install it yourself as:
58
+ Run migrations:
59
+
20
60
  ```bash
21
- $ gem install simple_wallet
61
+ bin/rails db:migrate db:test:prepare
62
+ ```
63
+
64
+ And finally add Account reference to your model:
65
+
66
+ ```ruby
67
+ class User < ApplicationRecord
68
+ belongs_to :account,
69
+ class_name: "::SimpleWallet::Account",
70
+ optional: true,
71
+ foreign_key: :simple_wallet_account_id
72
+
73
+ before_create :create_simple_wallet_account
74
+
75
+ private
76
+
77
+ def create_simple_wallet_account
78
+ create_account!
79
+ end
80
+ end
22
81
  ```
23
82
 
83
+ ### Querying the balance
84
+
85
+ ```ruby
86
+ user = User.create(first_name: "Marcin", last_name: "Urbanski")
87
+
88
+ user.account
89
+ =>
90
+ #<SimpleWallet::Account:0x0000710ebe38cac8
91
+ id: 1,
92
+ balance: 0,
93
+ income: 0,
94
+ outcome: 0,
95
+ created_at: "2026-04-11 09:05:41.534180000 -0700",
96
+ updated_at: "2026-04-11 09:05:41.534180000 -0700">
97
+ ```
98
+
99
+ ### Crediting
100
+ ```ruby
101
+ SimpleWallet::AccountCreditingService.new(account: user.account, amount: 1_000, source: User.admins.first, note: "Bonus!").credit
102
+
103
+ user.reload.account
104
+ =>
105
+ #<SimpleWallet::Account:0x0000710ebfc29108
106
+ id: 1,
107
+ balance: 1000,
108
+ income: 1000,
109
+ outcome: 0,
110
+ created_at: "2026-04-11 09:11:57.198448000 -0700",
111
+ updated_at: "2026-04-11 09:11:57.198448000 -0700">
112
+ ```
113
+
114
+ ### Debiting
115
+ ```ruby
116
+ SimpleWallet::AccountDebitingService.new(account: user.account, amount: 200, source: User.admins.first, note: "AI model invoice").debit
117
+
118
+ user.reload.account
119
+ =>
120
+ #<SimpleWallet::Account:0x0000710ebd85d350
121
+ id: 1,
122
+ balance: 800,
123
+ income: 1000,
124
+ outcome: -200,
125
+ created_at: "2026-04-11 09:11:57.198448000 -0700",
126
+ updated_at: "2026-04-11 09:11:57.198448000 -0700">
127
+ ```
128
+
129
+ ### Insufficient funds
130
+
131
+ ```ruby
132
+ service = SimpleWallet::AccountDebitingService.new(account: user.account, amount: 1_000_000, source: User.admins.first, note: "AI model invoice")
133
+
134
+ service.debit
135
+ => false
136
+
137
+ service.errors.messages
138
+ => {:amount=>["exceeds available balance"]}
139
+
140
+ # Debit up to account balance:
141
+ service = SimpleWallet::AccountDebitingService.new(account: user.account, amount: 1_000_000, source: User.admins.first, note: "AI model invoice", up_to_account_balance: true)
142
+
143
+ service.debit
144
+ => true
145
+
146
+ user.reload.account
147
+ =>
148
+ #<SimpleWallet::Account:0x0000710ebd8bc080
149
+ id: 5,
150
+ balance: 0,
151
+ income: 1000,
152
+ outcome: -1000,
153
+ created_at: "2026-04-11 09:11:57.198448000 -0700",
154
+ updated_at: "2026-04-11 09:11:57.198448000 -0700">
155
+
156
+ user.account.transactions
157
+ =>
158
+ [
159
+ # ...
160
+ #<SimpleWallet::Transaction::Debit:0x0000710ebd8ba3c0
161
+ id: 2,
162
+ type: "SimpleWallet::Transaction::Debit",
163
+ account_id: 1,
164
+ source_type: "User", # Admin
165
+ source_id: 11,
166
+ pre_account_balance: 800,
167
+ amount: -800, # Debited as much as we could.
168
+ note: "AI model invoice",
169
+ created_at: "2026-04-11 09:27:38.958150000 -0700",
170
+ updated_at: "2026-04-11 09:27:38.958150000 -0700">]
171
+ ```
172
+
173
+
174
+ ### Transfering between accounts
175
+ ```ruby
176
+ employer = User.create(first_name: "Rich", last_name: "Employer")
177
+ employee = User.create(first_name: "Marcin", last_name: "Urbanski")
178
+
179
+ SimpleWallet::AccountCreditingService.new(account: employer.account, amount: 1_000_000, note: "From venture capitalist").credit
180
+ SimpleWallet::TransferService.new(from: employer.reload.account, to: employee.reload.account, amount: 15, note: "We really appreciate your efforts!").transfer
181
+
182
+ employer.account.transactions.last
183
+ =>
184
+ #<SimpleWallet::Transaction::Debit:0x0000710ebf569d90
185
+ id: 6,
186
+ type: "SimpleWallet::Transaction::Debit",
187
+ account_id: 6,
188
+ source_type: nil,
189
+ source_id: nil,
190
+ pre_account_balance: 1000000,
191
+ amount: -15,
192
+ note: "We really appreciate your efforts!",
193
+ created_at: "2026-04-11 09:40:27.018567000 -0700",
194
+ updated_at: "2026-04-11 09:40:27.018567000 -0700">
195
+
196
+ employee.account.transactions.last
197
+ =>
198
+ #<SimpleWallet::Transaction::Credit:0x0000710ebf567f90
199
+ id: 7,
200
+ type: "SimpleWallet::Transaction::Credit",
201
+ account_id: 7,
202
+ source_type: nil,
203
+ source_id: nil,
204
+ pre_account_balance: 0,
205
+ amount: 15,
206
+ note: "We really appreciate your efforts!",
207
+ created_at: "2026-04-11 09:40:27.024512000 -0700",
208
+ updated_at: "2026-04-11 09:40:27.024512000 -0700">
209
+ ```
210
+
211
+ ### Transaction history
212
+ ```ruby
213
+ user.account.transactions
214
+
215
+ =>
216
+ [#<SimpleWallet::Transaction::Credit:0x0000710ebd85a510
217
+ id: 1,
218
+ type: "SimpleWallet::Transaction::Credit",
219
+ account_id: 1,
220
+ source_type: "User", # Admin
221
+ source_id: 11,
222
+ pre_account_balance: 0,
223
+ amount: 1000,
224
+ note: "Bonus!",
225
+ created_at: "2026-04-11 09:12:19.260240000 -0700",
226
+ updated_at: "2026-04-11 09:12:19.260240000 -0700">,
227
+ #<SimpleWallet::Transaction::Debit:0x0000710ebd85a3d0
228
+ id: 2,
229
+ type: "SimpleWallet::Transaction::Debit",
230
+ account_id: 1,
231
+ source_type: "User", # Admin
232
+ source_id: 11,
233
+ pre_account_balance: 1000,
234
+ amount: -200,
235
+ note: "AI model invoice",
236
+ created_at: "2026-04-11 09:15:16.248670000 -0700",
237
+ updated_at: "2026-04-11 09:15:16.248670000 -0700">]
238
+ ```
239
+
240
+
24
241
  ## Contributing
25
- Contribution directions go here.
242
+
243
+ Bug reports and pull requests are welcome on
244
+ [GitHub](https://github.com/murbanski/simple_wallet).
245
+
246
+ 1. Fork the repository
247
+ 2. Create a feature branch (`git checkout -b my-feature`)
248
+ 3. Commit your changes (`git commit -am 'Add my feature'`)
249
+ 4. Push the branch (`git push origin my-feature`)
250
+ 5. Open a Pull Request
251
+
252
+ Please run `rubocop` before submitting — the project ships with a
253
+ `.rubocop.yml`.
26
254
 
27
255
  ## License
28
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
256
+
257
+ The gem is available as open source under the terms of the
258
+ [MIT License](https://opensource.org/licenses/MIT).
@@ -21,12 +21,12 @@ module SimpleWallet
21
21
  success = ActiveRecord::Base.transaction(isolation: isolation_level) do
22
22
  return false unless valid?
23
23
 
24
- unless debiting_service.debit(set_transaction_isolation_level: set_transaction_isolation_level)
24
+ unless debiting_service.debit(set_transaction_isolation_level: false)
25
25
  copy_errors_from(debiting_service)
26
26
  return false
27
27
  end
28
28
 
29
- unless crediting_service.credit(set_transaction_isolation_level: set_transaction_isolation_level)
29
+ unless crediting_service.credit(set_transaction_isolation_level: false)
30
30
  copy_errors_from(crediting_service)
31
31
  raise ActiveRecord::Rollback
32
32
  end
@@ -1,4 +1,4 @@
1
- class CreateSimpleWalletAccount < ActiveRecord::Migration[8.1]
1
+ class CreateSimpleWalletAccount < ActiveRecord::Migration[7.0]
2
2
  def up
3
3
  create_table :simple_wallet_accounts do |t|
4
4
  t.integer :balance, null: false, default: 0
@@ -1,4 +1,4 @@
1
- class CreateSimpleWalletTransactions < ActiveRecord::Migration[8.1]
1
+ class CreateSimpleWalletTransactions < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :simple_wallet_transactions do |t|
4
4
  t.string :type, null: false, index: true
@@ -1,3 +1,3 @@
1
1
  module SimpleWallet
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -1016,3 +1016,17 @@ FOREIGN KEY ("account_id")
1016
1016
  ActiveRecord::InternalMetadata Update (0.7ms) UPDATE "ar_internal_metadata" SET "value" = 'test', "updated_at" = '2026-04-05 16:56:54.890556' WHERE "ar_internal_metadata"."key" = 'environment' /*application='Dummy'*/
1017
1017
  ActiveRecord::InternalMetadata Load (0.2ms) SELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = 'schema_sha1' ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1 /*application='Dummy'*/
1018
1018
  ActiveRecord::SchemaMigration Load (1.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC /*application='Dummy'*/
1019
+ ActiveRecord::SchemaMigration Load (0.9ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC /*application='Dummy'*/
1020
+ ActiveRecord::InternalMetadata Load (1.1ms) SELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = 'environment' ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1 /*application='Dummy'*/
1021
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC /*application='Dummy'*/
1022
+ ActiveRecord::InternalMetadata Load (0.1ms) SELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = 'environment' ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1 /*application='Dummy'*/
1023
+ ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC /*application='Dummy'*/
1024
+ ActiveRecord::InternalMetadata Load (0.1ms) SELECT * FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = 'environment' ORDER BY "ar_internal_metadata"."key" ASC LIMIT 1 /*application='Dummy'*/
1025
+ SQL (0.1ms) SET search_path TO public /*application='Dummy'*/
1026
+  (42.4ms) DROP DATABASE IF EXISTS "simple_wallet_development" /*application='Dummy'*/
1027
+ SQL (0.1ms) SET search_path TO public /*application='Dummy'*/
1028
+  (32.2ms) DROP DATABASE IF EXISTS "simple_wallet_test" /*application='Dummy'*/
1029
+ SQL (0.1ms) SET search_path TO public /*application='Dummy'*/
1030
+  (129.9ms) CREATE DATABASE "simple_wallet_development" ENCODING = 'unicode' /*application='Dummy'*/
1031
+ SQL (0.1ms) SET search_path TO public /*application='Dummy'*/
1032
+  (38.8ms) CREATE DATABASE "simple_wallet_test" ENCODING = 'unicode' /*application='Dummy'*/