File: collocated_enforcement.rb

package info (click to toggle)
ruby-graphql-client 0.18.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 252 kB
  • sloc: ruby: 1,878; makefile: 4
file content (63 lines) | stat: -rw-r--r-- 2,089 bytes parent folder | download
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
# frozen_string_literal: true
require "graphql/client/error"

module GraphQL
  class Client

    # Collocation will not be enforced if a stack trace includes any of these gems.
    WHITELISTED_GEM_NAMES = %w{pry byebug}

    # Raised when method is called from outside the expected file scope.
    class NonCollocatedCallerError < Error; end

    # Enforcements collocated object access best practices.
    module CollocatedEnforcement
      extend self

      # Public: Ignore collocated caller enforcement for the scope of the block.
      def allow_noncollocated_callers
        Thread.current[:query_result_caller_location_ignore] = true
        yield
      ensure
        Thread.current[:query_result_caller_location_ignore] = nil
      end

      def verify_collocated_path(location, path, method = "method")
        return yield if Thread.current[:query_result_caller_location_ignore]

        if (location.path != path) && !(WHITELISTED_GEM_NAMES.any? { |g| location.path.include?("gems/#{g}") })
          error = NonCollocatedCallerError.new("#{method} was called outside of '#{path}' https://git.io/v1syX")
          error.set_backtrace(caller(2))
          raise error
        end

        begin
          Thread.current[:query_result_caller_location_ignore] = true
          yield
        ensure
          Thread.current[:query_result_caller_location_ignore] = nil
        end
      end

      # Internal: Decorate method with collocated caller enforcement.
      #
      # mod - Target Module/Class
      # methods - Array of Symbol method names
      # path - String filename to assert calling from
      #
      # Returns nothing.
      def enforce_collocated_callers(mod, methods, path)
        mod.prepend(Module.new do
          methods.each do |method|
            define_method(method) do |*args, &block|
              location = caller_locations(1, 1)[0]
              CollocatedEnforcement.verify_collocated_path(location, path, method) do
                super(*args, &block)
              end
            end
          end
        end)
      end
    end
  end
end