activerecord-ejection_seat 0.2.0 → 0.3.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 (47) 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/Gemfile +2 -2
  6. data/Gemfile.lock +42 -55
  7. data/README.md +16 -6
  8. data/Rakefile +6 -6
  9. data/activerecord-ejection_seat.gemspec +40 -0
  10. data/lib/activerecord-ejection_seat/attributes_builder.rb +42 -0
  11. data/lib/activerecord-ejection_seat/ejectable.rb +7 -10
  12. data/lib/activerecord-ejection_seat/props_builder.rb +35 -23
  13. data/lib/activerecord-ejection_seat/version.rb +2 -2
  14. data/lib/activerecord-ejection_seat.rb +6 -3
  15. data/sorbet/rbi/gems/activemodel@7.0.4.1.rbi +6022 -0
  16. data/sorbet/rbi/gems/activerecord@7.0.4.1.rbi +37791 -0
  17. data/sorbet/rbi/gems/activesupport@7.0.4.1.rbi +18127 -0
  18. data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +11626 -2
  19. data/sorbet/rbi/gems/minitest@5.17.0.rbi +2319 -0
  20. data/sorbet/rbi/gems/{parser@3.1.3.0.rbi → parser@3.2.0.0.rbi} +907 -821
  21. data/sorbet/rbi/gems/{prettier_print@1.1.0.rbi → prettier_print@1.2.0.rbi} +0 -0
  22. data/sorbet/rbi/gems/{rubocop-ast@1.24.0.rbi → rubocop-ast@1.24.1.rbi} +702 -458
  23. data/sorbet/rbi/gems/rubocop-minitest@0.26.1.rbi +2320 -0
  24. data/sorbet/rbi/gems/{rubocop@1.41.0.rbi → rubocop@1.43.0.rbi} +2743 -625
  25. data/sorbet/rbi/gems/{ruby-lsp@0.3.7.rbi → ruby-lsp@0.3.8.rbi} +2 -1
  26. data/sorbet/rbi/gems/{sqlite3@1.5.4.rbi → sqlite3@1.6.0.rbi} +73 -0
  27. data/sorbet/rbi/gems/{syntax_tree@4.3.0.rbi → syntax_tree@5.2.0.rbi} +0 -0
  28. data/sorbet/rbi/gems/{tapioca@0.10.4.rbi → tapioca@0.10.5.rbi} +279 -96
  29. data/sorbet/rbi/gems/{unicode-display_width@2.3.0.rbi → unicode-display_width@2.4.2.rbi} +26 -7
  30. data/sorbet/rbi/gems/{unparser@0.6.5.rbi → unparser@0.6.7.rbi} +323 -64
  31. data/sorbet/rbi/gems/{yard-sorbet@0.7.0.rbi → yard-sorbet@0.8.0.rbi} +91 -41
  32. data/sorbet/rbi/shims/location.rbi +13 -0
  33. data/sorbet/rbi/shims/post.rbi +16 -0
  34. data/sorbet/rbi/shims/user.rbi +19 -0
  35. data/sorbet/rbi/todo.rbi +0 -4
  36. data/sorbet/tapioca/require.rb +16 -2
  37. metadata +29 -27
  38. data/sorbet/rbi/gems/activemodel@7.0.4.rbi +0 -8
  39. data/sorbet/rbi/gems/activerecord@7.0.4.rbi +0 -11
  40. data/sorbet/rbi/gems/activesupport@7.0.4.rbi +0 -93
  41. data/sorbet/rbi/gems/minitest@5.16.3.rbi +0 -8
  42. data/sorbet/rbi/gems/rspec-core@3.12.0.rbi +0 -10588
  43. data/sorbet/rbi/gems/rspec-expectations@3.12.1.rbi +0 -7817
  44. data/sorbet/rbi/gems/rspec-mocks@3.12.1.rbi +0 -4994
  45. data/sorbet/rbi/gems/rspec-support@3.12.0.rbi +0 -1477
  46. data/sorbet/rbi/gems/rspec@3.12.0.rbi +0 -10
  47. data/sorbet/rbi/gems/rubocop-rspec@2.16.0.rbi +0 -7650
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec011864f722a24abd09c60e4dbfd8c53e1a41a2922ef6827a527f3992d5b941
4
- data.tar.gz: ea35286dccfb37c99279b6ffe6403fd20ee72c8a488103bdaa4386e7f6c8b205
3
+ metadata.gz: f8cb74f8d55bb740891ac604fbb974cbbc78ce7e01bd67eb739a01cd4cdae756
4
+ data.tar.gz: c14a4d5a2382acda29d63c3a77bbf0e4080eff154e3b6cccfb4b91aa13b120e1
5
5
  SHA512:
6
- metadata.gz: a2dfb9277f769f3e9296a5388459e02acfa98ae0697009e3e8587dbaed774d45ec059652a7ffdf61c5a2a36c62b309e32fa10b834f20a8e84d145fc3c6f78dda
7
- data.tar.gz: b39ccda5b3aa68a6d63f9be7959f70daf7a8e262f67d0b4afbf9bd250c1f1cb42f25d28592b5223ccd46857548a2ce7936d061bc9f0b62030bbe870b03e7d5c3
6
+ metadata.gz: e4f736acf5cf15bab547d35f2cf4c21963a6a188029df0c23f59dbbbc900ef7474f0de67013ccac7afa853d832f5fb839e0503019b37a90a2508cab7ce0def55
7
+ data.tar.gz: 787f28eaa9428fbe03e6d48be126d591af896da66e4a360c250e9d9b70ae9d3a03505f66134b53e6ba8be927cbced95b52fd12d2e8a288e582a9189618d307e6
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.0
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.0
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.0)
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.1)
12
+ activesupport (= 7.0.4.1)
13
+ activerecord (7.0.4.1)
14
+ activemodel (= 7.0.4.1)
15
+ activesupport (= 7.0.4.1)
16
+ activesupport (7.0.4.1)
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
22
  concurrent-ruby (1.1.10)
23
- debug (1.7.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)
@@ -31,12 +31,12 @@ GEM
31
31
  reline (>= 0.3.0)
32
32
  json (2.6.3)
33
33
  language_server-protocol (3.17.0.2)
34
- minitest (5.16.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.0.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)
@@ -48,62 +48,49 @@ GEM
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.43.0)
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)
60
+ unicode-display_width (>= 2.4.0, < 3.0)
61
+ rubocop-ast (1.24.1)
75
62
  parser (>= 3.1.1.0)
63
+ rubocop-minitest (0.26.1)
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
67
  rubocop-sorbet (0.6.11)
81
68
  rubocop (>= 0.90.0)
82
- ruby-lsp (0.3.7)
69
+ ruby-lsp (0.3.8)
83
70
  language_server-protocol (~> 3.17.0)
84
71
  sorbet-runtime
85
- syntax_tree (>= 4.0.2, < 5.0.0)
72
+ syntax_tree (>= 5.0.0, < 6)
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.10626)
75
+ sorbet-static (= 0.5.10626)
76
+ sorbet-runtime (0.5.10626)
77
+ sorbet-static (0.5.10626-universal-darwin-21)
78
+ sorbet-static (0.5.10626-universal-darwin-22)
79
+ sorbet-static (0.5.10626-x86_64-linux)
80
+ sorbet-static-and-runtime (0.5.10626)
81
+ sorbet (= 0.5.10626)
82
+ sorbet-runtime (= 0.5.10626)
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 (5.2.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)
@@ -115,14 +102,14 @@ GEM
115
102
  thor (1.2.1)
116
103
  tzinfo (2.0.5)
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.4
data/README.md CHANGED
@@ -20,13 +20,21 @@ 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 and a `role` enum column. Another way to express this would be a `User` type that is a simple, typed struct.
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
+
27
34
  class User < T::Struct
28
35
  const :name, String
29
36
  const :age, Integer
37
+ const :role, UserRoles
30
38
  end
31
39
  end
32
40
  ```
@@ -35,6 +43,8 @@ In our model, we can specify an ejection to this type.
35
43
 
36
44
  ```ruby
37
45
  class User
46
+ enum :role, { admin: "admin", member: "member" }
47
+
38
48
  ejects_to Types::User
39
49
  end
40
50
  ```
@@ -42,17 +52,17 @@ end
42
52
  Now, we have two new methods available on `User`. First, we can eject from a `User` instance to a `Types::User`.
43
53
 
44
54
  ```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
55
+ User.new(name: "Max", age: 28, role: "admin").eject
56
+ # => Types::User(name: "Max", age: 28, role: Types::UserRoles::Admin)
57
+ User.new(name: "Max", age: 28, role: "admin").to_struct # alias
48
58
  ```
49
59
 
50
60
  Second, we can buckle into the `User` model with a `Types::User`.
51
61
 
52
62
  ```ruby
53
- user_struct = Types::User.new(name: "Max", age: 28)
63
+ user_struct = Types::User.new(name: "Max", age: 28, role: Types::UserRoles::Admin)
54
64
  User.buckle(user_struct)
55
- # => User(name: "Max", age: 28)
65
+ # => User(name: "Max", age: 28, role: "admin")
56
66
  User.from_struct(user_struct) # alias
57
67
  ```
58
68
 
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 = ["max@audaciousaardvark.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/AudaciousAardvark/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/AudaciousAardvark/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.0"
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"