Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#### Fixes

* [#2538](https://github.com/ruby-grape/grape/pull/2538): Fix validating nested json array params - [@mohammednasser-32](https://github.com/mohammednasser-32).
* [#2543](https://github.com/ruby-grape/grape/pull/2543): Fix array allocation on mount - [@ericproulx](https://github.com/ericproulx).
* Your contribution here.

### 2.3.0 (2025-02-08)

Expand Down
59 changes: 30 additions & 29 deletions lib/grape/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def initial_setup(base_instance_parent)
def override_all_methods!
(base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
define_singleton_method(method_override) do |*args, &block|
add_setup(method_override, *args, &block)
add_setup(method: method_override, args: args, block: block)
end
end
end
Expand All @@ -79,24 +79,24 @@ def configure
# For instance, a description could be done using: `desc configuration[:description]` if it may vary
# depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
# too much, you may actually want to provide a new API rather than remount it.
def mount_instance(opts = {})
instance = Class.new(@base_parent)
instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {})
instance.base = self
replay_setup_on(instance)
instance
def mount_instance(configuration: nil)
Class.new(@base_parent).tap do |instance|
instance.configuration = Grape::Util::EndpointConfiguration.new(configuration || {})
instance.base = self
replay_setup_on(instance)
end
end

private

# Replays the set up to produce an API as defined in this class, can be called
# on classes that inherit from Grape::API
def replay_setup_on(instance)
@setup.each do |setup_step|
replay_step_on(instance, setup_step)
replay_step_on(instance, **setup_step)
end
end

private

def instance_for_rack
if never_mounted?
base_instance
Expand All @@ -106,34 +106,35 @@ def instance_for_rack
end

# Adds a new stage to the set up require to get a Grape::API up and running
def add_setup(method, *args, &block)
setup_step = { method: method, args: args, block: block }
@setup += [setup_step]
def add_setup(step)
@setup << step
last_response = nil
@instances.each do |instance|
last_response = replay_step_on(instance, setup_step)
last_response = replay_step_on(instance, **step)
end

# Updating all previously mounted classes in the case that new methods have been executed.
if method != :mount && @setup.any?
previous_mount_steps = @setup.select { |step| step[:method] == :mount }
previous_mount_steps.each do |mount_step|
refresh_mount_step = mount_step.merge(method: :refresh_mounted_api)
@setup += [refresh_mount_step]
@instances.each do |instance|
replay_step_on(instance, refresh_mount_step)
end
refresh_mount_step if step[:method] != :mount
last_response
end

# Updating all previously mounted classes in the case that new methods have been executed.
def refresh_mount_step
@setup.each do |setup_step|
next if setup_step[:method] != :mount

refresh_mount_step = setup_step.merge(method: :refresh_mounted_api)
@setup << refresh_mount_step
@instances.each do |instance|
replay_step_on(instance, **refresh_mount_step)
end
end

last_response
end

def replay_step_on(instance, setup_step)
return if skip_immediate_run?(instance, setup_step[:args])
def replay_step_on(instance, method:, args:, block:)
return if skip_immediate_run?(instance, args)

args = evaluate_arguments(instance.configuration, *setup_step[:args])
response = instance.send(setup_step[:method], *args, &setup_step[:block])
eval_args = evaluate_arguments(instance.configuration, *args)
response = instance.send(method, *eval_args, &block)
if skip_immediate_run?(instance, [response])
response
else
Expand Down
5 changes: 0 additions & 5 deletions spec/grape/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1699,11 +1699,6 @@ def self.io
expect(subject.io.string).to include(message)
end
end

it 'does not unnecessarily retain duplicate setup blocks' do
subject.logger
expect { subject.logger }.not_to change(subject.instance_variable_get(:@setup), :size)
end
end

describe '.helpers' do
Expand Down