toolx 0.1.0 → 0.2.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 +4 -4
- data/README.md +92 -61
- data/lib/tasks/aliases.rake +2 -0
- data/lib/toolx/core/concerns/keygen.rb +44 -0
- data/lib/toolx/core/concerns/tokenizer.rb +41 -0
- data/lib/toolx/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71e2fe435a932be8cd57480bce5c52c27e369699cbe6de4f9dacc6eed981476b
|
4
|
+
data.tar.gz: 68b6b1546a453e517c25c98359a9e00d6bcae46e01cf2331564e81f0147cbd28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a4d5785740ee58fc8ed1489b4444482c2d9b98569091d8764d1f79038057f4b3421184aba119d2968f3c839845dbf9e708a55354ba09818b58e2c4114c5dff0
|
7
|
+
data.tar.gz: 5b38acc866cff44a922234c89093ea23fe44b3c26e84ff92c215404a086940bf9993b6fc065684e29f860fcafc7935c6a79d632df80f51f4fadbdbccffb6f14d
|
data/README.md
CHANGED
@@ -26,6 +26,8 @@ class ApplicationRecord < ActiveRecord::Base
|
|
26
26
|
include Toolx::Core::Concerns::Transformer
|
27
27
|
include Toolx::Core::Concerns::CustomIdentifier
|
28
28
|
include Toolx::Core::Concerns::DateTimeToBoolean
|
29
|
+
include Toolx::Core::Concerns::Keygen
|
30
|
+
include Toolx::Core::Concerns::Tokenizer
|
29
31
|
include Toolx::Core::Concerns::WithStateMachine
|
30
32
|
end
|
31
33
|
```
|
@@ -135,6 +137,96 @@ user.deleted = true # also sets current time
|
|
135
137
|
user.deleted = false # sets deleted_at to nil
|
136
138
|
```
|
137
139
|
|
140
|
+
### Keygen
|
141
|
+
|
142
|
+
Generate unique keys for your models, ensuring they are unique across the database.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
class User < ApplicationRecord
|
146
|
+
include Toolx::Core::Concerns::Keygen
|
147
|
+
|
148
|
+
keygen :dob, attr: :lic_key, secret: 'my$ecret', limit: 20, separator: '.', br: 3
|
149
|
+
end
|
150
|
+
user = User.new(dob: '1990-01-01')
|
151
|
+
user.save!
|
152
|
+
user.lic_key # => '090.cc2.207.c45.3e7.20b.f95'
|
153
|
+
```
|
154
|
+
|
155
|
+
### Tokenizer
|
156
|
+
|
157
|
+
Generate unique tokens for your models before creation, useful for API keys, access tokens, invitations, etc.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
class AccessToken < ApplicationRecord
|
161
|
+
include Toolx::Core::Concerns::Tokenizer
|
162
|
+
|
163
|
+
tokenize :token, size: 32, prefix: 'tok'
|
164
|
+
end
|
165
|
+
|
166
|
+
token = AccessToken.create!
|
167
|
+
token.token # => "tok-xnT3yM9dPQsA4W8Ks7LZhV5RuKcgByTw"
|
168
|
+
```
|
169
|
+
|
170
|
+
### Stateman via with_state_machine
|
171
|
+
|
172
|
+
It is working only with `Statesman::Adapters::ActiveRecord`
|
173
|
+
|
174
|
+
You can generate state machine classes via rake task
|
175
|
+
|
176
|
+
```bash
|
177
|
+
bin/rails "toolx:stateman:generate[Project]"
|
178
|
+
```
|
179
|
+
command will generate models for existing `Project` model as
|
180
|
+
`Project::StateMachine` and `Project::Transition`.
|
181
|
+
|
182
|
+
also will add migraion file for those models.
|
183
|
+
|
184
|
+
Example usage:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
class Project::StateMachine
|
188
|
+
include Statesman::Machine
|
189
|
+
|
190
|
+
state :active, initial: true
|
191
|
+
state :pending
|
192
|
+
state :skipped
|
193
|
+
state :cancelled
|
194
|
+
state :done
|
195
|
+
|
196
|
+
transition from: :active, to: [:pending, :pending, :skipped, :cancelled, :done]
|
197
|
+
transition from: :pending, to: [:skipped, :cancelled, :done]
|
198
|
+
transition from: :skipped, to: [:pending]
|
199
|
+
|
200
|
+
after_transition do |model, transition|
|
201
|
+
model.update!(status: transition.to_state)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class Project::Transition < ActiveRecord::Base
|
206
|
+
belongs_to :project
|
207
|
+
|
208
|
+
attribute :most_recent, :boolean, default: false
|
209
|
+
attribute :sort_key, :integer
|
210
|
+
attribute :to_state, :string
|
211
|
+
attribute :metadata, :json, default: {}
|
212
|
+
|
213
|
+
validates :to_state, inclusion: { in: Project::StateMachine.states }
|
214
|
+
end
|
215
|
+
|
216
|
+
class Project < ActiveRecord::Base
|
217
|
+
include Toolx::Core::Concerns::WithStateMachine
|
218
|
+
STATUSES = %w[active pending done skipped cancelled].freeze
|
219
|
+
|
220
|
+
with_state_machine
|
221
|
+
|
222
|
+
attribute :name, :string
|
223
|
+
attribute :status, :string, default: 'active'
|
224
|
+
|
225
|
+
validates :name, presence: true
|
226
|
+
validates :status, inclusion: { in: STATUSES }, allow_nil: true
|
227
|
+
end
|
228
|
+
```
|
229
|
+
|
138
230
|
### Errors
|
139
231
|
|
140
232
|
A structured, extensible error-handling framework:
|
@@ -227,7 +319,6 @@ end
|
|
227
319
|
Presenter.auto_present(Article.new(title: "A very long title"))
|
228
320
|
```
|
229
321
|
|
230
|
-
|
231
322
|
### SimpleCrypt
|
232
323
|
|
233
324
|
Simple AES encryption helper using ActiveSupport::MessageEncryptor.
|
@@ -383,66 +474,6 @@ SignupOperation.flow
|
|
383
474
|
.perform!(email: 'foo@bar.com', age: 20)
|
384
475
|
```
|
385
476
|
|
386
|
-
### Stateman via with_state_machine
|
387
|
-
|
388
|
-
It is working only with `Statesman::Adapters::ActiveRecord`
|
389
|
-
|
390
|
-
You can generate state machine classes via rake task
|
391
|
-
|
392
|
-
```bash
|
393
|
-
bin/rails "toolx:stateman:generate[Project]"
|
394
|
-
```
|
395
|
-
command will generate models for existing `Project` model as
|
396
|
-
`Project::StateMachine` and `Project::Transition`.
|
397
|
-
|
398
|
-
also will add migraion file for those models.
|
399
|
-
|
400
|
-
Example usage:
|
401
|
-
|
402
|
-
```ruby
|
403
|
-
class Project::StateMachine
|
404
|
-
include Statesman::Machine
|
405
|
-
|
406
|
-
state :active, initial: true
|
407
|
-
state :pending
|
408
|
-
state :skipped
|
409
|
-
state :cancelled
|
410
|
-
state :done
|
411
|
-
|
412
|
-
transition from: :active, to: [:pending, :pending, :skipped, :cancelled, :done]
|
413
|
-
transition from: :pending, to: [:skipped, :cancelled, :done]
|
414
|
-
transition from: :skipped, to: [:pending]
|
415
|
-
|
416
|
-
after_transition do |model, transition|
|
417
|
-
model.update!(status: transition.to_state)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
class Project::Transition < ActiveRecord::Base
|
422
|
-
belongs_to :project
|
423
|
-
|
424
|
-
attribute :most_recent, :boolean, default: false
|
425
|
-
attribute :sort_key, :integer
|
426
|
-
attribute :to_state, :string
|
427
|
-
attribute :metadata, :json, default: {}
|
428
|
-
|
429
|
-
validates :to_state, inclusion: { in: Project::StateMachine.states }
|
430
|
-
end
|
431
|
-
|
432
|
-
class Project < ActiveRecord::Base
|
433
|
-
include Toolx::Core::Concerns::WithStateMachine
|
434
|
-
STATUSES = %w[active pending done skipped cancelled].freeze
|
435
|
-
|
436
|
-
with_state_machine
|
437
|
-
|
438
|
-
attribute :name, :string
|
439
|
-
attribute :status, :string, default: 'active'
|
440
|
-
|
441
|
-
validates :name, presence: true
|
442
|
-
validates :status, inclusion: { in: STATUSES }, allow_nil: true
|
443
|
-
end
|
444
|
-
```
|
445
|
-
|
446
477
|
## License
|
447
478
|
|
448
479
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/tasks/aliases.rake
CHANGED
@@ -40,6 +40,8 @@ namespace :toolx do
|
|
40
40
|
include Toolx::Core::Concerns::CustomIdentifier
|
41
41
|
include Toolx::Core::Concerns::DateTimeToBoolean
|
42
42
|
include Toolx::Core::Concerns::WithStateMachine
|
43
|
+
include Toolx::Core::Concerns::Keygen
|
44
|
+
include Toolx::Core::Concerns::Tokenizer
|
43
45
|
end
|
44
46
|
RUBY
|
45
47
|
puts "\n"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Toolx
|
4
|
+
module Core
|
5
|
+
module Concerns
|
6
|
+
module Keygen
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
# Generates a unique key based on the provided attributes and an optional secret.
|
11
|
+
# @example
|
12
|
+
# class License < ApplicationRecord
|
13
|
+
# include Keygen
|
14
|
+
# keygen ->(m) { [m.id, m.format_date(m.expire_at), m.type, m.user_id] }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# license = License.new(id: '1234', expire_at: Time.current + 1.month, type: 'default', user_id: 'usr_5678')
|
18
|
+
# license.key # => "7034-d544-f8d1-fddf-d939-14ce-53c5-3ed9-f71f-3dab"
|
19
|
+
#
|
20
|
+
# @param source [Array<Symbol, Proc>] Attributes or a Proc to generate the key from.
|
21
|
+
# @param attr [Symbol] The attribute to store the generated key in (default)
|
22
|
+
# @param secret [String, nil] An optional secret to include in the key generation.
|
23
|
+
# @param limit [Integer] The maximum length of the generated key (default: 40).
|
24
|
+
# @param separator [String] The separator to use in the generated key (default: '-').
|
25
|
+
# @param br [Integer] The number of characters in each segment of the key (default: 4).
|
26
|
+
def keygen(*source, attr: :key, secret: nil, limit: 40, separator: '-', br: 4)
|
27
|
+
after_save do
|
28
|
+
next if source.blank?
|
29
|
+
|
30
|
+
binding = source.first.class == Proc
|
31
|
+
data = binding ? source.first.call(self) : source.map { |attr| send(attr).to_s }
|
32
|
+
value = (data.push(secret.to_s)).compact.join
|
33
|
+
result = Digest::SHA256.hexdigest(value)[0..limit].scan(/.{#{br}}/).join(separator)
|
34
|
+
|
35
|
+
next if send(attr) == result
|
36
|
+
|
37
|
+
update_column(attr, result)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Toolx
|
4
|
+
module Core
|
5
|
+
module Concerns
|
6
|
+
module Tokenizer
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
# Generates a unique token for a given attribute before creation.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# class AccessToken < ApplicationRecord
|
14
|
+
# include Toolx::Core::Concerns::Tokenizer
|
15
|
+
# tokenize :token, size: 32, prefix: 'tok'
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# token = AccessToken.create
|
19
|
+
# token.token # => "tok-MH3yTbcuZJ8o..."
|
20
|
+
#
|
21
|
+
# @param attribute [Symbol] The attribute to assign the token to.
|
22
|
+
# @param size [Integer] The size of the randomly generated token (default: 64).
|
23
|
+
# @param prefix [String, nil] Optional prefix to prepend to the token.
|
24
|
+
def tokenize(attribute, size: 64, prefix: nil)
|
25
|
+
before_create do
|
26
|
+
token_value = send(attribute)
|
27
|
+
next unless token_value.nil?
|
28
|
+
|
29
|
+
loop do
|
30
|
+
unique = SecureRandom.base58(size)
|
31
|
+
|
32
|
+
send("#{attribute}=", [prefix, unique].compact.join('-'))
|
33
|
+
break unless self.class.exists?(token_value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/toolx/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toolx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pawel Niemczyk
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-06-
|
10
|
+
date: 2025-06-25 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: statesman
|
@@ -288,6 +288,8 @@ files:
|
|
288
288
|
- lib/toolx/core/concerns/custom_identifier.rb
|
289
289
|
- lib/toolx/core/concerns/date_time_to_boolean.rb
|
290
290
|
- lib/toolx/core/concerns/inquirer.rb
|
291
|
+
- lib/toolx/core/concerns/keygen.rb
|
292
|
+
- lib/toolx/core/concerns/tokenizer.rb
|
291
293
|
- lib/toolx/core/concerns/transformer.rb
|
292
294
|
- lib/toolx/core/concerns/with_state_machine.rb
|
293
295
|
- lib/toolx/core/env.rb
|