Class: PaperTrailHistory::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/paper_trail_history/configuration.rb

Overview

Holds the settings that connect the engine to the host application.

The engine has no authentication of its own. Because ApplicationController does not descend from the host application's ApplicationController, none of the host's before_action filters run. The host application therefore has to grant access explicitly, either by giving a parent controller, by giving an authentication callback, or by allowing unauthenticated access on purpose.

Set the configuration in an initializer. The engine reads #parent_controller once, when Rails loads the controller for the first request. A later change has no effect.

Examples:

config/initializers/paper_trail_history.rb

PaperTrailHistory.configure do |config|
  config.parent_controller      = 'Admin::BaseController'
  config.authenticate_with      = -> { authenticate_user! }
  config.authorize_restore_with = -> { current_user.admin? }
end

Constant Summary collapse

DEFAULT_PARENT_CONTROLLER =

Parent controller that the engine uses when the host application gives none.

'ActionController::Base'
DEFAULT_PAGE_LIMIT =

Number of versions on one page when the host application gives none.

25
DEFAULT_ASSETS =

The interface uses Bootstrap. The engine loads it from a public CDN and protects each file with a subresource integrity value, thus the browser refuses a file that somebody changed.

The values belong to the pinned versions below. If you change a URL, you must also change the integrity value or remove it.

{
  bootstrap_css: {
    href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css',
    integrity: 'sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM'
  },
  bootstrap_icons_css: {
    href: 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css',
    integrity: 'sha384-l4UPAMHGzl7zwogLW4nOwaU2XTk6oiM1jhCRQstZEndoIiA2I5bg6fST3wzBSRBD'
  },
  bootstrap_js: {
    href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js',
    integrity: 'sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz'
  }
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Makes a configuration with the default values.



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/paper_trail_history/configuration.rb', line 124

def initialize
  @parent_controller = DEFAULT_PARENT_CONTROLLER
  @authenticate_with = nil
  @authorize_restore_with = nil
  @allow_unauthenticated_access = false
  @filter_attributes = nil
  @parameter_filter = nil
  @page_limit = DEFAULT_PAGE_LIMIT
  @assets = DEFAULT_ASSETS
  @show_version_counts = false
end

Instance Attribute Details

#allow_unauthenticated_accessBoolean

Lets the engine run without authentication outside development and test.

Set this to true only if a different layer protects the mount point, for example a VPN or a reverse proxy.

Returns:

  • (Boolean)


121
122
123
# File 'lib/paper_trail_history/configuration.rb', line 121

def allow_unauthenticated_access
  @allow_unauthenticated_access
end

#assetsHash{Symbol => Hash}

The files that the interface loads.

Each entry has a :href and an optional :integrity. Give your own URL to serve the files from the host application. This is necessary for a network without internet, and for a Content Security Policy that permits no other origin. Remove the :integrity value for a file that you serve yourself.

Examples:

Serve the files from the host application

config.assets = {
  bootstrap_css:       { href: '/assets/bootstrap.css' },
  bootstrap_icons_css: { href: '/assets/bootstrap-icons.css' },
  bootstrap_js:        { href: '/assets/bootstrap.bundle.js' }
}

Returns:

  • (Hash{Symbol => Hash})


66
67
68
# File 'lib/paper_trail_history/configuration.rb', line 66

def assets
  @assets
end

#authenticate_withProc?

Callback that runs as a before_action on every engine request.

The engine runs the callback with instance_exec in the controller. Thus the callback can use current_user, session, redirect_to, head and the route helpers of the host application. To refuse the request, stop the filter chain in the usual Rails manner, for example with redirect_to or head.

Returns:

  • (Proc, nil)


103
104
105
# File 'lib/paper_trail_history/configuration.rb', line 103

def authenticate_with
  @authenticate_with
end

#authorize_restore_withProc?

Callback that decides if the current user can restore a version.

Different from #authenticate_with, this callback is a predicate: give back true to permit the restore and false to refuse it. The engine runs it with instance_exec in the controller. If it is nil, each request that passes #authenticate_with can restore a version.

Returns:

  • (Proc, nil)


113
114
115
# File 'lib/paper_trail_history/configuration.rb', line 113

def authorize_restore_with
  @authorize_restore_with
end

#page_limitInteger

Number of versions that the engine shows on one page.

A version table of a production application can hold millions of rows. The engine reads one page at a time, thus this value also limits the memory that one request needs.

Returns:

  • (Integer)


89
90
91
# File 'lib/paper_trail_history/configuration.rb', line 89

def page_limit
  @page_limit
end

#parent_controllerString

Returns name of the controller class that the engine controllers descend from.

Returns:

  • (String)

    name of the controller class that the engine controllers descend from



92
93
94
# File 'lib/paper_trail_history/configuration.rb', line 92

def parent_controller
  @parent_controller
end

#show_version_countsBoolean

Shows the number of versions of each model in the model list.

The engine needs one count query for each version table to build this column, and a count reads the whole table. An application with a version table for each model thus pays one full read for each model on its root page. For 120 models with millions of rows this takes a long time, and the column is only information.

The page of a single model always shows its count. Only the list is without counts.

Returns:

  • (Boolean)


80
81
82
# File 'lib/paper_trail_history/configuration.rb', line 80

def show_version_counts
  @show_version_counts
end

Instance Method Details

#access_configured?Boolean

Tells if the host application granted access to the engine.

Returns:

  • (Boolean)

    true if the host application gave a parent controller, gave an authentication callback, or allowed unauthenticated access



183
184
185
# File 'lib/paper_trail_history/configuration.rb', line 183

def access_configured?
  custom_parent_controller? || authenticate_with.present? || allow_unauthenticated_access
end

#enforce_access_control?Boolean

Tells if the engine must refuse a request that no one configured access for.

The engine stays open in development and in test, so that the dummy application and the test suite work without an initializer.

Returns:

  • (Boolean)


193
194
195
# File 'lib/paper_trail_history/configuration.rb', line 193

def enforce_access_control?
  !Rails.env.local?
end

#filter_attributesArray

Attribute names whose values the engine must not show.

The default is the list of the host application, Rails.application.config.filter_parameters. Thus each attribute that Rails keeps out of the log files also stays out of this interface. The list accepts everything that ActiveSupport::ParameterFilter accepts: symbols, strings, regular expressions and procs.

The engine reads the list of the host application one time. Change the list in an initializer.

Examples:

Filter more attributes

config.filter_attributes += [:internal_note, /_secret\z/]

Returns:

  • (Array)


151
152
153
# File 'lib/paper_trail_history/configuration.rb', line 151

def filter_attributes
  @filter_attributes ||= Rails.application.config.filter_parameters
end

#filter_attributes=(value) ⇒ Array

Sets the attribute names whose values the engine must not show.

Parameters:

  • value (Array)

Returns:

  • (Array)


159
160
161
162
# File 'lib/paper_trail_history/configuration.rb', line 159

def filter_attributes=(value)
  @parameter_filter = nil
  @filter_attributes = value
end

#filtered_attribute?(name) ⇒ Boolean

Tells if the engine must hide the value of the given attribute.

The method asks ActiveSupport::ParameterFilter with a probe object. Thus the engine hides exactly the attributes that Rails hides, also for a regular expression or a proc in the list.

Parameters:

  • name (String, Symbol)

    name of the attribute

Returns:

  • (Boolean)


172
173
174
175
176
177
# File 'lib/paper_trail_history/configuration.rb', line 172

def filtered_attribute?(name)
  probe = Object.new
  key = name.to_s

  !parameter_filter.filter(key => probe)[key].equal?(probe)
end

#parent_controller_classClass

Resolves #parent_controller to a class.

Returns:

  • (Class)

    the controller class that the engine controllers descend from

Raises:



202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/paper_trail_history/configuration.rb', line 202

def parent_controller_class
  klass = parent_controller.to_s.constantize
  unless klass.is_a?(Class) && klass <= ActionController::Base
    raise ConfigurationError,
          "PaperTrailHistory parent_controller #{parent_controller.inspect} " \
          'must be an ActionController::Base descendant'
  end

  klass
rescue NameError => e
  raise ConfigurationError,
        "PaperTrailHistory parent_controller #{parent_controller.inspect} does not exist: #{e.message}"
end