orthoses-rails 1.2.0 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9681e80e9a106b8d984c304a0f26777deb5e2077aba6f8a0321f52976e24bcc
4
- data.tar.gz: c1f248d0e236bc72491e507da185317d55d607cebf4d6364209cd1601cacb6aa
3
+ metadata.gz: a9a2d180cb8faa295dc3043ea66a379ee71939436075104c0f85b63d6aede665
4
+ data.tar.gz: e02fa19ea12dfd296936673d67ecc6457b0e16101c18d4851747e0b895dace96
5
5
  SHA512:
6
- metadata.gz: 8ff02244b5844c28186021d4379818a5a0b56ffcb693425f500e3145ff36da721a278c972a6a324e3b3b26bcb3ce9652c98f9fb3c15549fd51924371362d37fb
7
- data.tar.gz: 284d60f37efb290cf6e5230dd10cda55066278e89e4b81866896793c535b4864edc7cb13c389d0c98a861c34630fba09df3bb04b1035291f7c4cc46666d22b9b
6
+ metadata.gz: 9e97ed8964e301618db94df7c0d410310ea3bd9605f66c9d7d0431dc71546e0faaff24f918ce05a58510250f7563934a9adb3977662d3526db0198016801ba77
7
+ data.tar.gz: 5d43746f2bcb1ae068902a66a0da54a852bb173fcdd85ff5d9ce0f07e2051cbbc31e7b1a2c8e0469b28e2ca6ca1661edb76a1665e6d7a3356c6bedb9de97cf00
@@ -17,6 +17,13 @@ namespace :orthoses do
17
17
  # Complement missing const name.
18
18
  use Orthoses::MissingName
19
19
 
20
+ # You can use other publicly available middleware.
21
+ # `Orthoses::YARD` is available at https://github.com/ksss/orthoses-yard.
22
+ # By using this middleware, you can add the capability
23
+ # to generate type information from YARD documentation.
24
+ # use Orthoses::YARD,
25
+ # parse: ['{app/lib}/**/*.rb']
26
+
20
27
  # You can load hand written RBS.
21
28
  # use Orthoses::LoadRBS,
22
29
  # paths: Dir.glob(Rails.root / "sig/hand-written/**/*.rbs")
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ module ActionMailer
5
+ class Base
6
+ def initialize(loader)
7
+ @loader = loader
8
+ end
9
+
10
+ def call
11
+ @loader.call.tap do |store|
12
+ ::ActionMailer::Base.descendants.each do |mailer_class|
13
+ base_name = Utils.module_name(mailer_class) or next
14
+ content = store[base_name]
15
+ mailer_class.action_methods.each do |action_method|
16
+ method_object = mailer_class.instance_method(action_method)
17
+ parameters = method_parameters_to_rbs_method_type(method_object).to_s.gsub(' -> untyped', '')
18
+ content << "def self.#{action_method}: #{parameters} -> ::ActionMailer::MessageDelivery"
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def method_parameters_to_rbs_method_type(method_object)
27
+ required_positionals = []
28
+ optional_positionals = []
29
+ rest_positionals = nil
30
+ trailing_positionals = []
31
+ required_keywords = {}
32
+ optional_keywords = {}
33
+ rest_keywords = nil
34
+ requireds = required_positionals
35
+ block = nil
36
+ untyped = RBS::Types::Bases::Any.new(location: nil)
37
+
38
+ method_object.parameters.each do |kind, name|
39
+ case kind
40
+ when :req
41
+ requireds << ::RBS::Types::Function::Param.new(name: name, type: untyped)
42
+ when :opt
43
+ requireds = trailing_positionals
44
+ optional_positionals << ::RBS::Types::Function::Param.new(name: name, type: untyped)
45
+ when :rest
46
+ requireds = trailing_positionals
47
+ name = nil if name == :*
48
+ rest_positionals = ::RBS::Types::Function::Param.new(name: name, type: untyped)
49
+ when :keyreq
50
+ required_keywords[name] = ::RBS::Types::Function::Param.new(name: nil, type: untyped)
51
+ when :key
52
+ optional_keywords[name] = ::RBS::Types::Function::Param.new(name: nil, type: untyped)
53
+ when :keyrest
54
+ rest_keywords = ::RBS::Types::Function::Param.new(name: name, type: untyped)
55
+ when :block
56
+ block = RBS::Types::Block.new(
57
+ type: RBS::Types::Function.empty(untyped).update(rest_positionals: RBS::Types::Function::Param.new(name: nil, type: untyped)),
58
+ required: true,
59
+ self_type: nil
60
+ )
61
+ else
62
+ raise "bug"
63
+ end
64
+ end
65
+
66
+ function = ::RBS::Types::Function.new(
67
+ required_positionals: required_positionals,
68
+ optional_positionals: optional_positionals,
69
+ rest_positionals: rest_positionals,
70
+ trailing_positionals: trailing_positionals,
71
+ required_keywords: required_keywords,
72
+ optional_keywords: optional_keywords,
73
+ rest_keywords: rest_keywords,
74
+ return_type: untyped,
75
+ )
76
+ ::RBS::MethodType.new(
77
+ location: nil,
78
+ type_params: [],
79
+ type: function,
80
+ block: block
81
+ )
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'action_mailer/base'
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ module ActiveModel
5
+ # < 7.0
6
+ # def attribute(name, type = Type::Value.new, **options)
7
+ # >= 7.0
8
+ # def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
9
+ class Attributes
10
+ DEFAULT_TYPES = {
11
+ big_integer: '::Integer',
12
+ binary: '::String',
13
+ boolean: 'bool',
14
+ date: '::Date',
15
+ datetime: '::Time',
16
+ decimal: '::BigDecimal',
17
+ float: '::Float',
18
+ immutable_string: '::String',
19
+ integer: '::Integer',
20
+ string: '::String',
21
+ time: '::Time'
22
+ }
23
+
24
+ def initialize(loader)
25
+ @loader = loader
26
+ end
27
+
28
+ def call
29
+ attribute = CallTracer::Lazy.new
30
+ store = attribute.trace('ActiveModel::Attributes::ClassMethods#attribute') do
31
+ @loader.call
32
+ end
33
+ attribute.captures.each do |capture|
34
+ receiver_name = Utils.module_name(capture.method.receiver) or next
35
+ name = capture.argument[:name]
36
+ active_model_version = Gem::Version.new(::ActiveModel::VERSION::STRING)
37
+ cast_type =
38
+ if active_model_version >= Gem::Version.new('7.1')
39
+ # https://github.com/rails/rails/commit/608cbfae36b125d7962b7ed9083c9e9e6ce70b88
40
+ capture.argument[:*].first
41
+ elsif active_model_version >= Gem::Version.new('7.0')
42
+ capture.argument[:cast_type]
43
+ else
44
+ capture.argument[:type]
45
+ end
46
+
47
+ return_type = DEFAULT_TYPES[cast_type] || 'untyped'
48
+
49
+ generated_attribute_methods = "#{receiver_name}::ActiveModelGeneratedAttributeMethods"
50
+ c = store[generated_attribute_methods]
51
+ c.header = "module #{generated_attribute_methods}"
52
+ c << "def #{name}: () -> #{return_type}?"
53
+ c << "def #{name}=: (untyped) -> untyped"
54
+
55
+ unless store[receiver_name].body.include?("include #{generated_attribute_methods}")
56
+ store[receiver_name] << "include #{generated_attribute_methods}"
57
+ end
58
+ end
59
+
60
+ store
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'active_model/attributes'
3
4
  require_relative 'active_model/has_secure_password'
@@ -67,15 +67,35 @@ module Orthoses
67
67
 
68
68
  return_type_param = Hash === values ? "[String, String]" : "[String, Integer]"
69
69
  store[base_name] << "def self.#{name.pluralize}: () -> ActiveSupport::HashWithIndifferentAccess#{return_type_param}"
70
+ enum_methods_content = store["#{base_name}::ActiveRecord_Enum_EnumMethods"]
71
+ enum_methods_content.header = "module #{base_name}::ActiveRecord_Enum_EnumMethods"
70
72
 
71
- pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
73
+ pairs = (values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index).to_h
72
74
  pairs.each do |label, value|
73
75
  value_method_name = "#{prefix}#{label}#{suffix}"
74
- enum_methods_content = store["#{base_name}::ActiveRecord_Enum_EnumMethods"]
75
- enum_methods_content.header = "module #{base_name}::ActiveRecord_Enum_EnumMethods"
76
76
  enum_methods_content << "def #{value_method_name}?: () -> bool"
77
77
  enum_methods_content << "def #{value_method_name}!: () -> bool"
78
+
79
+ value_method_alias = "#{prefix}#{label.to_s.gsub(/[\W&&[:ascii:]]+/, "_")}#{suffix}"
80
+ if value_method_alias != value_method_name
81
+ enum_methods_content << "def #{value_method_alias}?: () -> bool"
82
+ enum_methods_content << "def #{value_method_alias}!: () -> bool"
83
+ end
84
+ end
85
+
86
+ # Expressing type casting.
87
+ store[base_name] << "def #{name}: () -> (#{pairs.keys.map { |k| "\"#{k}\"" }.join(" | ")})"
88
+ symbol_pattern = if pairs.keys.any? { |key| key.match?(/[^a-zA-Z_]/) }
89
+ "(Symbol) -> void"
90
+ else
91
+ "(#{pairs.keys.map{|key|key.to_sym.inspect}.join(" | ")}) -> void"
78
92
  end
93
+ overloads = [
94
+ symbol_pattern,
95
+ "(#{pairs.keys.map{|key|key.to_s.inspect}.join(" | ")}) -> void",
96
+ "(#{pairs.values.map(&:inspect).join(" | ")}) -> void",
97
+ ]
98
+ store[base_name] << "def #{name}=: #{overloads.join(" | ")}"
79
99
  end
80
100
  end
81
101
  end
@@ -4,8 +4,6 @@ module Orthoses
4
4
  module ActiveRecord
5
5
  class GeneratedAttributeMethods
6
6
  TARGET_TYPE_MAP = {
7
- "attribute" => "() -> %<type>s",
8
- "attribute=" => "(%<type>s) -> %<type>s",
9
7
  "attribute?" => "() -> bool",
10
8
  "attribute_before_last_save" => "() -> %<opt>s",
11
9
  "attribute_before_type_cast" => "() -> %<type>s",
@@ -50,13 +48,14 @@ module Orthoses
50
48
  opt = "#{req}?"
51
49
  type = col.null ? opt : req
52
50
 
53
- ::ActiveRecord::Base.attribute_method_matchers.each do |matcher|
54
- tmpl = TARGET_TYPE_MAP[matcher.target]
51
+ lines << "attr_accessor #{name}: #{type}"
52
+ attribute_method_patterns(::ActiveRecord::Base).each do |matcher|
53
+ tmpl = TARGET_TYPE_MAP[matcher.proxy_target] or next
55
54
  lines << "def #{matcher.method_name(name)}: #{tmpl % {type: type, opt: opt}}"
56
55
  end
57
56
  end
58
57
  klass.attribute_aliases.each do |alias_name, column_name|
59
- ::ActiveRecord::Base.attribute_method_matchers.each do |matcher|
58
+ attribute_method_patterns(::ActiveRecord::Base).each do |matcher|
60
59
  lines << "alias #{matcher.method_name(alias_name)} #{matcher.method_name(column_name)}"
61
60
  end
62
61
  end
@@ -69,6 +68,20 @@ module Orthoses
69
68
  end
70
69
  end
71
70
  end
71
+
72
+ private
73
+
74
+ def attribute_method_patterns(const)
75
+ if const.respond_to?(:attribute_method_patterns)
76
+ const.attribute_method_patterns
77
+ else
78
+ const.attribute_method_matchers.each do |matcher|
79
+ def matcher.proxy_target
80
+ target
81
+ end
82
+ end
83
+ end
84
+ end
72
85
  end
73
86
  end
74
87
  end
@@ -19,7 +19,16 @@ module Orthoses
19
19
  class_specific_proxy = "#{model_name}::ActiveRecord_Associations_CollectionProxy"
20
20
  class_specific_generated_relation_methods = "#{model_name}::GeneratedRelationMethods"
21
21
 
22
+ # Expressing delegation.
22
23
  store[class_specific_generated_relation_methods].tap do |c|
24
+ klass.singleton_methods(false).each do |singleton_method|
25
+ c << "def #{singleton_method}: (*untyped, **untyped) -> untyped"
26
+ end
27
+ (klass.singleton_class.included_modules - ::ActiveRecord::Relation.included_modules).each do |mod|
28
+ mname = Utils.module_name(mod) or next
29
+ store[mname].header = "module #{mname}"
30
+ c << "include #{mname}"
31
+ end
23
32
  end
24
33
 
25
34
  store[class_specific_relation].tap do |c|
@@ -20,7 +20,7 @@ module Orthoses
20
20
  name = capture.argument[:name]
21
21
  body = capture.argument[:body]
22
22
 
23
- definition = "#{name}: #{parameters_to_type(body.parameters)} -> #{base_name}::ActiveRecord_Relation"
23
+ definition = "#{name}: #{parameters_to_type(parameters(body))} -> #{base_name}::ActiveRecord_Relation"
24
24
  store[base_name] << "def self.#{definition}"
25
25
  store["#{base_name}::GeneratedRelationMethods"].header = "module #{base_name}::GeneratedRelationMethods"
26
26
  store["#{base_name}::GeneratedRelationMethods"] << "def #{definition}"
@@ -58,6 +58,14 @@ module Orthoses
58
58
  end
59
59
  "(#{res.join(", ")})#{block}"
60
60
  end
61
+
62
+ def parameters(body)
63
+ if body.respond_to?(:to_proc)
64
+ body.to_proc.parameters
65
+ else
66
+ body.method(:call).parameters
67
+ end
68
+ end
61
69
  end
62
70
  end
63
71
  end
@@ -36,7 +36,7 @@ module Orthoses
36
36
  def members_prototype_of(mod_name)
37
37
  mod = Object.const_get(mod_name)
38
38
  runtime = ::RBS::Prototype::Runtime.new(patterns: nil, env: nil, merge: false)
39
- type_name = runtime.to_type_name(mod_name)
39
+ type_name = TypeName(mod_name)
40
40
  [].tap do |members|
41
41
  runtime.generate_methods(mod, type_name, members)
42
42
  end
@@ -17,6 +17,9 @@ module Orthoses
17
17
  def call
18
18
  loader = @loader
19
19
  Orthoses::Builder.new do
20
+ use Orthoses::ActionMailer::Base
21
+
22
+ use Orthoses::ActiveModel::Attributes
20
23
  use Orthoses::ActiveModel::HasSecurePassword
21
24
 
22
25
  use Orthoses::ActiveRecord::BelongsTo
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Orthoses
4
4
  module Rails
5
- VERSION = "1.2.0"
5
+ VERSION = "1.4.0"
6
6
  end
7
7
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'orthoses'
4
4
 
5
+ require_relative 'action_mailer'
5
6
  require_relative 'active_model'
6
7
  require_relative 'active_record'
7
8
  require_relative 'active_storage'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orthoses-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ksss
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-02 00:00:00.000000000 Z
11
+ date: 2023-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: orthoses
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.11'
19
+ version: '1.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.11'
26
+ version: '1.13'
27
27
  description: Orthoses middleware collection for Ruby on Rails
28
28
  email:
29
29
  - co000ri@gmail.com
@@ -37,7 +37,10 @@ files:
37
37
  - lib/generators/orthoses/rails/install_generator.rb
38
38
  - lib/generators/orthoses/rails/templates/rails.rake
39
39
  - lib/orthoses-rails.rb
40
+ - lib/orthoses/action_mailer.rb
41
+ - lib/orthoses/action_mailer/base.rb
40
42
  - lib/orthoses/active_model.rb
43
+ - lib/orthoses/active_model/attributes.rb
41
44
  - lib/orthoses/active_model/has_secure_password.rb
42
45
  - lib/orthoses/active_record.rb
43
46
  - lib/orthoses/active_record/belongs_to.rb