boba 0.1.8 → 0.1.9

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: 8f3bbf276bdafe4ad59d597cfa3d481db980d8a1cc0639066289ee72671bee01
4
- data.tar.gz: ed16c9705abe9aaa2997124342b920d748aac6d131bc25b4930a4bdfd8c82e4b
3
+ metadata.gz: c38ab860f2d0913aef856dc5903505a28fdfe971c30be7d116c3f8b1b6f1b366
4
+ data.tar.gz: 16b5fb166214dfd6157efb1c481a4667616ee937c1746d620aef892b457f39be
5
5
  SHA512:
6
- metadata.gz: 3efa241e2373a7566a2f9a565366a750947854c2af1ac7d26e66b934f6b4c18835ca8c58b64ec22f107871b46a1bde563663b1d30a1fe3dea54ba9c7afe8d82c
7
- data.tar.gz: 59a567e245ad5d424bfccc9c101584500235ef2a71282e65cdcba5cb6448267684bc9b6fe5e85309c56db96ec8d0e088fa3a3bc33500b12a81614e9c122ff95e
6
+ metadata.gz: 7a1bed23004b75855101c0c05628d12d3aba8f6f808856402032069310195e3699035659c43337630efbf9d2ac796243ba7c57e83ab633a873e728caab642433
7
+ data.tar.gz: 4542bcfde6d1e4f0dd9538dafde159bab762844d18cfa746468ea71831a846953c3f3d6182eed33253736633f8a5f221f1f0d3519f8201159afa2e2830fb368a
data/lib/boba/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Boba
5
- VERSION = "0.1.8"
5
+ VERSION = "0.1.9"
6
6
  end
@@ -0,0 +1,178 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ return unless defined?(Shrine)
5
+
6
+ module Tapioca
7
+ module Dsl
8
+ module Compilers
9
+ # `Tapioca::Dsl::Compilers::Shrine` decorates RBI files for classes that include
10
+ # a `Shrine::Attachment` module provided by the `shrine` gem.
11
+ # https://github.com/shrinerb/shrine
12
+ #
13
+ # For example, with the following model:
14
+ # ~~~rb
15
+ # class Photo < ActiveRecord::Base
16
+ # include ImageUploader::Attachment(:image)
17
+ # end
18
+ # ~~~
19
+ #
20
+ # This compiler will generate the following RBI:
21
+ # ~~~rbi
22
+ # class Photo
23
+ # include ShrineGeneratedMethods
24
+ # extend ShrineGeneratedClassMethods
25
+ #
26
+ # module ShrineGeneratedClassMethods
27
+ # sig { params(options: T.untyped).returns(::Shrine::Attacher) }
28
+ # def image_attacher(**options); end
29
+ # end
30
+ #
31
+ # module ShrineGeneratedMethods
32
+ # sig { returns(T.nilable(::Shrine::UploadedFile)) }
33
+ # def image; end
34
+ #
35
+ # sig { params(value: T.untyped).returns(T.untyped) }
36
+ # def image=(value); end
37
+ #
38
+ # sig { params(options: T.untyped).returns(T.nilable(::Shrine::Attacher)) }
39
+ # def image_attacher(**options); end
40
+ #
41
+ # sig { returns(T::Boolean) }
42
+ # def image_changed?; end
43
+ #
44
+ # sig { params(args: T.untyped, options: T.untyped).returns(T.nilable(String)) }
45
+ # def image_url(*args, **options); end
46
+ # end
47
+ # end
48
+ # ~~~
49
+ class Shrine < Tapioca::Dsl::Compiler
50
+ include RBIHelper
51
+
52
+ InstanceMethodModuleName = "ShrineGeneratedMethods"
53
+ ClassMethodModuleName = "ShrineGeneratedClassMethods"
54
+
55
+ ConstantType = type_member { { fixed: T.class_of(Object) } }
56
+
57
+ class << self
58
+ # @override
59
+ #: -> Enumerable[Module[top]]
60
+ def gather_constants
61
+ all_classes.select do |klass|
62
+ klass.ancestors.any? { |ancestor| ancestor.is_a?(::Shrine::Attachment) }
63
+ end
64
+ end
65
+ end
66
+
67
+ # @override
68
+ #: -> void
69
+ def decorate
70
+ attachments = shrine_attachments(constant)
71
+ return if attachments.empty?
72
+
73
+ root.create_path(constant) do |klass|
74
+ instance_module = RBI::Module.new(InstanceMethodModuleName)
75
+ class_module = RBI::Module.new(ClassMethodModuleName)
76
+
77
+ attachments.each do |attachment|
78
+ name = attachment.attachment_name
79
+
80
+ # Filter to methods that follow shrine's naming convention (<name> or <name>= or <name>_*).
81
+ # This excludes method overrides like `reload` from the ActiveRecord plugin.
82
+ attachment.instance_methods(false).sort
83
+ .filter { |m| m == name || m == :"#{name}=" || m.start_with?("#{name}_") }
84
+ .each do |method_name|
85
+ method_obj = attachment.instance_method(method_name)
86
+ instance_module.create_method(
87
+ method_name.to_s,
88
+ parameters: compile_parameters(method_obj),
89
+ return_type: return_type_for(name, method_name),
90
+ )
91
+ end
92
+
93
+ # Class method from entity plugin:
94
+ # .<name>_attacher - returns a class-level attacher instance
95
+ next unless constant.respond_to?(:"#{name}_attacher")
96
+
97
+ class_method_obj = constant.method(:"#{name}_attacher")
98
+ class_module.create_method(
99
+ "#{name}_attacher",
100
+ parameters: compile_parameters(class_method_obj),
101
+ return_type: "::Shrine::Attacher",
102
+ )
103
+ end
104
+
105
+ klass << instance_module
106
+ klass.create_include(InstanceMethodModuleName)
107
+ klass << class_module
108
+ klass.create_extend(ClassMethodModuleName)
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ #: (singleton(Object) klass) -> Array[::Shrine::Attachment]
115
+ def shrine_attachments(klass)
116
+ klass.ancestors
117
+ .filter_map { |ancestor| ancestor if ancestor.is_a?(::Shrine::Attachment) }
118
+ .sort_by(&:attachment_name)
119
+ end
120
+
121
+ # Maps a dynamically discovered method name to its return type.
122
+ #
123
+ # Known method patterns (from shrine's entity/model plugins) are given
124
+ # specific return types. Methods that don't match any known pattern
125
+ # (e.g. those added by third-party shrine plugins) fall back to
126
+ # T.untyped so they still appear in the generated RBI.
127
+ #
128
+ # #<name> -> entity plugin: returns the attached file
129
+ # #<name>_url -> entity plugin: returns the URL to the file
130
+ # #<name>_attacher -> entity plugin: returns the Attacher instance
131
+ # #<name>= -> model plugin: assigns a file (setter)
132
+ # #<name>_changed? -> model plugin: checks if attachment changed
133
+ #
134
+ #: (Symbol attachment_name, Symbol method_name) -> String?
135
+ def return_type_for(attachment_name, method_name)
136
+ case method_name
137
+ when attachment_name
138
+ "T.nilable(::Shrine::UploadedFile)"
139
+ when :"#{attachment_name}_url"
140
+ "T.nilable(::String)"
141
+ when :"#{attachment_name}_attacher"
142
+ "T.nilable(::Shrine::Attacher)"
143
+ when /=$/
144
+ nil
145
+ when /\?$/
146
+ "T::Boolean"
147
+ else
148
+ "T.untyped"
149
+ end
150
+ end
151
+
152
+ #: (UnboundMethod | Method method_obj) -> Array[RBI::TypedParam]
153
+ def compile_parameters(method_obj)
154
+ method_obj.parameters.filter_map do |(type, name)|
155
+ name_str = name.to_s
156
+ name_str = "arg" if name_str.empty?
157
+ case type
158
+ when :req
159
+ create_param(name_str, type: "T.untyped")
160
+ when :opt
161
+ create_opt_param(name_str, type: "T.untyped", default: "T.unsafe(nil)")
162
+ when :rest
163
+ create_rest_param(name_str, type: "T.untyped")
164
+ when :keyreq
165
+ create_kw_param(name_str, type: "T.untyped")
166
+ when :key
167
+ create_kw_opt_param(name_str, type: "T.untyped", default: "T.unsafe(nil)")
168
+ when :keyrest
169
+ create_kw_rest_param(name_str, type: "T.untyped")
170
+ when :block
171
+ create_block_param(name_str, type: "T.untyped")
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Angellist
@@ -60,15 +60,16 @@ files:
60
60
  - lib/tapioca/dsl/compilers/money_rails.rb
61
61
  - lib/tapioca/dsl/compilers/noticed.rb
62
62
  - lib/tapioca/dsl/compilers/paperclip.rb
63
+ - lib/tapioca/dsl/compilers/shrine.rb
63
64
  - lib/tapioca/dsl/compilers/state_machines_extended.rb
64
65
  homepage: https://github.com/angellist/boba
65
66
  licenses:
66
67
  - MIT
67
68
  metadata:
68
69
  bug_tracker_uri: https://github.com/angellist/boba/issues
69
- changelog_uri: https://github.com/angellist/boba/blob/0.1.8/History.md
70
+ changelog_uri: https://github.com/angellist/boba/blob/0.1.9/History.md
70
71
  homepage_uri: https://github.com/angellist/boba
71
- source_code_uri: https://github.com/angellist/boba/tree/0.1.8
72
+ source_code_uri: https://github.com/angellist/boba/tree/0.1.9
72
73
  rubygems_mfa_required: 'true'
73
74
  rdoc_options: []
74
75
  require_paths: