Class: Routing::OrganizationsHelper::MappedHelpers

Inherits:
Object
  • Object
show all
Defined in:
app/helpers/routing/organizations_helper.rb

Overview

Provides organization-aware url helpers by automatically switching between organization-scoped routes (/o/:organization_path/…) and global routes based on the current organization context.

This class iterates through existing URL helpers and maps between global and Organization helpers by matching helper names.

Constant Summary collapse

ORGANIZATION_PATH_PATTERN =
'/o/:organization_path'
ORGANIZATION_PATH_REGEX =
%r{(?<=^|_)organizations?_}
PATH_SUFFIX =
'_path'
URL_SUFFIX =
'_url'

Class Method Summary collapse

Class Method Details

.build_override_module(route_pairs) ⇒ Object

Build a module that overrides URL helpers with organization-aware versions



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
# File 'app/helpers/routing/organizations_helper.rb', line 78

def self.build_override_module(route_pairs)
  Module.new do
    route_pairs.each do |global_route, org_route|
      [PATH_SUFFIX, URL_SUFFIX].each do |suffix|
        method_name = "#{global_route}#{suffix}"
        org_method_name = "#{org_route}#{suffix}"

        define_method(method_name) do |*args, **kwargs|
          current_organization = Routing::OrganizationsHelper::MappedHelpers.current_organization

          # Handle Ruby 2.4+ keyword argument compatibility
          # If kwargs is empty but last arg is a hash, treat it as kwargs
          kwargs = args.pop if kwargs.empty? && args.last.is_a?(Hash) && !args.last.frozen?

          if current_organization && current_organization.scoped_paths?
            kwargs[:organization_path] ||= current_organization.path
          end

          if kwargs[:organization_path]
            # Call the Organization helper method
            method(org_method_name).call(*args, **kwargs)
          else
            # Call the original helper method
            super(*args, **kwargs)
          end
        end
      end
    end
  end
end

.build_route_pairs(organization_routes, global_routes) ⇒ Object

Build a Hash of global route => Organization route names.



56
57
58
59
60
61
62
63
64
65
66
67
# File 'app/helpers/routing/organizations_helper.rb', line 56

def self.build_route_pairs(organization_routes, global_routes)
  org_route_names = organization_routes.map(&:name)
  global_route_names = global_routes.map(&:name)

  # Global route => Organization route
  org_route_names.each_with_object({}) do |org_route_name, route_pairs|
    global_route_name = extract_global_route_name(org_route_name)
    next unless global_route_names.include?(global_route_name)

    route_pairs[global_route_name] = org_route_name
  end
end

.current_organizationObject



38
39
40
41
42
# File 'app/helpers/routing/organizations_helper.rb', line 38

def self.current_organization
  # rubocop:disable Gitlab/AvoidCurrentOrganization -- Current organization not available earlier.
  Current.organization_assigned && Current.organization
  # rubocop:enable Gitlab/AvoidCurrentOrganization
end

.extract_global_route_name(org_route_name) ⇒ Object

Map organization named route to global route.



70
71
72
73
74
75
# File 'app/helpers/routing/organizations_helper.rb', line 70

def self.extract_global_route_name(org_route_name)
  return if org_route_name.nil?

  # Handle organization patterns with proper underscore preservation
  org_route_name.gsub(ORGANIZATION_PATH_REGEX, '')
end

.find_route_pairsObject



44
45
46
47
48
# File 'app/helpers/routing/organizations_helper.rb', line 44

def self.find_route_pairs
  all_routes = Rails.application.routes.routes
  org_routes, global_routes = all_routes.partition { |route| organization_route?(route) }
  build_route_pairs(org_routes, global_routes)
end

.installObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'app/helpers/routing/organizations_helper.rb', line 21

def self.install
  return if already_installed

  url_helpers = Gitlab::Application.routes.url_helpers

  # Preserve the original root_url and root_path for use in specific circumstances.
  url_helpers.alias_method :unscoped_root_url, :root_url if url_helpers.respond_to?(:root_url)
  url_helpers.alias_method :unscoped_root_path, :root_path if url_helpers.respond_to?(:root_path)

  # Override URL helpers to be Organization context aware.
  route_pairs = find_route_pairs
  override_module = build_override_module(route_pairs)
  url_helpers.prepend(override_module)

  self.already_installed = true
end

.organization_route?(route) ⇒ Boolean

Route name represents an Organization route.

Returns:

  • (Boolean)


51
52
53
# File 'app/helpers/routing/organizations_helper.rb', line 51

def self.organization_route?(route)
  route.path.spec.to_s.include?(ORGANIZATION_PATH_PATTERN)
end