activerecord-ejection_seat 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +11 -8
  3. data/.ruby-version +1 -0
  4. data/.tool-versions +1 -0
  5. data/CODE_OF_CONDUCT.md +1 -1
  6. data/Gemfile +2 -2
  7. data/Gemfile.lock +48 -61
  8. data/README.md +29 -8
  9. data/Rakefile +6 -6
  10. data/activerecord-ejection_seat.gemspec +40 -0
  11. data/lib/activerecord-ejection_seat/attributes_builder.rb +42 -0
  12. data/lib/activerecord-ejection_seat/ejectable.rb +7 -10
  13. data/lib/activerecord-ejection_seat/props_builder.rb +35 -23
  14. data/lib/activerecord-ejection_seat/version.rb +2 -2
  15. data/lib/activerecord-ejection_seat.rb +6 -3
  16. data/sorbet/rbi/gems/activemodel@7.0.4.2.rbi +6022 -0
  17. data/sorbet/rbi/gems/activerecord@7.0.4.2.rbi +37791 -0
  18. data/sorbet/rbi/gems/activesupport@7.0.4.2.rbi +18127 -0
  19. data/sorbet/rbi/gems/concurrent-ruby@1.2.0.rbi +11570 -0
  20. data/sorbet/rbi/gems/irb@1.6.2.rbi +35 -50
  21. data/sorbet/rbi/gems/{language_server-protocol@3.17.0.2.rbi → language_server-protocol@3.17.0.3.rbi} +0 -0
  22. data/sorbet/rbi/gems/minitest@5.17.0.rbi +2319 -0
  23. data/sorbet/rbi/gems/{parser@3.1.3.0.rbi → parser@3.2.1.0.rbi} +1302 -927
  24. data/sorbet/rbi/gems/{prettier_print@1.1.0.rbi → prettier_print@1.2.0.rbi} +0 -0
  25. data/sorbet/rbi/gems/{regexp_parser@2.6.1.rbi → regexp_parser@2.7.0.rbi} +598 -144
  26. data/sorbet/rbi/gems/{rubocop-ast@1.24.0.rbi → rubocop-ast@1.26.0.rbi} +777 -533
  27. data/sorbet/rbi/gems/rubocop-minitest@0.27.0.rbi +2386 -0
  28. data/sorbet/rbi/gems/{rubocop-sorbet@0.6.11.rbi → rubocop-sorbet@0.7.0.rbi} +158 -114
  29. data/sorbet/rbi/gems/{rubocop@1.41.0.rbi → rubocop@1.45.1.rbi} +3841 -1338
  30. data/sorbet/rbi/gems/{ruby-lsp@0.3.7.rbi → ruby-lsp@0.4.0.rbi} +2 -1
  31. data/sorbet/rbi/gems/{sqlite3@1.5.4.rbi → sqlite3@1.6.0.rbi} +73 -0
  32. data/sorbet/rbi/gems/{syntax_tree@4.3.0.rbi → syntax_tree@6.0.0.rbi} +0 -0
  33. data/sorbet/rbi/gems/{tapioca@0.10.4.rbi → tapioca@0.10.5.rbi} +279 -96
  34. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5917 -0
  35. data/sorbet/rbi/gems/{unicode-display_width@2.3.0.rbi → unicode-display_width@2.4.2.rbi} +26 -7
  36. data/sorbet/rbi/gems/{unparser@0.6.5.rbi → unparser@0.6.7.rbi} +323 -64
  37. data/sorbet/rbi/gems/{yard-sorbet@0.7.0.rbi → yard-sorbet@0.8.0.rbi} +91 -41
  38. data/sorbet/rbi/shims/location.rbi +13 -0
  39. data/sorbet/rbi/shims/post.rbi +16 -0
  40. data/sorbet/rbi/shims/user.rbi +19 -0
  41. data/sorbet/rbi/todo.rbi +0 -4
  42. data/sorbet/tapioca/require.rb +16 -2
  43. metadata +39 -37
  44. data/sorbet/rbi/gems/activemodel@7.0.4.rbi +0 -8
  45. data/sorbet/rbi/gems/activerecord@7.0.4.rbi +0 -11
  46. data/sorbet/rbi/gems/activesupport@7.0.4.rbi +0 -93
  47. data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +0 -8
  48. data/sorbet/rbi/gems/minitest@5.16.3.rbi +0 -8
  49. data/sorbet/rbi/gems/rspec-core@3.12.0.rbi +0 -10588
  50. data/sorbet/rbi/gems/rspec-expectations@3.12.1.rbi +0 -7817
  51. data/sorbet/rbi/gems/rspec-mocks@3.12.1.rbi +0 -4994
  52. data/sorbet/rbi/gems/rspec-support@3.12.0.rbi +0 -1477
  53. data/sorbet/rbi/gems/rspec@3.12.0.rbi +0 -10
  54. data/sorbet/rbi/gems/rubocop-rspec@2.16.0.rbi +0 -7650
  55. data/sorbet/rbi/gems/tzinfo@2.0.5.rbi +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec011864f722a24abd09c60e4dbfd8c53e1a41a2922ef6827a527f3992d5b941
4
- data.tar.gz: ea35286dccfb37c99279b6ffe6403fd20ee72c8a488103bdaa4386e7f6c8b205
3
+ metadata.gz: d37d5dedef56e4a743bb6b979dfcd53d8e8adaa5dfe7aad1edf3431aea18c987
4
+ data.tar.gz: a574c315f0b1578a84115e7702bf3613dd01eab053b1a4b6da72186e9aab585e
5
5
  SHA512:
6
- metadata.gz: a2dfb9277f769f3e9296a5388459e02acfa98ae0697009e3e8587dbaed774d45ec059652a7ffdf61c5a2a36c62b309e32fa10b834f20a8e84d145fc3c6f78dda
7
- data.tar.gz: b39ccda5b3aa68a6d63f9be7959f70daf7a8e262f67d0b4afbf9bd250c1f1cb42f25d28592b5223ccd46857548a2ce7936d061bc9f0b62030bbe870b03e7d5c3
6
+ metadata.gz: bd053a38351b5e7b8310f87a57338e2b08403a7a17f1976171699b672d005bb106b24e87daaea22f4fa090cde35f4d3205571575f075ea9ea16d8a2870d40434
7
+ data.tar.gz: ad848b3caaf2faed99b6d6d28f4530dc441c6cce7f7001af4d8131b0de94e35443bb7df54d59d3dd0505cfd94e04cf51ffb1c4c100a624087f5628da3d384625
data/.rubocop.yml CHANGED
@@ -1,11 +1,20 @@
1
+ inherit_mode:
2
+ merge:
3
+ - Exclude
4
+
1
5
  require:
6
+ - rubocop-minitest
2
7
  - rubocop-rake
3
- - rubocop-rspec
4
8
  - rubocop-sorbet
5
9
 
6
10
  AllCops:
7
11
  NewCops: enable
8
- TargetRubyVersion: 2.7
12
+ TargetRubyVersion: 3.0
13
+ Exclude:
14
+ - sorbet/**/*.rbi
15
+
16
+ Style/AccessorGrouping:
17
+ Enabled: false
9
18
 
10
19
  Style/StringLiterals:
11
20
  Enabled: true
@@ -17,9 +26,3 @@ Style/StringLiteralsInInterpolation:
17
26
 
18
27
  Layout/LineLength:
19
28
  Max: 120
20
-
21
- RSpec/FilePath:
22
- Enabled: false
23
-
24
- RSpec/MultipleExpectations:
25
- Max: 3
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.1
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.1
data/CODE_OF_CONDUCT.md CHANGED
@@ -39,7 +39,7 @@ This Code of Conduct applies within all community spaces, and also applies when
39
39
 
40
40
  ## Enforcement
41
41
 
42
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at max@audaciousaardvark.com. All complaints will be reviewed and investigated promptly and fairly.
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at maxveldink@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
43
43
 
44
44
  All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
45
 
data/Gemfile CHANGED
@@ -8,8 +8,8 @@ gemspec
8
8
  group :development do
9
9
  gem "rake"
10
10
  gem "rubocop"
11
+ gem "rubocop-minitest"
11
12
  gem "rubocop-rake"
12
- gem "rubocop-rspec"
13
13
  gem "rubocop-sorbet"
14
14
  gem "sorbet"
15
15
  gem "spoom", require: false
@@ -19,7 +19,7 @@ group :development do
19
19
  end
20
20
 
21
21
  group :development, :test do
22
- gem "rspec"
22
+ gem "minitest"
23
23
  gem "sorbet-runtime"
24
24
  gem "sorbet-struct-comparable"
25
25
  gem "sqlite3"
data/Gemfile.lock CHANGED
@@ -1,26 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-ejection_seat (0.1.0)
5
- activerecord (>= 5.2)
4
+ activerecord-ejection_seat (0.3.1)
5
+ activerecord (>= 6.0)
6
6
  sorbet-runtime
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (7.0.4)
12
- activesupport (= 7.0.4)
13
- activerecord (7.0.4)
14
- activemodel (= 7.0.4)
15
- activesupport (= 7.0.4)
16
- activesupport (7.0.4)
11
+ activemodel (7.0.4.2)
12
+ activesupport (= 7.0.4.2)
13
+ activerecord (7.0.4.2)
14
+ activemodel (= 7.0.4.2)
15
+ activesupport (= 7.0.4.2)
16
+ activesupport (7.0.4.2)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
18
  i18n (>= 1.6, < 2)
19
19
  minitest (>= 5.1)
20
20
  tzinfo (~> 2.0)
21
21
  ast (2.4.2)
22
- concurrent-ruby (1.1.10)
23
- debug (1.7.0)
22
+ concurrent-ruby (1.2.0)
23
+ debug (1.7.1)
24
24
  irb (>= 1.5.0)
25
25
  reline (>= 0.3.1)
26
26
  diff-lcs (1.5.0)
@@ -30,13 +30,13 @@ GEM
30
30
  irb (1.6.2)
31
31
  reline (>= 0.3.0)
32
32
  json (2.6.3)
33
- language_server-protocol (3.17.0.2)
34
- minitest (5.16.3)
33
+ language_server-protocol (3.17.0.3)
34
+ minitest (5.17.0)
35
35
  netrc (0.11.0)
36
36
  parallel (1.22.1)
37
- parser (3.1.3.0)
37
+ parser (3.2.1.0)
38
38
  ast (~> 2.4.1)
39
- prettier_print (1.1.0)
39
+ prettier_print (1.2.0)
40
40
  rainbow (3.1.1)
41
41
  rake (13.0.6)
42
42
  rbi (0.0.16)
@@ -44,66 +44,53 @@ GEM
44
44
  parser (>= 2.6.4.0)
45
45
  sorbet-runtime (>= 0.5.9204)
46
46
  unparser
47
- regexp_parser (2.6.1)
47
+ regexp_parser (2.7.0)
48
48
  reline (0.3.2)
49
49
  io-console (~> 0.5)
50
50
  rexml (3.2.5)
51
- rspec (3.12.0)
52
- rspec-core (~> 3.12.0)
53
- rspec-expectations (~> 3.12.0)
54
- rspec-mocks (~> 3.12.0)
55
- rspec-core (3.12.0)
56
- rspec-support (~> 3.12.0)
57
- rspec-expectations (3.12.1)
58
- diff-lcs (>= 1.2.0, < 2.0)
59
- rspec-support (~> 3.12.0)
60
- rspec-mocks (3.12.1)
61
- diff-lcs (>= 1.2.0, < 2.0)
62
- rspec-support (~> 3.12.0)
63
- rspec-support (3.12.0)
64
- rubocop (1.41.0)
51
+ rubocop (1.45.1)
65
52
  json (~> 2.3)
66
53
  parallel (~> 1.10)
67
- parser (>= 3.1.2.1)
54
+ parser (>= 3.2.0.0)
68
55
  rainbow (>= 2.2.2, < 4.0)
69
56
  regexp_parser (>= 1.8, < 3.0)
70
57
  rexml (>= 3.2.5, < 4.0)
71
- rubocop-ast (>= 1.23.0, < 2.0)
58
+ rubocop-ast (>= 1.24.1, < 2.0)
72
59
  ruby-progressbar (~> 1.7)
73
- unicode-display_width (>= 1.4.0, < 3.0)
74
- rubocop-ast (1.24.0)
75
- parser (>= 3.1.1.0)
60
+ unicode-display_width (>= 2.4.0, < 3.0)
61
+ rubocop-ast (1.26.0)
62
+ parser (>= 3.2.1.0)
63
+ rubocop-minitest (0.27.0)
64
+ rubocop (>= 0.90, < 2.0)
76
65
  rubocop-rake (0.6.0)
77
66
  rubocop (~> 1.0)
78
- rubocop-rspec (2.16.0)
79
- rubocop (~> 1.33)
80
- rubocop-sorbet (0.6.11)
67
+ rubocop-sorbet (0.7.0)
81
68
  rubocop (>= 0.90.0)
82
- ruby-lsp (0.3.7)
69
+ ruby-lsp (0.4.0)
83
70
  language_server-protocol (~> 3.17.0)
84
71
  sorbet-runtime
85
- syntax_tree (>= 4.0.2, < 5.0.0)
72
+ syntax_tree (>= 6, < 7)
86
73
  ruby-progressbar (1.11.0)
87
- sorbet (0.5.10597)
88
- sorbet-static (= 0.5.10597)
89
- sorbet-runtime (0.5.10597)
90
- sorbet-static (0.5.10597-universal-darwin-21)
91
- sorbet-static (0.5.10597-universal-darwin-22)
92
- sorbet-static (0.5.10597-x86_64-linux)
93
- sorbet-static-and-runtime (0.5.10597)
94
- sorbet (= 0.5.10597)
95
- sorbet-runtime (= 0.5.10597)
74
+ sorbet (0.5.10676)
75
+ sorbet-static (= 0.5.10676)
76
+ sorbet-runtime (0.5.10676)
77
+ sorbet-static (0.5.10676-universal-darwin-21)
78
+ sorbet-static (0.5.10676-universal-darwin-22)
79
+ sorbet-static (0.5.10676-x86_64-linux)
80
+ sorbet-static-and-runtime (0.5.10676)
81
+ sorbet (= 0.5.10676)
82
+ sorbet-runtime (= 0.5.10676)
96
83
  sorbet-struct-comparable (1.3.0)
97
84
  sorbet-runtime (>= 0.5)
98
85
  spoom (1.1.15)
99
86
  sorbet (>= 0.5.10187)
100
87
  sorbet-runtime (>= 0.5.9204)
101
88
  thor (>= 0.19.2)
102
- sqlite3 (1.5.4-arm64-darwin)
103
- sqlite3 (1.5.4-x86_64-linux)
104
- syntax_tree (4.3.0)
105
- prettier_print (>= 1.0.2)
106
- tapioca (0.10.4)
89
+ sqlite3 (1.6.0-arm64-darwin)
90
+ sqlite3 (1.6.0-x86_64-linux)
91
+ syntax_tree (6.0.0)
92
+ prettier_print (>= 1.2.0)
93
+ tapioca (0.10.5)
107
94
  bundler (>= 1.17.3)
108
95
  netrc (>= 0.11.0)
109
96
  parallel (>= 1.21.0)
@@ -113,16 +100,16 @@ GEM
113
100
  thor (>= 1.2.0)
114
101
  yard-sorbet
115
102
  thor (1.2.1)
116
- tzinfo (2.0.5)
103
+ tzinfo (2.0.6)
117
104
  concurrent-ruby (~> 1.0)
118
- unicode-display_width (2.3.0)
119
- unparser (0.6.5)
105
+ unicode-display_width (2.4.2)
106
+ unparser (0.6.7)
120
107
  diff-lcs (~> 1.3)
121
- parser (>= 3.1.0)
108
+ parser (>= 3.2.0)
122
109
  webrick (1.7.0)
123
110
  yard (0.9.28)
124
111
  webrick (~> 1.7.0)
125
- yard-sorbet (0.7.0)
112
+ yard-sorbet (0.8.0)
126
113
  sorbet-runtime (>= 0.5)
127
114
  yard (>= 0.9)
128
115
 
@@ -134,11 +121,11 @@ PLATFORMS
134
121
  DEPENDENCIES
135
122
  activerecord-ejection_seat!
136
123
  debug
124
+ minitest
137
125
  rake
138
- rspec
139
126
  rubocop
127
+ rubocop-minitest
140
128
  rubocop-rake
141
- rubocop-rspec
142
129
  rubocop-sorbet
143
130
  ruby-lsp
144
131
  sorbet
@@ -149,4 +136,4 @@ DEPENDENCIES
149
136
  tapioca
150
137
 
151
138
  BUNDLED WITH
152
- 2.3.26
139
+ 2.4.7
data/README.md CHANGED
@@ -20,13 +20,26 @@ If bundler is not being used to manage dependencies, install the gem by executin
20
20
 
21
21
  ## Usage
22
22
 
23
- Let's say you have an `ActiveRecord` model for a `User` with a string `name` column and an integer `age` column. Another way to express this would be a `User` type that is a simple, typed struct.
23
+ Let's say you have an `ActiveRecord` model for a `User` with a string `name` column, an integer `age` column, a `role` enum column and a belongs to association with a `Location` model. Another way to express this would be a `User` type that is a simple, typed struct with other supporting structs.
24
24
 
25
25
  ```ruby
26
26
  module Types
27
+ class UserRoles < T::Enum
28
+ enums do
29
+ Admin = new("admin")
30
+ Member = new("member")
31
+ end
32
+ end
33
+
34
+ class Location < T::Struct
35
+ const :name, String
36
+ end
37
+
27
38
  class User < T::Struct
28
39
  const :name, String
29
40
  const :age, Integer
41
+ const :role, UserRoles
42
+ const :location, Location
30
43
  end
31
44
  end
32
45
  ```
@@ -35,6 +48,9 @@ In our model, we can specify an ejection to this type.
35
48
 
36
49
  ```ruby
37
50
  class User
51
+ belongs_to :location
52
+ enum :role, { admin: "admin", member: "member" }
53
+
38
54
  ejects_to Types::User
39
55
  end
40
56
  ```
@@ -42,17 +58,18 @@ end
42
58
  Now, we have two new methods available on `User`. First, we can eject from a `User` instance to a `Types::User`.
43
59
 
44
60
  ```ruby
45
- User.new(name: "Max", age: 28).eject
46
- # => Types::User(name: "Max", age: 28)
47
- User.new(name: "Max", age: 28).to_struct # alias
61
+ location = Location.new(name: "Florida")
62
+ User.new(name: "Max", age: 28, role: "admin", location: location).eject
63
+ # => Types::User(name: "Max", age: 28, role: Types::UserRoles::Admin, location: Types::Location.new(name: "Florida))
64
+ User.new(name: "Max", age: 28, role: "admin", location: location).to_struct # alias
48
65
  ```
49
66
 
50
67
  Second, we can buckle into the `User` model with a `Types::User`.
51
68
 
52
69
  ```ruby
53
- user_struct = Types::User.new(name: "Max", age: 28)
70
+ user_struct = Types::User.new(name: "Max", age: 28, role: Types::UserRoles::Admin, location: Types::Location.new(name: "Florida"))
54
71
  User.buckle(user_struct)
55
- # => User(name: "Max", age: 28)
72
+ # => User(name: "Max", age: 28, role: "admin", location: Location(name: "Florida"))
56
73
  User.from_struct(user_struct) # alias
57
74
  ```
58
75
 
@@ -64,7 +81,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
64
81
 
65
82
  ## Contributing
66
83
 
67
- Bug reports and pull requests are welcome on GitHub at https://github.com/audaciousaardvark/activerecord-ejection_seat. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/audaciousaardvark/activerecord-ejection_seat/blob/master/CODE_OF_CONDUCT.md).
84
+ Bug reports and pull requests are welcome on GitHub at https://github.com/maxveldink/activerecord-ejection_seat. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/maxveldink/activerecord-ejection_seat/blob/master/CODE_OF_CONDUCT.md).
68
85
 
69
86
  ## License
70
87
 
@@ -72,4 +89,8 @@ The gem is available as open source under the terms of the [MIT License](https:/
72
89
 
73
90
  ## Code of Conduct
74
91
 
75
- Everyone interacting in this project's codebase, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/audaciousaardvark/activerecord-ejection_seat/blob/master/CODE_OF_CONDUCT.md).
92
+ Everyone interacting in this project's codebase, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/maxveldink/activerecord-ejection_seat/blob/master/CODE_OF_CONDUCT.md).
93
+
94
+ ## Sponsorships
95
+
96
+ I love creating in the open. If you find this or any other [maxveld.ink](https://maxveld.ink) content useful, please consider sponsoring me on [GitHub](https://github.com/sponsors/maxveldink).
data/Rakefile CHANGED
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
4
+ require "minitest/test_task"
5
5
 
6
- RSpec::Core::RakeTask.new(:spec)
6
+ Minitest::TestTask.create do |t|
7
+ t.test_globs = ["test/**/*_test.rb"]
8
+ end
7
9
 
8
10
  require "rubocop/rake_task"
9
11
 
10
- RuboCop::RakeTask.new do |t|
11
- t.options = ["-A"]
12
- end
12
+ RuboCop::RakeTask.new
13
13
 
14
14
  desc "Run tapioca compilers"
15
15
  task :tapioca do
@@ -21,4 +21,4 @@ task :sorbet do
21
21
  sh "bundle exec srb tc"
22
22
  end
23
23
 
24
- task default: %i[rubocop sorbet spec]
24
+ task default: %i[rubocop:autocorrect_all sorbet test]
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/activerecord-ejection_seat/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "activerecord-ejection_seat"
7
+ spec.version = ActiveRecord::EjectionSeat::VERSION
8
+ spec.authors = ["Max VelDink"]
9
+ spec.email = ["maxveldink@gmail.com"]
10
+
11
+ spec.summary = "Eject from an ActiveRecord model to a Sorbet T::Struct, or buckle back in."
12
+ spec.description = "When working with ActiveRecord models, sometimes you want to eject to a simpler, safer object. \
13
+ Enter Sorbet's T::Struct. This gem makes it much easier to target a \
14
+ Sorbet T::Struct and eject from an ActiveRecord model into the struct. \
15
+ It also allows you to buckle in from a simple struct to a new ActiveRecord model instance."
16
+ spec.homepage = "https://github.com/maxveldink/activerecord-ejection_seat"
17
+ spec.license = "MIT"
18
+ spec.required_ruby_version = ">= 3.0.0"
19
+
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ spec.metadata["rubygems_mfa_required"] = "true"
22
+
23
+ spec.metadata["homepage_uri"] = spec.homepage
24
+ spec.metadata["source_code_uri"] = spec.homepage
25
+ spec.metadata["changelog_uri"] = "https://github.com/maxveldink/activerecord-ejection_seat/blob/main/CHANGELOG.md"
26
+
27
+ # Specify which files should be added to the gem when it is released.
28
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
+ spec.files = Dir.chdir(__dir__) do
30
+ `git ls-files -z`.split("\x0").reject do |f|
31
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
32
+ end
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ spec.add_runtime_dependency "activerecord", ">= 6.0"
39
+ spec.add_runtime_dependency "sorbet-runtime"
40
+ end
@@ -0,0 +1,42 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ # Creates initialization payload for targeted ActiveRecord model
5
+ class AttributesBuilder
6
+ extend T::Sig
7
+
8
+ sig { params(struct: T::Struct, target_model: T.class_of(ActiveRecord::Base)).void }
9
+ def initialize(struct:, target_model:)
10
+ @struct = struct
11
+ @target_model = target_model
12
+ end
13
+
14
+ sig { returns(T::Hash[String, T.untyped]) }
15
+ def build
16
+ attrs = struct.serialize
17
+ props = attrs.keys & target_model.column_names.map { |name| name.delete_suffix("_id") }
18
+
19
+ attrs.slice(*props).to_h { |key, value| build_attribute(name: key, value: value) }
20
+ end
21
+
22
+ private
23
+
24
+ sig { params(name: String, value: T.untyped).returns(T::Array[T.untyped]) }
25
+ def build_attribute(name:, value:)
26
+ if value.is_a?(Hash)
27
+ association_class = target_model.reflect_on_all_associations.find do |association|
28
+ association.name.to_s == name
29
+ end&.klass
30
+
31
+ value = association_class&.new(value)
32
+ end
33
+
34
+ [name, value]
35
+ end
36
+
37
+ sig { returns(T::Struct) }
38
+ attr_reader :struct
39
+
40
+ sig { returns(T.class_of(ActiveRecord::Base)) }
41
+ attr_reader :target_model
42
+ end
@@ -1,8 +1,6 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
- require "active_record"
5
-
6
4
  module ActiveRecord
7
5
  module EjectionSeat
8
6
  # Defines `#eject` `.buckle` methods for going between ActiveRecord models and Sorbet T::Structs.
@@ -16,10 +14,12 @@ module ActiveRecord
16
14
 
17
15
  def define_eject_method(klass)
18
16
  define_method(:eject) do
19
- attrs = attributes.deep_symbolize_keys
20
- attribute_props = klass.props.keys & attrs.keys
21
-
22
- klass.new(PropsBuilder.new(attrs.slice(*attribute_props), klass.props).build)
17
+ klass.new(
18
+ PropsBuilder.new(
19
+ model: self,
20
+ target_struct: klass
21
+ ).build
22
+ )
23
23
  end
24
24
 
25
25
  alias_method :to_struct, :eject
@@ -29,10 +29,7 @@ module ActiveRecord
29
29
  define_singleton_method(:buckle) do |struct|
30
30
  raise ArgumentError if struct.class != klass
31
31
 
32
- attrs = struct.serialize
33
- props = attrs.keys & column_names
34
-
35
- new(attrs.slice(*props))
32
+ new(AttributesBuilder.new(struct: struct, target_model: self).build)
36
33
  end
37
34
 
38
35
  singleton_class.send :alias_method, :from_struct, :buckle
@@ -1,44 +1,56 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "sorbet-runtime"
5
-
6
4
  # Creates initialization payload for targeted T::Struct
7
5
  class PropsBuilder
8
6
  extend T::Sig
9
7
 
10
- Attributes = T.type_alias { T::Hash[Symbol, T.untyped] }
11
-
12
8
  sig do
13
9
  params(
14
- attributes_from_model: Attributes,
15
- target_props: Attributes
10
+ model: ActiveRecord::Base,
11
+ target_struct: T.class_of(T::Struct)
16
12
  ).void
17
13
  end
18
- def initialize(attributes_from_model, target_props)
19
- @attributes_from_model = attributes_from_model
20
- @target_props = target_props
14
+ def initialize(model:, target_struct:)
15
+ @model = model
16
+ @target_struct = target_struct
21
17
  end
22
18
 
23
- sig { returns(Attributes) }
19
+ sig { returns(T::Hash[Symbol, T.untyped]) }
24
20
  def build
25
- built_props = {}
26
-
27
- attributes_from_model.each do |k, v|
28
- prop_type = target_props.dig(k, :type)
21
+ target_struct.props.keys.each_with_object({}) do |prop_name, returned_props|
22
+ attribute = model.respond_to?(prop_name) ? build_attribute(prop_name) : nil
29
23
 
30
- built_props[k] = if prop_type < T::Enum
31
- prop_type.deserialize(v)
32
- else
33
- v
34
- end
24
+ returned_props[prop_name] = attribute
35
25
  end
36
-
37
- built_props
38
26
  end
39
27
 
40
28
  private
41
29
 
42
- sig { returns(Attributes) }
43
- attr_reader :attributes_from_model, :target_props
30
+ sig { params(prop_name: Symbol).returns(T.untyped) }
31
+ def build_attribute(prop_name)
32
+ attribute = model.send(prop_name)
33
+ prop_type = target_struct.props.dig(prop_name, :type)
34
+
35
+ cast_attribute(attribute, prop_type)
36
+ end
37
+
38
+ sig { params(attribute: T.untyped, prop_type: Class).returns(T.untyped) }
39
+ def cast_attribute(attribute, prop_type)
40
+ if prop_type < T::Enum
41
+ prop_type.deserialize(attribute)
42
+ elsif prop_type < T::Struct
43
+ prop_type.new(PropsBuilder.new(model: attribute, target_struct: prop_type).build)
44
+ else
45
+ attribute
46
+ end
47
+ rescue KeyError
48
+ nil
49
+ end
50
+
51
+ sig { returns(ActiveRecord::Base) }
52
+ attr_reader :model
53
+
54
+ sig { returns(T.class_of(T::Struct)) }
55
+ attr_reader :target_struct
44
56
  end
@@ -1,8 +1,8 @@
1
- # typed: false
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ActiveRecord
5
5
  module EjectionSeat
6
- VERSION = "0.2.0"
6
+ VERSION = "0.3.1"
7
7
  end
8
8
  end
@@ -1,12 +1,15 @@
1
1
  # rubocop:disable Naming/FileName
2
2
  # rubocop:enable Naming/FileName
3
- # typed: false
3
+ # typed: strict
4
4
  # frozen_string_literal: true
5
5
 
6
+ require "active_record"
7
+ require "sorbet-runtime"
8
+ require "active_support/lazy_load_hooks"
9
+
6
10
  require_relative "activerecord-ejection_seat/version"
7
11
  require_relative "activerecord-ejection_seat/props_builder"
8
-
9
- require "active_support/lazy_load_hooks"
12
+ require_relative "activerecord-ejection_seat/attributes_builder"
10
13
 
11
14
  ActiveSupport.on_load(:active_record) do
12
15
  require "activerecord-ejection_seat/ejectable"