lib/tapioca/compilers/dsl/action_controller_helpers.rb in tapioca-0.4.27 vs lib/tapioca/compilers/dsl/action_controller_helpers.rb in tapioca-0.5.0

- old
+ new

@@ -1,10 +1,8 @@ # typed: strict # frozen_string_literal: true -require "parlour" - begin require "action_controller" rescue LoadError return end @@ -70,58 +68,100 @@ class ActionControllerHelpers < Base extend T::Sig sig do override - .params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(::ActionController::Base)) + .params(root: RBI::Tree, constant: T.class_of(::ActionController::Base)) .void end def decorate(root, constant) + helpers_module = constant._helpers + proxied_helper_methods = constant._helper_methods.map(&:to_s).map(&:to_sym) + helper_proxy_name = "HelperProxy" helper_methods_name = "HelperMethods" - proxied_helper_methods = constant._helper_methods.map(&:to_s).map(&:to_sym) # Define the helpers method - root.path(constant) do |controller| - create_method(controller, 'helpers', return_type: helper_proxy_name) + root.create_path(constant) do |controller| + controller.create_method("helpers", return_type: helper_proxy_name) # Create helper method module controller.create_module(helper_methods_name) do |helper_methods| - helpers_module = constant._helpers + # If the controller has no helper defined, then it just inherits + # the Action Controlller base helper methods module, so we should + # just add that as an include and stop doing more processing. + if helpers_module.name == "ActionController::Base::HelperMethods" + next helper_methods.create_include(T.must(qualified_name_of(helpers_module))) + end + # Find all the included helper modules and generate an include + # for each of those helper modules gather_includes(helpers_module).each do |ancestor| helper_methods.create_include(ancestor) end + # Generate a method definition in the helper module for each + # helper method defined via the `helper_method` call in the controller. helpers_module.instance_methods(false).each do |method_name| method = if proxied_helper_methods.include?(method_name) - constant.instance_method(method_name) + helper_method_proxy_target(constant, method_name) else helpers_module.instance_method(method_name) end - create_method_from_def(helper_methods, method) + + if method + create_method_from_def(helper_methods, method) + else + create_unknown_proxy_method(helper_methods, method_name) + end end end # Create helper proxy class - controller.create_class(helper_proxy_name, superclass: "::ActionView::Base") do |proxy| + controller.create_class(helper_proxy_name, superclass_name: "::ActionView::Base") do |proxy| proxy.create_include(helper_methods_name) end end end sig { override.returns(T::Enumerable[Module]) } def gather_constants - ::ActionController::Base.descendants.reject(&:abstract?).select(&:name) + descendants_of(::ActionController::Base).reject(&:abstract?).select(&:name) end private + sig do + params( + constant: T.class_of(::ActionController::Base), + method_name: Symbol + ).returns(T.nilable(UnboundMethod)) + end + def helper_method_proxy_target(constant, method_name) + # Lookup the proxy target method only if it is defined as a public/protected or private method. + if constant.method_defined?(method_name) || constant.private_method_defined?(method_name) + constant.instance_method(method_name) + end + end + + sig { params(helper_methods: RBI::Scope, method_name: Symbol).void } + def create_unknown_proxy_method(helper_methods, method_name) + helper_methods.create_method( + method_name.to_s, + parameters: [ + create_rest_param("args", type: "T.untyped"), + create_kw_rest_param("kwargs", type: "T.untyped"), + create_block_param("blk", type: "T.untyped"), + ], + return_type: "T.untyped" + ) + end + sig { params(mod: Module).returns(T::Array[String]) } def gather_includes(mod) mod.ancestors .reject { |ancestor| ancestor.is_a?(Class) || ancestor == mod || ancestor.name.nil? } - .map { |ancestor| "::#{ancestor.name}" } + .map { |ancestor| T.must(qualified_name_of(ancestor)) } .reverse end end end end