module Alchemy

Provides authentication accessors.

Alchemy has some defaults for user model name and login logout path names:

Alchemy.user_class_name defaults to +‘User’+ Alchemy.user_class_primary_key defaults to :id +Alchemy.current_user_method defaults to +‘current_user’+ +Alchemy.signup_path defaults to +‘/signup’+ +Alchemy.login_path defaults to +‘/login’+ +Alchemy.logout_path defaults to +‘/logout’+ +Alchemy.logout_method defaults to +‘delete’+ +Alchemy.unauthorized_path defaults to +‘/’+

Anyway, you can tell Alchemy about your authentication model configuration:

1. Your user class name - @see: Alchemy.user_class
2. Your users table primary key - @see: Alchemy.user_class_primary_key
3. A method on your ApplicationController to get current user -
   @see: Alchemy.current_user_method
4. The path to the signup form - @see: Alchemy.signup_path
5. The path to the login form - @see: Alchemy.login_path
6. The path to the logout method - @see: Alchemy.logout_path
7. The http verb for the logout method - @see: Alchemy.logout_method
8. The path to the page showing the user she's unauthorized - @see: Alchemy.unauthorized_path

Example

# config/initializers/alchemy.rb
Alchemy.user_class_name = 'Admin'
Alchemy.user_class_primary_key = :user_id
Alchemy.current_user_method = 'current_admin'
Alchemy.signup_path = '/auth/signup'
Alchemy.login_path = '/auth/login'
Alchemy.logout_path = '/auth/logout'
Alchemy.logout_method = 'get'
Alchemy.unauthorized_path = '/home'

If you don’t have your own user model or don’t want to provide one, add the ‘alchemy-devise` gem into your App’s Gemfile.

Adding your own CanCan abilities

If your app or your engine has own CanCan abilities you must register them:

Alchemy.register_ability MyCustom::Ability

Custom error classes.

Provides admin interface routing configuration accessors.

Alchemy has some defaults for admin path and admin constraints:

+Alchemy.admin_path defaults to +‘admin’+ +Alchemy.admin_constraints defaults to +{}+

Anyway, you can tell Alchemy about your routing configuration:

1. The path to the admin panel - @see: Alchemy.admin_path
2. The constraints for the admin panel (like subdomain) - @see: Alchemy.admin_constraints

A word of caution: you need to know what you are doing if you set admin_path to ”. This can cause routing name clashes, e.g. a page named ‘dashboard’ will clash with the Alchemy dashboard.

Example

If you do not wish to use the default admin interface routing (‘example.com/admin’) and prefer e.g. ‘hidden.example.com/backend’, those are the settings you need:

# config/initializers/alchemy.rb
Alchemy.admin_path = 'backend'
Alchemy.admin_constraints = {subdomain: 'hidden'}

Constants

Deprecation
LOOKUP_CONTEXT
VERSION
YAML_PERMITTED_CLASSES

Attributes

importmap[RW]

Public Class Methods

admin_importmaps() click to toggle source

Additional importmaps to be included in the Alchemy admin UI

Be sure to also pin modules with Alchemy.importmap.

Example

# config/alchemy/importmap.rb
Alchemy.importmap.pin "alchemy_solidus", to: "alchemy_solidus.js", preload: true
Alchemy.importmap.pin_all_from Alchemy::Solidus::Engine.root.join("app/javascript/alchemy_solidus"),
  under: "alchemy_solidus",
  preload: true

# lib/alchemy/solidus/engine.rb
initializer "alchemy_solidus.assets", before: "alchemy.importmap" do |app|
  Alchemy.admin_importmaps.add({
    importmap_path: root.join("config/importmap.rb"),
    source_paths: [
      root.join("app/javascript")
    ],
    name: "alchemy_solidus"
  })
  app.config.assets.precompile << "alchemy_solidus/manifest.js"
end

@return [Set<Hash>]

# File lib/alchemy.rb, line 90
def self.admin_importmaps
  @_admin_importmaps ||= Set.new([{
    importmap_path: Engine.root.join("config/importmap.rb"),
    source_paths: [
      Engine.root.join("app/javascript"),
      Engine.root.join("vendor/javascript")
    ],
    name: "alchemy_admin"
  }])
end
admin_js_imports() click to toggle source

Additional JS modules to be imported in the Alchemy admin UI

Be sure to also pin the modules with Alchemy.importmap.

Example

Alchemy.importmap.pin "flatpickr/de",
  to: "https://ga.jspm.io/npm:flatpickr@4.6.13/dist/l10n/de.js"

Alchemy.admin_js_imports << "flatpickr/de"
# File lib/alchemy.rb, line 57
def self.admin_js_imports
  @_admin_js_imports ||= Set.new
end
admin_js_imports=(sources) click to toggle source
# File lib/alchemy.rb, line 61
def self.admin_js_imports=(sources)
  @_admin_js_imports = Set[sources]
end
gem_version() click to toggle source
# File lib/alchemy/version.rb, line 10
def self.gem_version
  Gem::Version.new(VERSION)
end
preview_sources() click to toggle source

Define page preview sources

A preview source is a Ruby class returning an URL that is used as source for the preview frame in the admin UI.

Example

# lib/acme/preview_source.rb
class Acme::PreviewSource < Alchemy::Admin::PreviewUrl
  def url_for(page)
    if page.site.name == "Next"
      "https://user:#{ENV['PREVIEW_HTTP_PASS']}@next.acme.com"
    else
      "https://www.acme.com"
    end
  end
end

# config/initializers/alchemy.rb
require "acme/preview_source"
Alchemy.preview_sources << Acme::PreviewSource

# config/locales/de.yml
de:
  activemodel:
    models:
      acme/preview_source: Acme Vorschau
# File lib/alchemy.rb, line 38
def self.preview_sources
  @_preview_sources ||= Set.new << Alchemy::Admin::PreviewUrl
end
preview_sources=(sources) click to toggle source
# File lib/alchemy.rb, line 42
def self.preview_sources=(sources)
  @_preview_sources = Array(sources)
end
publish_targets() click to toggle source

Define page publish targets

A publish target is a ActiveJob that gets performed whenever a user clicks the publish page button.

Use this to trigger deployment hooks of external services in an asychronous way.

Example

# app/jobs/publish_job.rb
class PublishJob < ApplicationJob
  def perform(page)
    RestClient.post(ENV['BUILD_HOOK_URL'])
  end
end

# config/initializers/alchemy.rb
Alchemy.publish_targets << PublishJob
# File lib/alchemy.rb, line 121
def self.publish_targets
  @_publish_targets ||= Set.new
end
register_ability(klass) click to toggle source

Register a CanCan Ability class

# File lib/alchemy/auth_accessors.rb, line 124
def self.register_ability(klass)
  @abilities ||= []
  @abilities << klass
end
registered_abilities() click to toggle source

All CanCan Ability classes registered to Alchemy

# File lib/alchemy/auth_accessors.rb, line 131
def self.registered_abilities
  @abilities ||= []
end
t(msg, **kwargs) click to toggle source

Alchemy shortcut translation method

Instead of having to call:

Alchemy::I18n.translate(:hello)

You can use this shortcut method:

Alchemy.t(:hello)
# File lib/alchemy/i18n.rb, line 15
def t(msg, **kwargs)
  Alchemy::I18n.translate(msg, **kwargs)
end
user_class() click to toggle source
# File lib/alchemy/auth_accessors.rb, line 94
  def self.user_class
    @@user_class ||= begin
      @@user_class_name.constantize
    rescue NameError => e
      if /#{Regexp.escape(@@user_class_name)}/.match?(e.message)
        Rails.logger.warn <<~MSG
          #{e.message}
          #{e.backtrace.join("\n")}

          AlchemyCMS cannot find any user class!

          Please add a user class and tell Alchemy about it:

              # config/initializers/alchemy.rb
              Alchemy.user_class_name = 'MyUser'

          Or add the `alchemy-devise` gem to your Gemfile:

              bundle add alchemy-devise

        MSG
        nil
      else
        raise e
      end
    end
  end
user_class_name() click to toggle source
Prefix with

when getting to avoid constant name conflicts

# File lib/alchemy/auth_accessors.rb, line 82
def self.user_class_name
  if !@@user_class_name.is_a?(String)
    raise TypeError, "Alchemy.user_class_name must be a String, not a Class."
  end

  "::#{@@user_class_name}"
end
user_class_name=(user_class_name) click to toggle source
# File lib/alchemy/auth_accessors.rb, line 90
def self.user_class_name=(user_class_name)
  @@user_class_name = user_class_name
end
version() click to toggle source
# File lib/alchemy/version.rb, line 6
def self.version
  VERSION
end