module MarkupAttributes
The main behaviour for defining markup attributes
To enable this for your models, either extend it directly into one or more models, or extend it into `ApplicationRecord` to include it everywhere
Markup constraints¶ ↑
Markup content typically comes from users of the app, and as with any user-generated content, we probably can't allow them to insert any old HTML into our rendered pages.
The main benefit of this approach is that it allows us to declare constraints on the types of markup we want to deal with in a single place, but without coupling rendering logic into the model itself.
Those constraints are grouped into certain types of elements:
- Emphasis (:emphasis)
-
allow `i` and `em` tags.
- Links (:links)
-
allow `a` tags, but mark them as `nofollow` so that they are not useful for spammers.
- Images (:images)
-
allow `img` tags.
Automatic sanitisation¶ ↑
All the HTML generated is automatically run through Rails' own sanitisation mechanism, which means that things like `<script>alert();</script>` will automatically either be sanitised into a non-running piece of content, or entirely removed, depending on the options given.
@example a simple model declaring textile attributes
class Post < ApplicationRecord extend MarkupAttributes textile_attribute :title, :description, allow: [:emphasis, :images] # ... end
Constants
- MARKUP_TYPES_REGISTRY
- VERSION
Public Instance Methods
Declare one or more attributes as containing Markdown markup. @param (see markup_attribute
)
# File lib/markup_attributes.rb, line 77 def markdown_attribute(*attribute_names, **options) markup_attribute(*attribute_names, **options.merge(markup: :markdown)) end
Declare one or more attributes as containing markup content
Markup types¶ ↑
The following types can be provided in either the :allow
or :deny
options:
- :emphasis
-
i
andem
tags - :links
-
a
tags (will all have +rel=nofollow+ automatically set) - :images
-
img
tags
@param attribute_names [Symbol, Array<Symbol>] One or more attribute names to mark as containing markup @param options [Hash<Symbol=>Symbol>, Hash<Symbol=>Array<Symbol>>] Options to be used when rendering this/these attributes @option options [Symbol] :markup The markup engine to use, either :textile
or :markdown
. If you use either of
+textile_attribute+ or +markdown_attribute+, wrapper methods, this will be set automatically.
@option options [Symbol, Array<Symbol>] :allow Which types of markup to allow (any not included are implicity denied) @option options [Symbol, Array<symbol>] :deny Which types of markup to deny (any not included are implicity allowed)
# File lib/markup_attributes.rb, line 95 def markup_attribute(*attribute_names, **options) raise "must define :markup option" unless options[:markup] options.deep_symbolize_keys! options[:allow] = Array.wrap(options[:allow] || :all).map(&:to_sym) options[:deny] = Array.wrap(options[:deny]).map(&:to_sym) options.freeze attribute_registry_type = get_type_for(options) attribute_names.each do |attribute_name| if respond_to?(:translates?) && translates? && translated_attribute_names.include?(attribute_name) translation_class.attribute attribute_name, attribute_registry_type else attribute attribute_name, attribute_registry_type end end end
Declare one or more attributes as containing Textile markup. @param (see markup_attribute
) @example An attribute which only supports Textile emphasis and links
textile_attribute :title, allow: [:emphasis, :links]
@example An attribute which supports everything except images
textile_attribute :title, deny: [:images]
# File lib/markup_attributes.rb, line 71 def textile_attribute(*attribute_names, **options) markup_attribute(*attribute_names, **options.merge(markup: :textile)) end
Private Instance Methods
# File lib/markup_attributes.rb, line 117 def get_type_for(options) allow_keys = options[:allow].map { |x| "+#{x}" } deny_keys = options[:deny].map { |x| "-#{x}" } type_key = [options[:markup],(allow_keys + deny_keys).sort.join].join('=>').to_sym MARKUP_TYPES_REGISTRY.fetch(type_key) do klass = Class.new(MarkupType) klass.markup_options = options ActiveRecord::Type.register(type_key, klass) MARKUP_TYPES_REGISTRY[type_key] = klass klass end type_key end