1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
# frozen_string_literal: true
require 'active_support/concern'
module Grape
module DSL
# Keeps track of settings (implemented as key-value pairs, grouped by
# types), in two contexts: top-level settings which apply globally no
# matter where they're defined, and inheritable settings which apply only
# in the current scope and scopes nested under it.
module Settings
extend ActiveSupport::Concern
attr_writer :inheritable_setting, :top_level_setting
# Fetch our top-level settings, which apply to all endpoints in the API.
def top_level_setting
@top_level_setting ||= build_top_level_setting
end
# Fetch our current inheritable settings, which are inherited by
# nested scopes but not shared across siblings.
def inheritable_setting
@inheritable_setting ||= Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from top_level_setting }
end
# @param type [Symbol]
# @param key [Symbol]
def unset(type, key)
setting = inheritable_setting.send(type)
setting.delete key
end
# @param type [Symbol]
# @param key [Symbol]
# @param value [Object] will be stored if the value is currently empty
# @return either the old value, if it wasn't nil, or the given value
def get_or_set(type, key, value)
setting = inheritable_setting.send(type)
if value.nil?
setting[key]
else
setting[key] = value
end
end
# @param key [Symbol]
# @param value [Object]
# @return (see #get_or_set)
def global_setting(key, value = nil)
get_or_set :global, key, value
end
# @param key [Symbol]
def unset_global_setting(key)
unset :global, key
end
# (see #global_setting)
def route_setting(key, value = nil)
get_or_set :route, key, value
end
# (see #unset_global_setting)
def unset_route_setting(key)
unset :route, key
end
# (see #global_setting)
def namespace_setting(key, value = nil)
get_or_set :namespace, key, value
end
# (see #unset_global_setting)
def unset_namespace_setting(key)
unset :namespace, key
end
# (see #global_setting)
def namespace_inheritable(key, value = nil)
get_or_set :namespace_inheritable, key, value
end
# (see #unset_global_setting)
def unset_namespace_inheritable(key)
unset :namespace_inheritable, key
end
# @param key [Symbol]
def namespace_inheritable_to_nil(key)
inheritable_setting.namespace_inheritable[key] = nil
end
# (see #global_setting)
def namespace_stackable(key, value = nil)
get_or_set :namespace_stackable, key, value
end
def namespace_reverse_stackable(key, value = nil)
get_or_set :namespace_reverse_stackable, key, value
end
def namespace_stackable_with_hash(key)
settings = get_or_set :namespace_stackable, key, nil
return if settings.blank?
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
end
def namespace_reverse_stackable_with_hash(key)
settings = get_or_set :namespace_reverse_stackable, key, nil
return if settings.blank?
result = {}
settings.each do |setting|
setting.each do |field, value|
result[field] ||= value
end
end
result
end
# (see #unset_global_setting)
def unset_namespace_stackable(key)
unset :namespace_stackable, key
end
# (see #global_setting)
def api_class_setting(key, value = nil)
get_or_set :api_class, key, value
end
# (see #unset_global_setting)
def unset_api_class_setting(key)
unset :api_class, key
end
# Fork our inheritable settings to a new instance, copied from our
# parent's, but separate so we won't modify it. Every call to this
# method should have an answering call to #namespace_end.
def namespace_start
@inheritable_setting = Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from inheritable_setting }
end
# Set the inheritable settings pointer back up by one level.
def namespace_end
route_end
@inheritable_setting = inheritable_setting.parent
end
# Stop defining settings for the current route and clear them for the
# next, within a namespace.
def route_end
inheritable_setting.route_end
end
# Execute the block within a context where our inheritable settings are forked
# to a new copy (see #namespace_start).
def within_namespace(&block)
namespace_start
result = yield if block
namespace_end
reset_validations!
result
end
private
# Builds the current class :inheritable_setting. If available, it inherits from
# the superclass's :inheritable_setting.
def build_top_level_setting
Grape::Util::InheritableSetting.new.tap do |setting|
# Doesn't try to inherit settings from +Grape::API::Instance+ which also responds to
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
# in the chain for every endpoint.
setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
end
end
end
end
end
|