toolx 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f027d545a91be1a02f8ef28f52ced20c6d6ffe3defc99be3f59dd783b78bed2f
4
- data.tar.gz: 453e20a3f42da721d4acdb50fb62de95ad62b4801c6c652c3285c8a5de311a21
3
+ metadata.gz: 6fc01244224cb4f82dd203b789591041faf4d0e155236d2aa68a1f6df3b418d8
4
+ data.tar.gz: 77e069e564822a8a91050bac2bba8b3e76d4c36c460852fa29219af9e7c4a32c
5
5
  SHA512:
6
- metadata.gz: 9551fd33fe000bf1a10e5500a409f770ea64533b3db892825174f96659773fa93b872b78f21cb7dbdfc575045e54024bddb18e499b96417c36a1edf0a931a3b4
7
- data.tar.gz: '018ed1cadf76d903502d6e1b5fdd1c3bc14d3891995d35d2566fb36c2c15ffbd59ea04c6af87d46f7c876cbf840a5f903b3b2eae8b6d43e3afafa3738ee74de1'
6
+ metadata.gz: b6cf65409dfd5aebac40a2ce888ebc2cd71d426849df88624323d6a7dc8f2c6dd25317852d320dee7036930841dd3bc8d92584af7b5607dfb78b2104252d1d6b
7
+ data.tar.gz: 1c67d81bd13da36a9e3c745939f006119fdf8f03449cdf069fada40901b9e460c054b77979d1d79d907eb42b01073756fa0336c3fe3c2b258d77a837a18d8986
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).
@@ -2,7 +2,7 @@ namespace :toolx do
2
2
  namespace :aliases do
3
3
  desc 'Generate toolx_aliases.rb'
4
4
  task :generate do
5
- file_name = Rails.root.join('lib', 'toolx_aliases.rb')
5
+ file_name = Rails.root.join('config', 'initializers', 'toolx_aliases.rb')
6
6
  return if File.exist?(file_name)
7
7
 
8
8
  File.open(file_name, 'w') do |file|
@@ -14,6 +14,8 @@ namespace :toolx do
14
14
  # DateTimeToBoolean = Toolx::Core::Concerns::DateTimeToBoolean
15
15
  # CustomIdentifier = Toolx::Core::Concerns::CustomIdentifier
16
16
  # Transformer = Toolx::Core::Concerns::Transformer
17
+ # Keygen = Toolx::Core::Concerns::Keygen
18
+ # Tokenizer = Toolx::Core::Concerns::Tokenizer
17
19
 
18
20
  NestedError = Toolx::Core::Errors::NestedError
19
21
  NestedStandardError = Toolx::Core::Errors::NestedStandardError
@@ -40,11 +42,13 @@ namespace :toolx do
40
42
  include Toolx::Core::Concerns::CustomIdentifier
41
43
  include Toolx::Core::Concerns::DateTimeToBoolean
42
44
  include Toolx::Core::Concerns::WithStateMachine
45
+ include Toolx::Core::Concerns::Keygen
46
+ include Toolx::Core::Concerns::Tokenizer
43
47
  end
44
48
  RUBY
45
49
  puts "\n"
46
50
  puts "Add line below to config/application.rb"
47
- puts "require_relative '../lib/toolx_aliases'"
51
+ puts "require_relative 'initializers/toolx_aliases'"
48
52
  end
49
53
  end
50
54
  end
@@ -3,7 +3,14 @@ namespace :toolx do
3
3
  desc 'Generate annotate rake tasks'
4
4
  task :generate do
5
5
  file_name = Rails.root.join('lib', 'tasks', 'annotate_rb.rake')
6
- return if File.exist?(file_name)
6
+ next puts "Already executed" if File.exist?(file_name)
7
+
8
+ template_root_path = File.expand_path("../templates", __dir__)
9
+ config_name = '.annotaterb.yml'
10
+ basic_configuration = File.read(File.join(template_root_path, config_name), trim_mode: '-')
11
+ File.write(Rails.root.join(config_name).to_s, basic_configuration)
12
+
13
+ puts "Basic configuration for annotate_rb gem created at #{config_name}"
7
14
 
8
15
  File.open(file_name, 'w') do |file|
9
16
  file.write <<~RUBY
@@ -0,0 +1,58 @@
1
+ ---
2
+ :position: before
3
+ :position_in_additional_file_patterns: before
4
+ :position_in_class: after
5
+ :position_in_factory: after
6
+ :position_in_fixture: after
7
+ :position_in_routes: after
8
+ :position_in_serializer: after
9
+ :position_in_test: after
10
+ :classified_sort: true
11
+ :exclude_controllers: true
12
+ :exclude_factories: false
13
+ :exclude_fixtures: true
14
+ :exclude_helpers: true
15
+ :exclude_scaffolds: true
16
+ :exclude_serializers: false
17
+ :exclude_sti_subclasses: false
18
+ :exclude_tests: true
19
+ :force: false
20
+ :format_markdown: false
21
+ :format_rdoc: false
22
+ :format_yard: false
23
+ :frozen: false
24
+ :ignore_model_sub_dir: false
25
+ :ignore_unknown_models: false
26
+ :include_version: false
27
+ :show_check_constraints: false
28
+ :show_complete_foreign_keys: false
29
+ :show_foreign_keys: true
30
+ :show_indexes: true
31
+ :simple_indexes: false
32
+ :sort: true
33
+ :timestamp: false
34
+ :trace: false
35
+ :with_comment: true
36
+ :with_column_comments: true
37
+ :with_table_comments: true
38
+ :active_admin: false
39
+ :command:
40
+ :debug: false
41
+ :hide_default_column_types: ''
42
+ :hide_limit_column_types: ''
43
+ :ignore_columns:
44
+ :ignore_routes:
45
+ :models: true
46
+ :routes: false
47
+ :skip_on_db_migrate: false
48
+ :target_action: :do_annotations
49
+ :wrapper:
50
+ :wrapper_close:
51
+ :wrapper_open:
52
+ :classes_default_to_s: []
53
+ :additional_file_patterns: []
54
+ :model_dir:
55
+ - app/models
56
+ :require: []
57
+ :root_dir:
58
+ - ''
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Toolx
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.1'
5
5
  end
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.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pawel Niemczyk
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-06-24 00:00:00.000000000 Z
10
+ date: 2025-06-26 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: statesman
@@ -281,6 +281,7 @@ files:
281
281
  - lib/tasks/aliases.rake
282
282
  - lib/tasks/annotate_rb.rake
283
283
  - lib/tasks/stateman.rake
284
+ - lib/templates/.annotaterb.yml
284
285
  - lib/templates/stateman/migration.rb.erb
285
286
  - lib/templates/stateman/state_machine.rb.erb
286
287
  - lib/templates/stateman/transition.rb.erb
@@ -288,6 +289,8 @@ files:
288
289
  - lib/toolx/core/concerns/custom_identifier.rb
289
290
  - lib/toolx/core/concerns/date_time_to_boolean.rb
290
291
  - lib/toolx/core/concerns/inquirer.rb
292
+ - lib/toolx/core/concerns/keygen.rb
293
+ - lib/toolx/core/concerns/tokenizer.rb
291
294
  - lib/toolx/core/concerns/transformer.rb
292
295
  - lib/toolx/core/concerns/with_state_machine.rb
293
296
  - lib/toolx/core/env.rb
@@ -330,7 +333,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
330
333
  - !ruby/object:Gem::Version
331
334
  version: '0'
332
335
  requirements: []
333
- rubygems_version: 3.6.2
336
+ rubygems_version: 3.6.3
334
337
  specification_version: 4
335
338
  summary: Toolx is a set of tools for Ruby on Rails applications that simplifies common
336
339
  tasks and enhances development experience.