diff --git a/lib/interfaces/proxy.rb b/lib/interfaces/proxy.rb index daabde8..ac2ff80 100644 --- a/lib/interfaces/proxy.rb +++ b/lib/interfaces/proxy.rb @@ -10,7 +10,7 @@ class Proxy attr_reader :name - def_delegators :@source, :file_path, :lines, :lines=, :start_line, :end_line, :scope + def_delegators :@source, :file_path, :lines, :lines=, :start_line, :end_line, :start_index, :scope def_delegators :@source, :wrap, :export def initialize(name:, source:) diff --git a/lib/models/source.rb b/lib/models/source.rb index a4b6c8d..60b58c3 100644 --- a/lib/models/source.rb +++ b/lib/models/source.rb @@ -38,8 +38,6 @@ def export lines[start_index..end_index].join end - private - def start_index start_line - 1 end diff --git a/lib/proxies/method_proxy.rb b/lib/proxies/method_proxy.rb index 2a1338c..74e9d7a 100644 --- a/lib/proxies/method_proxy.rb +++ b/lib/proxies/method_proxy.rb @@ -29,6 +29,15 @@ def tagged_params(tag) @tagged_params[tag] || [] end + def rewrite_signature + old_line = lines[start_index] + scope_prefix = old_line.match?(/def self\./) ? 'def self.' : 'def ' + all_params = @params.map { |p| p.expression ? p.export(typed: false) : p.name.to_s } + return_suffix = @return_proxy ? " #{@return_proxy.export}" : '' + indent = old_line[/^\s*/] + lines[start_index] = "#{indent}#{scope_prefix}#{@name}(#{all_params.join(', ')})#{return_suffix}\n" + end + def expressions? @params.any?(&:expression) || @return_proxy end diff --git a/lib/proxies/param_proxy.rb b/lib/proxies/param_proxy.rb index e0e1bac..22c6240 100644 --- a/lib/proxies/param_proxy.rb +++ b/lib/proxies/param_proxy.rb @@ -18,7 +18,42 @@ def initialize(name:, source:, type:, position: nil, value: :LOWKEY_UNDEFINED, e end def required? - @value == :LOWKEY_UNDEFINED + @value == :LOWKEY_UNDEFINED || @value == ':LOWKEY_UNDEFINED' + end + + def export(typed: true) + return @source.export if typed + return untyped_keyword_param if %i[key_req key_opt].include?(@type) + + untyped_positional_param + end + + private + + def untyped_keyword_param + default = resolved_default + default.nil? ? "#{@name}:" : "#{@name}: #{default}" + end + + def untyped_positional_param + default = resolved_default + default.nil? ? @name.to_s : "#{@name} = #{default}" + end + + def resolved_default + if @expression + return nil if @expression.default_value == :LOW_TYPE_UNDEFINED + return nil if @expression.default_value.nil? && required? + + value = @expression.default_value + return value.value.inspect if value.is_a?(ValueExpression) + + return value.inspect + end + + return nil if @value == :LOWKEY_UNDEFINED || @value == ':LOWKEY_UNDEFINED' + + @value end end end diff --git a/spec/units/method_proxy_rewrite_spec.rb b/spec/units/method_proxy_rewrite_spec.rb new file mode 100644 index 0000000..f7975b8 --- /dev/null +++ b/spec/units/method_proxy_rewrite_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'prism' +require_relative '../../lib/lowkey' + +RSpec.describe Lowkey::MethodProxy do + subject(:file_proxy) { Lowkey.load('spec/fixtures/mock_node.rbx') } + + let(:method_proxy) { file_proxy['Lowkey::MockNode'][:render] } + + describe '#rewrite_signature' do + it 'replaces the signature line in the shared lines array' do + original_line = method_proxy.lines[method_proxy.start_index].dup + method_proxy.rewrite_signature + rewritten = method_proxy.lines[method_proxy.start_index] + expect(rewritten).not_to eq(original_line) + end + + it 'removes type annotations from the signature' do + method_proxy.rewrite_signature + rewritten = method_proxy.lines[method_proxy.start_index] + expect(rewritten).not_to match(/String|Integer|Symbol/) + end + + it 'preserves the method name' do + method_proxy.rewrite_signature + rewritten = method_proxy.lines[method_proxy.start_index] + expect(rewritten).to include('def render') + end + + it 'is reflected in file_proxy.export since lines are shared' do + method_proxy.rewrite_signature + expect(file_proxy.export).to include(method_proxy.lines[method_proxy.start_index].strip) + end + end +end diff --git a/spec/units/param_proxy_spec.rb b/spec/units/param_proxy_spec.rb new file mode 100644 index 0000000..f98d278 --- /dev/null +++ b/spec/units/param_proxy_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'prism' +require_relative '../../lib/lowkey' + +RSpec.describe Lowkey::ParamProxy do + subject(:file_proxy) { Lowkey.load('spec/fixtures/mock_node.rbx') } + + let(:method_proxy) { file_proxy['Lowkey::MockNode'][:render] } + + describe '#export' do + context 'typed: true (default)' do + it 'returns the raw source for a positional param' do + param = method_proxy[:one] + expect(param.export).to eq(param.export(typed: true)) + end + + it 'returns the raw source for a keyword param' do + param = method_proxy[:three] + expect(param.export).to eq(param.export(typed: true)) + end + end + + context 'typed: false with no expression set' do + it 'returns plain name for a required positional param' do + param = method_proxy[:one] + expect(param.export(typed: false)).to eq('one') + end + + it 'returns plain name for a required keyword param' do + param = method_proxy[:three] + expect(param.export(typed: false)).to eq('three:') + end + + it 'returns name with default for an optional positional param' do + param = method_proxy[:two] + expect(param.export(typed: false)).to eq("two = 'mock value'") + end + + it 'returns name with default for an optional keyword param' do + param = method_proxy[:four] + expect(param.export(typed: false)).to eq("four: 'mock value'") + end + end + end +end