File: request.rb

package info (click to toggle)
ruby-paper-trail 12.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,200 kB
  • sloc: ruby: 6,743; makefile: 6
file content (166 lines) | stat: -rw-r--r-- 5,142 bytes parent folder | download | duplicates (2)
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
# frozen_string_literal: true

require "request_store"

module PaperTrail
  # Manages variables that affect the current HTTP request, such as `whodunnit`.
  #
  # Please do not use `PaperTrail::Request` directly, use `PaperTrail.request`.
  # Currently, `Request` is a `Module`, but in the future it is quite possible
  # we may make it a `Class`. If we make such a choice, we will not provide any
  # warning and will not treat it as a breaking change. You've been warned :)
  #
  # @api private
  module Request
    class InvalidOption < RuntimeError
    end

    class << self
      # Sets any data from the controller that you want PaperTrail to store.
      # See also `PaperTrail::Rails::Controller#info_for_paper_trail`.
      #
      #   PaperTrail.request.controller_info = { ip: request_user_ip }
      #   PaperTrail.request.controller_info # => { ip: '127.0.0.1' }
      #
      # @api public
      def controller_info=(value)
        store[:controller_info] = value
      end

      # Returns the data from the controller that you want PaperTrail to store.
      # See also `PaperTrail::Rails::Controller#info_for_paper_trail`.
      #
      #   PaperTrail.request.controller_info = { ip: request_user_ip }
      #   PaperTrail.request.controller_info # => { ip: '127.0.0.1' }
      #
      # @api public
      def controller_info
        store[:controller_info]
      end

      # Switches PaperTrail off for the given model.
      # @api public
      def disable_model(model_class)
        enabled_for_model(model_class, false)
      end

      # Switches PaperTrail on for the given model.
      # @api public
      def enable_model(model_class)
        enabled_for_model(model_class, true)
      end

      # Sets whether PaperTrail is enabled or disabled for the current request.
      # @api public
      def enabled=(value)
        store[:enabled] = value
      end

      # Returns `true` if PaperTrail is enabled for the request, `false` otherwise.
      # See `PaperTrail::Rails::Controller#paper_trail_enabled_for_controller`.
      # @api public
      def enabled?
        !!store[:enabled]
      end

      # Sets whether PaperTrail is enabled or disabled for this model in the
      # current request.
      # @api public
      def enabled_for_model(model, value)
        store[:"enabled_for_#{model}"] = value
      end

      # Returns `true` if PaperTrail is enabled for this model in the current
      # request, `false` otherwise.
      # @api public
      def enabled_for_model?(model)
        model.include?(::PaperTrail::Model::InstanceMethods) &&
          !!store.fetch(:"enabled_for_#{model}", true)
      end

      # @api private
      def merge(options)
        options.to_h.each do |k, v|
          store[k] = v
        end
      end

      # @api private
      def set(options)
        store.clear
        merge(options)
      end

      # Returns a deep copy of the internal hash from our RequestStore. Keys are
      # all symbols. Values are mostly primitives, but whodunnit can be a Proc.
      # We cannot use Marshal.dump here because it doesn't support Proc. It is
      # unclear exactly how `deep_dup` handles a Proc, but it doesn't complain.
      # @api private
      def to_h
        store.deep_dup
      end

      # Temporarily set `options` and execute a block.
      # @api private
      def with(options)
        return unless block_given?
        validate_public_options(options)
        before = to_h
        merge(options)
        yield
      ensure
        set(before)
      end

      # Sets who is responsible for any changes that occur during request. You
      # would normally use this in a migration or on the console, when working
      # with models directly.
      #
      # `value` is usually a string, the name of a person, but you can set
      # anything that responds to `to_s`. You can also set a Proc, which will
      # not be evaluated until `whodunnit` is called later, usually right before
      # inserting a `Version` record.
      #
      # @api public
      def whodunnit=(value)
        store[:whodunnit] = value
      end

      # Returns who is reponsible for any changes that occur during request.
      #
      # @api public
      def whodunnit
        who = store[:whodunnit]
        who.respond_to?(:call) ? who.call : who
      end

      private

      # Returns a Hash, initializing with default values if necessary.
      # @api private
      def store
        RequestStore.store[:paper_trail] ||= {
          enabled: true
        }
      end

      # Provide a helpful error message if someone has a typo in one of their
      # option keys. We don't validate option values here. That's traditionally
      # been handled with casting (`to_s`, `!!`) in the accessor method.
      # @api private
      def validate_public_options(options)
        options.each do |k, _v|
          case k
          when :controller_info,
              /enabled_for_/,
              :enabled,
              :whodunnit
            next
          else
            raise InvalidOption, "Invalid option: #{k}"
          end
        end
      end
    end
  end
end