rails_type_id 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.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +25 -0
  3. data/.ruby-version +1 -0
  4. data/README.md +46 -0
  5. data/Rakefile +16 -0
  6. data/lib/rails_type_id/concern.rb +129 -0
  7. data/lib/rails_type_id/require.rb +6 -0
  8. data/lib/rails_type_id/test_helper.rb +16 -0
  9. data/lib/rails_type_id/version.rb +6 -0
  10. data/lib/rails_type_id.rb +9 -0
  11. data/lib/tapioca/dsl/compilers/rails_type_id_compiler.rb +49 -0
  12. data/sig/rails_type_id.rbs +4 -0
  13. data/sorbet/config +4 -0
  14. data/sorbet/rbi/annotations/.gitattributes +1 -0
  15. data/sorbet/rbi/annotations/actionmailer.rbi +10 -0
  16. data/sorbet/rbi/annotations/actionpack.rbi +430 -0
  17. data/sorbet/rbi/annotations/actionview.rbi +75 -0
  18. data/sorbet/rbi/annotations/activejob.rbi +44 -0
  19. data/sorbet/rbi/annotations/activemodel.rbi +89 -0
  20. data/sorbet/rbi/annotations/activerecord.rbi +98 -0
  21. data/sorbet/rbi/annotations/activesupport.rbi +468 -0
  22. data/sorbet/rbi/annotations/globalid.rbi +30 -0
  23. data/sorbet/rbi/annotations/minitest.rbi +119 -0
  24. data/sorbet/rbi/annotations/railties.rbi +61 -0
  25. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  26. data/sorbet/rbi/dsl/.gitattributes +1 -0
  27. data/sorbet/rbi/dsl/active_model/validations/callbacks.rbi +21 -0
  28. data/sorbet/rbi/dsl/active_model/validations.rbi +26 -0
  29. data/sorbet/rbi/dsl/active_support/callbacks.rbi +21 -0
  30. data/sorbet/rbi/dsl/rails_type_id/concern.rbi +22 -0
  31. data/sorbet/rbi/gems/.gitattributes +1 -0
  32. data/sorbet/rbi/gems/actioncable@8.0.2.rbi +3133 -0
  33. data/sorbet/rbi/gems/actionmailbox@8.0.2.rbi +991 -0
  34. data/sorbet/rbi/gems/actionmailer@8.0.2.rbi +2775 -0
  35. data/sorbet/rbi/gems/actionpack@8.0.2.rbi +21167 -0
  36. data/sorbet/rbi/gems/actiontext@8.0.2.rbi +1449 -0
  37. data/sorbet/rbi/gems/actionview@8.0.2.rbi +15789 -0
  38. data/sorbet/rbi/gems/activejob@8.0.2.rbi +2864 -0
  39. data/sorbet/rbi/gems/activemodel@8.0.2.rbi +6946 -0
  40. data/sorbet/rbi/gems/activerecord@8.0.2.rbi +42458 -0
  41. data/sorbet/rbi/gems/activestorage@8.0.2.rbi +2156 -0
  42. data/sorbet/rbi/gems/activesupport@8.0.2.rbi +21274 -0
  43. data/sorbet/rbi/gems/ast@2.4.3.rbi +586 -0
  44. data/sorbet/rbi/gems/base64@0.3.0.rbi +545 -0
  45. data/sorbet/rbi/gems/benchmark@0.4.1.rbi +619 -0
  46. data/sorbet/rbi/gems/bigdecimal@3.2.2.rbi +275 -0
  47. data/sorbet/rbi/gems/builder@3.3.0.rbi +9 -0
  48. data/sorbet/rbi/gems/concurrent-ruby@1.3.5.rbi +11734 -0
  49. data/sorbet/rbi/gems/connection_pool@2.5.3.rbi +9 -0
  50. data/sorbet/rbi/gems/crass@1.0.6.rbi +623 -0
  51. data/sorbet/rbi/gems/date@3.4.1.rbi +403 -0
  52. data/sorbet/rbi/gems/drb@2.2.3.rbi +1661 -0
  53. data/sorbet/rbi/gems/erb@5.0.2.rbi +878 -0
  54. data/sorbet/rbi/gems/erubi@1.13.1.rbi +157 -0
  55. data/sorbet/rbi/gems/globalid@1.2.1.rbi +742 -0
  56. data/sorbet/rbi/gems/i18n@1.14.7.rbi +2383 -0
  57. data/sorbet/rbi/gems/io-console@0.8.1.rbi +9 -0
  58. data/sorbet/rbi/gems/json@2.12.2.rbi +2287 -0
  59. data/sorbet/rbi/gems/language_server-protocol@3.17.0.5.rbi +9 -0
  60. data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +323 -0
  61. data/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
  62. data/sorbet/rbi/gems/loofah@2.24.1.rbi +1105 -0
  63. data/sorbet/rbi/gems/mail@2.8.1.rbi +8890 -0
  64. data/sorbet/rbi/gems/marcel@1.0.4.rbi +239 -0
  65. data/sorbet/rbi/gems/mini_mime@1.1.5.rbi +173 -0
  66. data/sorbet/rbi/gems/minitest@5.25.5.rbi +2231 -0
  67. data/sorbet/rbi/gems/net-imap@0.5.9.rbi +10285 -0
  68. data/sorbet/rbi/gems/net-pop@0.1.2.rbi +927 -0
  69. data/sorbet/rbi/gems/net-protocol@0.2.2.rbi +292 -0
  70. data/sorbet/rbi/gems/net-smtp@0.5.1.rbi +1240 -0
  71. data/sorbet/rbi/gems/netrc@0.11.0.rbi +177 -0
  72. data/sorbet/rbi/gems/nio4r@2.7.4.rbi +388 -0
  73. data/sorbet/rbi/gems/nokogiri@1.18.9.rbi +8548 -0
  74. data/sorbet/rbi/gems/parallel@1.27.0.rbi +291 -0
  75. data/sorbet/rbi/gems/parser@3.3.9.0.rbi +5537 -0
  76. data/sorbet/rbi/gems/pp@0.6.2.rbi +368 -0
  77. data/sorbet/rbi/gems/prettyprint@0.2.0.rbi +477 -0
  78. data/sorbet/rbi/gems/prism@1.4.0.rbi +41790 -0
  79. data/sorbet/rbi/gems/psych@5.2.6.rbi +2542 -0
  80. data/sorbet/rbi/gems/racc@1.8.1.rbi +168 -0
  81. data/sorbet/rbi/gems/rack-session@2.1.1.rbi +722 -0
  82. data/sorbet/rbi/gems/rack-test@2.2.0.rbi +729 -0
  83. data/sorbet/rbi/gems/rack@3.2.0.rbi +5054 -0
  84. data/sorbet/rbi/gems/rackup@2.2.1.rbi +230 -0
  85. data/sorbet/rbi/gems/rails-dom-testing@2.3.0.rbi +790 -0
  86. data/sorbet/rbi/gems/rails-html-sanitizer@1.6.2.rbi +645 -0
  87. data/sorbet/rbi/gems/rails@8.0.2.rbi +9 -0
  88. data/sorbet/rbi/gems/railties@8.0.2.rbi +4014 -0
  89. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
  90. data/sorbet/rbi/gems/rake@13.3.0.rbi +3039 -0
  91. data/sorbet/rbi/gems/rbi@0.3.6.rbi +5162 -0
  92. data/sorbet/rbi/gems/rbs@4.0.0.dev.4.rbi +7895 -0
  93. data/sorbet/rbi/gems/rdoc@6.14.2.rbi +12944 -0
  94. data/sorbet/rbi/gems/regexp_parser@2.10.0.rbi +3833 -0
  95. data/sorbet/rbi/gems/reline@0.6.2.rbi +9 -0
  96. data/sorbet/rbi/gems/require-hooks@0.2.2.rbi +110 -0
  97. data/sorbet/rbi/gems/rexml@3.4.1.rbi +5205 -0
  98. data/sorbet/rbi/gems/rubocop-ast@1.46.0.rbi +7473 -0
  99. data/sorbet/rbi/gems/rubocop-sorbet@0.10.5.rbi +2386 -0
  100. data/sorbet/rbi/gems/rubocop@1.79.1.rbi +63674 -0
  101. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
  102. data/sorbet/rbi/gems/securerandom@0.4.1.rbi +75 -0
  103. data/sorbet/rbi/gems/spoom@1.7.5.rbi +5832 -0
  104. data/sorbet/rbi/gems/sqlite3@2.7.3.rbi +1989 -0
  105. data/sorbet/rbi/gems/stringio@3.1.7.rbi +9 -0
  106. data/sorbet/rbi/gems/tapioca@0.17.7.rbi +3692 -0
  107. data/sorbet/rbi/gems/thor@1.4.0.rbi +4399 -0
  108. data/sorbet/rbi/gems/timeout@0.4.3.rbi +157 -0
  109. data/sorbet/rbi/gems/typeid@0.2.2.rbi +239 -0
  110. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5919 -0
  111. data/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +132 -0
  112. data/sorbet/rbi/gems/unicode-emoji@4.0.4.rbi +251 -0
  113. data/sorbet/rbi/gems/uri@1.0.3.rbi +2354 -0
  114. data/sorbet/rbi/gems/useragent@0.16.11.rbi +9 -0
  115. data/sorbet/rbi/gems/uuid7@0.2.0.rbi +60 -0
  116. data/sorbet/rbi/gems/websocket-driver@0.8.0.rbi +1065 -0
  117. data/sorbet/rbi/gems/websocket-extensions@0.1.5.rbi +117 -0
  118. data/sorbet/rbi/gems/with_model@2.2.0.rbi +282 -0
  119. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +430 -0
  120. data/sorbet/rbi/gems/yard@0.9.37.rbi +18512 -0
  121. data/sorbet/rbi/gems/zeitwerk@2.7.3.rbi +1196 -0
  122. data/sorbet/tapioca/config.yml +13 -0
  123. data/sorbet/tapioca/require.rb +13 -0
  124. metadata +208 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b002a4e9136bfe9b886e13b8aaea8962f98b6ccf94e64dc875423c985fab6f8c
4
+ data.tar.gz: 7e245dcb3726845ddc8617cb91f2b6e79510baff72348d27124255855d79b3e6
5
+ SHA512:
6
+ metadata.gz: eb7fb1c5a1953d4b6ce56aa3771b18001220bcb383413abf17157d4cd7644cc505ac6c8cefe30902d246cf15b297fffa631db674ef33a07852c9189d3a08ac19
7
+ data.tar.gz: d534f95fbbec9934890497da6b2c9b0a4e0d82189b66c8e176a57758d5c9353336147b6136343b1cd8ee18388be655b631d336452e9a0fd5528b8dd9100178df
data/.rubocop.yml ADDED
@@ -0,0 +1,25 @@
1
+ plugins:
2
+ - rubocop-sorbet
3
+
4
+ AllCops:
5
+ Enabled: true
6
+ NewCops: enable
7
+ TargetRubyVersion: 3.1
8
+ Exclude:
9
+ - 'vendor/**/*'
10
+
11
+ Metrics/AbcSize:
12
+ Enabled: false
13
+
14
+ Metrics/MethodLength:
15
+ Enabled: false
16
+
17
+ Sorbet/HasSigil:
18
+ Enabled: true
19
+ MinimumStrictness: true
20
+
21
+ Style/StringLiterals:
22
+ EnforcedStyle: double_quotes
23
+
24
+ Style/StringLiteralsInInterpolation:
25
+ EnforcedStyle: double_quotes
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.5
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # rails_type_id
2
+
3
+ A gem that makes simple to use [TypeID](https://github.com/broothie/typeid-ruby) as the primary key for ActiveRecord models.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ ```bash
10
+ bundle add rails_type_id
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Declaring on models
16
+ Add `RailsTypeId::Concern` to the model. This model's `id` field should have either a String or UUID database column type.
17
+
18
+ ```ruby
19
+ # app/models/my_model.rb
20
+ require "rails_type_id"
21
+
22
+ class MyModel < ActiveRecord::Base
23
+ include RailsTypeId::Concern
24
+
25
+ # Prefix should be unique within your project
26
+ with_type_id_prefix("mm")
27
+ end
28
+ ```
29
+
30
+ ### Using the `type_id` field
31
+
32
+ Model instances will have a `type_id` field of type `TypeID`.
33
+
34
+ ```ruby
35
+ my_model = MyModel.create!(..)
36
+ my_model.id #=> "019867fe-560f-7941-a7ed-8472639c7ace"
37
+ my_model.type_id #=> #<TypeID mm_01k1kzwngff50tfvc4e9hsrype>
38
+ my_model.type_id.to_s #=> mm_01k1kzwngff50tfvc4e9hsrype
39
+ ```
40
+
41
+ ## Development
42
+
43
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
44
+
45
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
46
+
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task :sorbet do
13
+ sh "bin/srb"
14
+ end
15
+
16
+ task default: %i[test rubocop sorbet]
@@ -0,0 +1,129 @@
1
+ # typed: false # rubocop:disable Sorbet/HasSigil
2
+ # frozen_string_literal: true
3
+
4
+ require "active_model"
5
+ require "active_support"
6
+ require "sorbet-runtime"
7
+ require "typeid"
8
+
9
+ module RailsTypeId
10
+ # RailsTypeId::Concern is a Rails ActiveSupport::Concern that uses a Stripe-style "type ID"
11
+ # for its ID field.
12
+ module Concern
13
+ extend ::ActiveSupport::Concern
14
+ include ::ActiveModel::Validations::Callbacks
15
+
16
+ class InvalidTypeIdPrefix < StandardError; end
17
+
18
+ # Checks validity of the ActiveRecord model instances
19
+ class Validator < ActiveModel::Validator
20
+ def validate(record)
21
+ result = Helpers.validate_type_id_prefix(record.class.type_id_prefix)
22
+ record.errors.add(:type_id_prefix, result) if result
23
+ end
24
+ end
25
+
26
+ class_methods do
27
+ def with_type_id_prefix(prefix)
28
+ Helpers.validate_type_id_prefix!(prefix)
29
+
30
+ @_type_id_prefix = prefix
31
+ end
32
+
33
+ def type_id_prefix
34
+ @_type_id_prefix
35
+ end
36
+
37
+ def from_controller_id_param(type_id_str)
38
+ find(TypeID.from_string(type_id_str).uuid.to_s)
39
+ end
40
+ end
41
+
42
+ included do
43
+ attribute :id # Postgres UUID field
44
+
45
+ before_create :generate_uuid_v7
46
+ validates_with Validator
47
+
48
+ # Returns the TypeID for the model
49
+ # @return [TypeID]
50
+ define_method :type_id do
51
+ Helpers.validate_type_id_prefix!(self.class.type_id_prefix)
52
+ TypeID.from_uuid(self.class.type_id_prefix, id)
53
+ end
54
+
55
+ # If `id` is unset, generates a new UUID v7 and sets it
56
+ # @return [void]
57
+ define_method :generate_uuid_v7 do
58
+ case self.class.attribute_types["id"].type
59
+ when :uuid, :string
60
+ self.id ||= SecureRandom.uuid_v7
61
+ end
62
+ end
63
+
64
+ define_method :to_param do
65
+ type_id.to_s
66
+ end
67
+ end
68
+
69
+ # Internal helper methods
70
+ class Helpers
71
+ extend T::Sig
72
+
73
+ class << self
74
+ def validate_type_id_prefix(prefix)
75
+ return "type_id_prefix cannot be nil" if prefix.nil?
76
+
77
+ return nil if prefix.match(/\A[a-z]{1,10}\z/)
78
+
79
+ "type_id_prefix must be lowercase alphabetic (a-z) with length >= 1, <= 10"
80
+ end
81
+
82
+ def validate_type_id_prefix!(prefix)
83
+ result = validate_type_id_prefix(prefix)
84
+ raise InvalidTypeIdPrefix, result if result.present?
85
+ end
86
+
87
+ def lookup_type_id(type_id)
88
+ klasses = lookup_model(type_id)
89
+ return if klasses.nil?
90
+
91
+ id = type_id # TODO: parse out the uuid part
92
+ klasses.each do |klass|
93
+ result = klass.find_by(id: id)
94
+ return result unless result.nil?
95
+ end
96
+
97
+ nil
98
+ end
99
+
100
+ def lookup_model(type_id)
101
+ prefix = get_prefix(type_id)
102
+ return if prefix.nil?
103
+
104
+ prefix_map[prefix]
105
+ end
106
+
107
+ def get_prefix(id)
108
+ prefix, = parse_id(id)
109
+ prefix
110
+ end
111
+
112
+ private
113
+
114
+ def parse_id(id)
115
+ id.split("_")
116
+ end
117
+
118
+ def prefix_map
119
+ Rails.application.eager_load!
120
+ # This has to be group_by and not index_by because we can have multiple
121
+ # models that use the same prefix (like Settings)
122
+ @prefix_map ||= ActiveRecord::Base.descendants
123
+ .select { |klass| klass.respond_to?(:type_id_prefix) }
124
+ .group_by(&:type_id_prefix)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,6 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "version"
5
+ require_relative "concern"
6
+ require_relative "test_helper"
@@ -0,0 +1,16 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module RailsTypeId
5
+ # Static helpers for testing Rails models with type IDs
6
+ module TestHelper
7
+ def models_missing_type_id_prefix(base_class:, ignore_classes: [])
8
+ Rails.application.eager_load!
9
+ base_class.descendants.select do |klass|
10
+ next if ignore_classes.include?(klass)
11
+
12
+ !klass.respond_to?(:type_id_prefix)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module RailsTypeId
5
+ VERSION = "0.1.0"
6
+ end
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "rails_type_id/require"
5
+
6
+ module RailsTypeId
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ end
@@ -0,0 +1,49 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "tapioca"
5
+
6
+ require_relative "../../../rails_type_id/concern"
7
+
8
+ module Tapioca
9
+ module Dsl
10
+ module Compilers
11
+ # This Tapioca compiler generates RBI for Rails models that include RailsTypeId::Concern.
12
+ class RailsTypeIdCompiler < Tapioca::Dsl::Compiler
13
+ extend T::Sig
14
+
15
+ ConstantType = type_member { { fixed: T.class_of(RailsTypeId::Concern) } }
16
+
17
+ RBI_MODULE_NAME = "RailsTypeIdMethods"
18
+
19
+ class << self
20
+ extend T::Sig
21
+
22
+ sig { override.returns(T::Enumerable[Module]) }
23
+ def gather_constants
24
+ all_classes
25
+ .select { |c| c < RailsTypeId::Concern }
26
+ end
27
+ end
28
+
29
+ sig { override.void }
30
+ def decorate
31
+ root.create_path(constant) do |klass|
32
+ klass.create_module(RBI_MODULE_NAME) do |methods_mod|
33
+ methods_mod.create_method(
34
+ "type_id",
35
+ return_type: "TypeID"
36
+ )
37
+
38
+ methods_mod.create_method(
39
+ "to_param",
40
+ return_type: "String"
41
+ )
42
+ end
43
+ klass.create_include(RBI_MODULE_NAME)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ module RailsTypeId
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
data/sorbet/config ADDED
@@ -0,0 +1,4 @@
1
+ --dir
2
+ .
3
+ --ignore=vendor/
4
+
@@ -0,0 +1 @@
1
+ **/*.rbi linguist-vendored=true
@@ -0,0 +1,10 @@
1
+ # typed: true
2
+
3
+ # DO NOT EDIT MANUALLY
4
+ # This file was pulled from a central RBI files repository.
5
+ # Please run `bin/tapioca annotations` to update it.
6
+
7
+ class ActionMailer::Base
8
+ sig { params(headers: T.untyped, block: T.nilable(T.proc.params(arg0: ActionMailer::Collector).void)).returns(Mail::Message) }
9
+ def mail(headers = nil, &block); end
10
+ end