inline_svg-1.10.0/ 0000755 0000041 0000041 00000000000 15145733677 014000 5 ustar www-data www-data inline_svg-1.10.0/.gitignore 0000644 0000041 0000041 00000000242 15145733677 015766 0 ustar www-data www-data *.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
.ruby-*
inline_svg-1.10.0/inline_svg.gemspec 0000644 0000041 0000041 00000002275 15145733677 017510 0 ustar www-data www-data # coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'inline_svg/version'
Gem::Specification.new do |spec|
spec.name = "inline_svg"
spec.version = InlineSvg::VERSION
spec.authors = ["James Martin"]
spec.email = ["inline_svg@jmrtn.com"]
spec.summary = %q{Embeds an SVG document, inline.}
spec.description = %q{Get an SVG into your view and then style it with CSS.}
spec.homepage = "https://github.com/jamesmartin/inline_svg"
spec.license = "MIT"
spec.files = `git ls-files`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec", "~> 3.2"
spec.add_development_dependency "rspec_junit_formatter", "0.2.2"
spec.add_development_dependency "pry"
spec.add_development_dependency "rubocop"
spec.add_runtime_dependency "activesupport", ">= 3.0"
spec.add_runtime_dependency "nokogiri", ">= 1.6"
end
inline_svg-1.10.0/.github/ 0000755 0000041 0000041 00000000000 15145733677 015340 5 ustar www-data www-data inline_svg-1.10.0/.github/workflows/ 0000755 0000041 0000041 00000000000 15145733677 017375 5 ustar www-data www-data inline_svg-1.10.0/.github/workflows/ruby.yml 0000644 0000041 0000041 00000000543 15145733677 021103 0 ustar www-data www-data name: Ruby
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Ruby 2.7
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.7
- name: Build and test with Rake
run: |
gem install bundler
bundle install --jobs 4 --retry 3
bundle exec rake
inline_svg-1.10.0/.github/workflows/rails_6_webpacker_integration_tests.yaml 0000644 0000041 0000041 00000003377 15145733677 027502 0 ustar www-data www-data name: Rails 6 Webpacker Integration Tests (unreliable)
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
test-branch: [rails6-webpacker]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Checkout test app
uses: actions/checkout@v2
with:
repository: jamesmartin/inline_svg_test_app
ref: ${{ matrix.test-branch }}
path: test_app
- name: Set up Ruby 3.1
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.1
- name: Build local gem
run: |
gem install bundler
bundle install --jobs 4 --retry 3
bundle exec rake build
- name: Use the local gem in the test App
id: uselocalgem
uses: jacobtomlinson/gha-find-replace@0.1.1
with:
find: "gem 'inline_svg'"
replace: "gem 'inline_svg', path: '${{github.workspace}}'"
- name: Check local gem in use
run: |
test "${{ steps.uselocalgem.outputs.modifiedFiles }}" != "0"
grep "inline_svg" $GITHUB_WORKSPACE/test_app/Gemfile
- name: Bundle
run: |
cd $GITHUB_WORKSPACE/test_app
bundle install --jobs 4 --retry 3
- name: Set up Node.js 16.x
uses: actions/setup-node@v2
with:
node-version: 16
if: matrix.test-branch == 'rails6-webpacker'
- name: Set up Python 2.7
uses: actions/setup-python@v4
with:
python-version: '2.7'
- name: Generate Webpacker config
run: |
cd $GITHUB_WORKSPACE/test_app
CXXFLAGS="--std=c++17" yarn install --check-files
bundle exec rake webpacker:compile
if: matrix.test-branch == 'rails6-webpacker'
- name: Test
run: |
cd $GITHUB_WORKSPACE/test_app
bundle exec rake test
inline_svg-1.10.0/.github/workflows/integration_test.yml 0000644 0000041 0000041 00000002410 15145733677 023477 0 ustar www-data www-data name: Integration Tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
test-branch: [rails5, rails6, rails7]
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Checkout test app
uses: actions/checkout@v2
with:
repository: jamesmartin/inline_svg_test_app
ref: ${{ matrix.test-branch }}
path: test_app
- name: Set up Ruby 2.7
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.7
- name: Build local gem
run: |
gem install bundler
bundle install --jobs 4 --retry 3
bundle exec rake build
- name: Use the local gem in the test App
id: uselocalgem
uses: jacobtomlinson/gha-find-replace@0.1.1
with:
find: "gem 'inline_svg'"
replace: "gem 'inline_svg', path: '${{github.workspace}}'"
- name: Check local gem in use
run: |
test "${{ steps.uselocalgem.outputs.modifiedFiles }}" != "0"
grep "inline_svg" $GITHUB_WORKSPACE/test_app/Gemfile
- name: Bundle
run: |
cd $GITHUB_WORKSPACE/test_app
bundle install --jobs 4 --retry 3
- name: Test
run: |
cd $GITHUB_WORKSPACE/test_app
bundle exec rake test
inline_svg-1.10.0/lib/ 0000755 0000041 0000041 00000000000 15145733677 014546 5 ustar www-data www-data inline_svg-1.10.0/lib/inline_svg/ 0000755 0000041 0000041 00000000000 15145733677 016703 5 ustar www-data www-data inline_svg-1.10.0/lib/inline_svg/asset_file.rb 0000644 0000041 0000041 00000000522 15145733677 021345 0 ustar www-data www-data module InlineSvg
class AssetFile
class FileNotFound < IOError; end
UNREADABLE_PATH = ''
def self.named(filename)
asset_path = FindsAssetPaths.by_filename(filename)
File.read(asset_path || UNREADABLE_PATH)
rescue Errno::ENOENT
raise FileNotFound.new("Asset not found: #{asset_path}")
end
end
end
inline_svg-1.10.0/lib/inline_svg/cached_asset_file.rb 0000644 0000041 0000041 00000005276 15145733677 022647 0 ustar www-data www-data # frozen_string_literal: true
module InlineSvg
class CachedAssetFile
attr_reader :assets, :filters, :paths
# For each of the given paths, recursively reads each asset and stores its
# contents alongside the full path to the asset.
#
# paths - One or more String representing directories on disk to search
# for asset files. Note: paths are searched recursively.
# filters - One or more Strings/Regexps to match assets against. Only
# assets matching all filters will be cached and available to load.
# Note: Specifying no filters will cache every file found in
# paths.
#
def initialize(paths: [], filters: [])
@paths = Array(paths).compact.map { |p| Pathname.new(p) }
@filters = Array(filters).map { |f| Regexp.new(f) }
@assets = @paths.reduce({}) { |assets, p| assets.merge(read_assets(assets, p)) }
@sorted_asset_keys = assets.keys.sort { |a, b| a.size <=> b.size }
end
# Public: Finds the named asset and returns the contents as a string.
#
# asset_name - A string representing the name of the asset to load
#
# Returns: A String or raises InlineSvg::AssetFile::FileNotFound error
def named(asset_name)
assets[key_for_asset(asset_name)] or
raise InlineSvg::AssetFile::FileNotFound.new("Asset not found: #{asset_name}")
end
private
# Internal: Finds the key for a given asset name (using a Regex). In the
# event of an ambiguous asset_name matching multiple assets, this method
# ranks the matches by their full file path, choosing the shortest (most
# exact) match over all others.
#
# Returns a String representing the key for the named asset or nil if there
# is no match.
def key_for_asset(asset_name)
@sorted_asset_keys.find { |k| k.include?(asset_name) }
end
# Internal: Recursively descends through current_paths reading each file it
# finds and adding them to the accumulator if the fullpath of the file
# matches all configured filters.
#
# acc - Hash representing the accumulated assets keyed by full path
# paths - Pathname representing the current node in the directory
# structure to consider
#
# Returns a Hash containing the contents of each asset, keyed by fullpath
# to the asset.
def read_assets(acc, paths)
paths.each_child do |child|
if child.directory?
read_assets(acc, child)
elsif child.readable_real?
acc[child.to_s] = File.read(child) if matches_all_filters?(child)
end
end
acc
end
def matches_all_filters?(path)
filters.all? { |f| f.match(path.to_s) }
end
end
end
inline_svg-1.10.0/lib/inline_svg/webpack_asset_finder.rb 0000644 0000041 0000041 00000003074 15145733677 023376 0 ustar www-data www-data module InlineSvg
class WebpackAssetFinder
def self.find_asset(filename)
new(filename)
end
def initialize(filename)
@filename = filename
manifest_lookup = asset_helper.manifest.lookup(@filename)
@asset_path = manifest_lookup.present? ? URI(manifest_lookup).path : ""
end
def pathname
return if @asset_path.blank?
if asset_helper.dev_server.running?
dev_server_asset(@asset_path)
elsif asset_helper.config.public_path.present?
File.join(asset_helper.config.public_path, @asset_path)
end
end
private
def asset_helper
@asset_helper ||=
if defined?(::Shakapacker)
::Shakapacker
else
::Webpacker
end
end
def dev_server_asset(file_path)
asset = fetch_from_dev_server(file_path)
begin
Tempfile.new(file_path).tap do |file|
file.binmode
file.write(asset)
file.rewind
end
rescue StandardError => e
Rails.logger.error "[inline_svg] Error creating tempfile for #{@filename}: #{e}"
raise
end
end
def fetch_from_dev_server(file_path)
http = Net::HTTP.new(asset_helper.dev_server.host, asset_helper.dev_server.port)
http.use_ssl = asset_helper.dev_server.protocol == "https"
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.request(Net::HTTP::Get.new(file_path)).body
rescue StandardError => e
Rails.logger.error "[inline_svg] Error fetching #{@filename} from webpack-dev-server: #{e}"
raise
end
end
end
inline_svg-1.10.0/lib/inline_svg/static_asset_finder.rb 0000644 0000041 0000041 00000001515 15145733677 023247 0 ustar www-data www-data require "pathname"
# Naive fallback asset finder for when sprockets >= 3.0 &&
# config.assets.precompile = false
# Thanks to @ryanswood for the original code:
# https://github.com/jamesmartin/inline_svg/commit/661bbb3bef7d1b4bd6ccd63f5f018305797b9509
module InlineSvg
class StaticAssetFinder
def self.find_asset(filename)
new(filename)
end
def initialize(filename)
@filename = filename
end
def pathname
if ::Rails.application.config.assets.compile
asset = ::Rails.application.assets[@filename]
Pathname.new(asset.filename) if asset.present?
else
manifest = ::Rails.application.assets_manifest
asset_path = manifest.assets[@filename]
unless asset_path.nil?
::Rails.root.join(manifest.directory, asset_path)
end
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/id_generator.rb 0000644 0000041 0000041 00000000567 15145733677 021702 0 ustar www-data www-data require 'digest'
module InlineSvg
class IdGenerator
class Randomness
require "securerandom"
def self.call
SecureRandom.hex(10)
end
end
def self.generate(base, salt, randomness: Randomness)
bytes = Digest::SHA1.digest("#{base}-#{salt}-#{randomness.call}")
'a' + Digest.hexencode(bytes).to_i(16).to_s(36)
end
end
end
inline_svg-1.10.0/lib/inline_svg/action_view/ 0000755 0000041 0000041 00000000000 15145733677 021212 5 ustar www-data www-data inline_svg-1.10.0/lib/inline_svg/action_view/helpers.rb 0000644 0000041 0000041 00000005361 15145733677 023206 0 ustar www-data www-data require 'action_view/helpers' if defined?(Rails)
require 'action_view/context' if defined?(Rails)
module InlineSvg
module ActionView
module Helpers
def inline_svg_tag(filename, transform_params={})
with_asset_finder(InlineSvg.configuration.asset_finder) do
render_inline_svg(filename, transform_params)
end
end
def inline_svg_pack_tag(filename, transform_params={})
with_asset_finder(InlineSvg::WebpackAssetFinder) do
render_inline_svg(filename, transform_params)
end
end
def inline_svg(filename, transform_params={})
render_inline_svg(filename, transform_params)
end
private
def backwards_compatible_html_escape(filename)
# html_escape_once was introduced in newer versions of Rails.
if ERB::Util.respond_to?(:html_escape_once)
ERB::Util.html_escape_once(filename)
else
ERB::Util.html_escape(filename)
end
end
def render_inline_svg(filename, transform_params={})
begin
svg_file = read_svg(filename)
rescue InlineSvg::AssetFile::FileNotFound => error
raise error if InlineSvg.configuration.raise_on_file_not_found?
return placeholder(filename) unless transform_params[:fallback].present?
if transform_params[:fallback].present?
begin
svg_file = read_svg(transform_params[:fallback])
rescue InlineSvg::AssetFile::FileNotFound
placeholder(filename)
end
end
end
InlineSvg::TransformPipeline.generate_html_from(svg_file, transform_params).html_safe
end
def read_svg(filename)
if InlineSvg::IOResource === filename
InlineSvg::IOResource.read filename
else
configured_asset_file.named filename
end
end
def placeholder(filename)
css_class = InlineSvg.configuration.svg_not_found_css_class
not_found_message = "'#{backwards_compatible_html_escape(filename)}' #{extension_hint(filename)}"
if css_class.nil?
return "".html_safe
else
return "".html_safe
end
end
def configured_asset_file
InlineSvg.configuration.asset_file
end
def with_asset_finder(asset_finder)
Thread.current[:inline_svg_asset_finder] = asset_finder
output = yield
Thread.current[:inline_svg_asset_finder] = nil
output
end
def extension_hint(filename)
filename.ends_with?(".svg") ? "" : "(Try adding .svg to your filename) "
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/ 0000755 0000041 0000041 00000000000 15145733677 022603 5 ustar www-data www-data inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/ 0000755 0000041 0000041 00000000000 15145733677 026034 5 ustar www-data www-data inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/transformation.rb 0000644 0000041 0000041 00000002174 15145733677 031433 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class Transformation
def self.create_with_value(value)
self.new(value)
end
attr_reader :value
def initialize(value)
@value = value
end
def transform(*)
raise "#transform should be implemented by subclasses of Transformation"
end
# Parses a document and yields the contained SVG nodeset to the given block
# if it exists.
#
# Returns a Nokogiri::XML::Document.
def with_svg(doc)
doc = Nokogiri::XML::Document.parse(
doc.to_html(encoding: "UTF-8"), nil, "UTF-8"
)
svg = doc.at_css "svg"
yield svg if svg && block_given?
doc
end
end
class NullTransformation < Transformation
def transform(doc)
doc
end
end
end
module InlineSvg
class CustomTransformation < InlineSvg::TransformPipeline::Transformations::Transformation
# Inherit from this class to keep custom transformation class definitions short
# E.g.
# class MyTransform < InlineSvg::CustomTransformation
# def transform(doc)
# # Your code here...
# end
# end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/preserve_aspect_ratio.rb 0000644 0000041 0000041 00000000342 15145733677 032750 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class PreserveAspectRatio < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["preserveAspectRatio"] = self.value
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/style_attribute.rb 0000644 0000041 0000041 00000000433 15145733677 031604 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class StyleAttribute < Transformation
def transform(doc)
with_svg(doc) do |svg|
styles = svg["style"].to_s.split(";")
styles << value
svg["style"] = styles.join(";")
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/title.rb 0000644 0000041 0000041 00000000517 15145733677 027505 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class Title < Transformation
def transform(doc)
with_svg(doc) do |svg|
node = Nokogiri::XML::Node.new("title", doc)
node.content = value
svg.search("title").each { |node| node.remove }
svg.prepend_child(node)
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/data_attributes.rb 0000644 0000041 0000041 00000000714 15145733677 031542 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class DataAttributes < Transformation
def transform(doc)
with_svg(doc) do |svg|
with_valid_hash_from(self.value).each_pair do |name, data|
svg["data-#{dasherize(name)}"] = data
end
end
end
private
def with_valid_hash_from(hash)
Hash.try_convert(hash) || {}
end
def dasherize(string)
string.to_s.gsub(/_/, "-")
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/description.rb 0000644 0000041 0000041 00000000523 15145733677 030704 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class Description < Transformation
def transform(doc)
with_svg(doc) do |svg|
node = Nokogiri::XML::Node.new("desc", doc)
node.content = value
svg.search("desc").each { |node| node.remove }
svg.prepend_child(node)
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/aria_attributes.rb 0000644 0000041 0000041 00000001561 15145733677 031546 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class AriaAttributes < Transformation
def transform(doc)
with_svg(doc) do |svg|
# Add role
svg["role"] = "img"
# Build aria-labelledby string
aria_elements = []
svg.search("title").each do |element|
aria_elements << element["id"] = element_id_for("title", element)
end
svg.search("desc").each do |element|
aria_elements << element["id"] = element_id_for("desc", element)
end
if aria_elements.any?
svg["aria-labelledby"] = aria_elements.join(" ")
end
end
end
def element_id_for(base, element)
if element["id"].nil?
InlineSvg::IdGenerator.generate(base, element.text)
else
InlineSvg::IdGenerator.generate(element["id"], element.text)
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/id_attribute.rb 0000644 0000041 0000041 00000000311 15145733677 031033 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class IdAttribute < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["id"] = self.value
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/height.rb 0000644 0000041 0000041 00000000310 15145733677 027623 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class Height < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["height"] = self.value
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/size.rb 0000644 0000041 0000041 00000000643 15145733677 027336 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class Size < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["width"] = width_of(self.value)
svg["height"] = height_of(self.value)
end
end
def width_of(value)
value.split(/\*/).map(&:strip)[0]
end
def height_of(value)
value.split(/\*/).map(&:strip)[1] || width_of(value)
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/aria_hidden.rb 0000644 0000041 0000041 00000000321 15145733677 030604 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class AriaHidden < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["aria-hidden"] = self.value
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/aria_hidden_attribute.rb 0000644 0000041 0000041 00000000332 15145733677 032671 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class AriaHiddenAttribute < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["aria-hidden"] = self.value
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/no_comment.rb 0000644 0000041 0000041 00000000440 15145733677 030515 0 ustar www-data www-data module InlineSvg::TransformPipeline
module Transformations
class NoComment < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg.xpath("//comment()").each do |comment|
comment.remove
end
end
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/width.rb 0000644 0000041 0000041 00000000306 15145733677 027477 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class Width < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["width"] = self.value
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/view_box.rb 0000644 0000041 0000041 00000000305 15145733677 030201 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class ViewBox < Transformation
def transform(doc)
with_svg(doc) do |svg|
svg["viewBox"] = value
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations/class_attribute.rb 0000644 0000041 0000041 00000000441 15145733677 031550 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
class ClassAttribute < Transformation
def transform(doc)
with_svg(doc) do |svg|
classes = (svg["class"] || "").split(" ")
classes << value
svg["class"] = classes.join(" ")
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline/transformations.rb 0000644 0000041 0000041 00000006677 15145733677 026401 0 ustar www-data www-data module InlineSvg::TransformPipeline::Transformations
# Transformations are run in priority order, lowest number first:
def self.built_in_transformations
{
id: { transform: IdAttribute, priority: 1 },
desc: { transform: Description, priority: 2 },
title: { transform: Title, priority: 3 },
aria: { transform: AriaAttributes },
aria_hidden: { transform: AriaHiddenAttribute },
class: { transform: ClassAttribute },
style: { transform: StyleAttribute },
data: { transform: DataAttributes },
nocomment: { transform: NoComment },
preserve_aspect_ratio: { transform: PreserveAspectRatio },
size: { transform: Size },
width: { transform: Width },
height: { transform: Height },
view_box: { transform: ViewBox },
}
end
def self.custom_transformations
magnify_priorities(InlineSvg.configuration.custom_transformations)
end
def self.magnify_priorities(transforms)
transforms.inject({}) do |output, (name, definition)|
priority = definition.fetch(:priority, built_in_transformations.size)
output[name] = definition.merge( { priority: magnify(priority) } )
output
end
end
def self.magnify(priority=0)
(priority + 1) * built_in_transformations.size
end
def self.all_transformations
in_priority_order(built_in_transformations.merge(custom_transformations))
end
def self.lookup(transform_params)
return [] unless transform_params.any? || custom_transformations.any?
transform_params_with_defaults = params_with_defaults(transform_params)
all_transformations.map { |name, definition|
value = transform_params_with_defaults[name]
definition.fetch(:transform, no_transform).create_with_value(value) if value
}.compact
end
def self.in_priority_order(transforms)
transforms.sort_by { |_, options| options.fetch(:priority, transforms.size) }
end
def self.params_with_defaults(params)
without_empty_values(all_default_values.merge(params))
end
def self.without_empty_values(params)
params.reject {|key, value| value.nil?}
end
def self.all_default_values
custom_transformations
.values
.select {|opt| opt[:default_value] != nil}
.map {|opt| [opt[:attribute], opt[:default_value]]}
.inject({}) {|options, attrs| options.merge!(attrs[0] => attrs[1])}
end
def self.no_transform
InlineSvg::TransformPipeline::Transformations::NullTransformation
end
end
require 'inline_svg/transform_pipeline/transformations/transformation'
require 'inline_svg/transform_pipeline/transformations/no_comment'
require 'inline_svg/transform_pipeline/transformations/class_attribute'
require 'inline_svg/transform_pipeline/transformations/style_attribute'
require 'inline_svg/transform_pipeline/transformations/title'
require 'inline_svg/transform_pipeline/transformations/description'
require 'inline_svg/transform_pipeline/transformations/size'
require 'inline_svg/transform_pipeline/transformations/height'
require 'inline_svg/transform_pipeline/transformations/width'
require 'inline_svg/transform_pipeline/transformations/view_box'
require 'inline_svg/transform_pipeline/transformations/id_attribute'
require 'inline_svg/transform_pipeline/transformations/data_attributes'
require 'inline_svg/transform_pipeline/transformations/preserve_aspect_ratio'
require 'inline_svg/transform_pipeline/transformations/aria_attributes'
require "inline_svg/transform_pipeline/transformations/aria_hidden_attribute"
inline_svg-1.10.0/lib/inline_svg/finds_asset_paths.rb 0000644 0000041 0000041 00000000521 15145733677 022727 0 ustar www-data www-data module InlineSvg
class FindsAssetPaths
def self.by_filename(filename)
asset = configured_asset_finder.find_asset(filename)
asset.try(:pathname) || asset.try(:filename)
end
def self.configured_asset_finder
Thread.current[:inline_svg_asset_finder] || InlineSvg.configuration.asset_finder
end
end
end
inline_svg-1.10.0/lib/inline_svg/io_resource.rb 0000644 0000041 0000041 00000000556 15145733677 021554 0 ustar www-data www-data module InlineSvg
module IOResource
def self.===(object)
object.is_a?(IO) || object.is_a?(StringIO)
end
def self.default_for(object)
case object
when StringIO then ''
when IO then 1
end
end
def self.read(object)
start = object.pos
str = object.read
object.seek start
str
end
end
end
inline_svg-1.10.0/lib/inline_svg/transform_pipeline.rb 0000644 0000041 0000041 00000000662 15145733677 023134 0 ustar www-data www-data module InlineSvg
module TransformPipeline
def self.generate_html_from(svg_file, transform_params)
document = Nokogiri::XML::Document.parse(svg_file)
Transformations.lookup(transform_params).reduce(document) do |doc, transformer|
transformer.transform(doc)
end.to_html.strip
end
end
end
require 'nokogiri'
require 'inline_svg/id_generator'
require 'inline_svg/transform_pipeline/transformations'
inline_svg-1.10.0/lib/inline_svg/version.rb 0000644 0000041 0000041 00000000052 15145733677 020712 0 ustar www-data www-data module InlineSvg
VERSION = "1.10.0"
end
inline_svg-1.10.0/lib/inline_svg/railtie.rb 0000644 0000041 0000041 00000001365 15145733677 020666 0 ustar www-data www-data require 'rails/railtie'
module InlineSvg
class Railtie < ::Rails::Railtie
initializer "inline_svg.action_view" do |app|
ActiveSupport.on_load :action_view do
require "inline_svg/action_view/helpers"
include InlineSvg::ActionView::Helpers
end
end
config.after_initialize do |app|
InlineSvg.configure do |config|
# Configure the asset_finder:
# Only set this when a user-configured asset finder has not been
# configured already.
if config.asset_finder.nil?
# In default Rails apps, this will be a fully operational
# Sprockets::Environment instance
config.asset_finder = app.instance_variable_get(:@assets)
end
end
end
end
end
inline_svg-1.10.0/lib/inline_svg/propshaft_asset_finder.rb 0000644 0000041 0000041 00000000506 15145733677 023765 0 ustar www-data www-data module InlineSvg
class PropshaftAssetFinder
def self.find_asset(filename)
new(filename)
end
def initialize(filename)
@filename = filename
end
def pathname
asset_path = ::Rails.application.assets.load_path.find(@filename)
asset_path.path unless asset_path.nil?
end
end
end
inline_svg-1.10.0/lib/inline_svg.rb 0000644 0000041 0000041 00000006277 15145733677 017244 0 ustar www-data www-data require "inline_svg/version"
require "inline_svg/action_view/helpers"
require "inline_svg/asset_file"
require "inline_svg/cached_asset_file"
require "inline_svg/finds_asset_paths"
require "inline_svg/propshaft_asset_finder"
require "inline_svg/static_asset_finder"
require "inline_svg/webpack_asset_finder"
require "inline_svg/transform_pipeline"
require "inline_svg/io_resource"
require "inline_svg/railtie" if defined?(Rails)
require 'active_support'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string'
require 'nokogiri'
module InlineSvg
class Configuration
class Invalid < ArgumentError; end
attr_reader :asset_file, :asset_finder, :custom_transformations, :svg_not_found_css_class
def initialize
@custom_transformations = {}
@asset_file = InlineSvg::AssetFile
@svg_not_found_css_class = nil
@raise_on_file_not_found = false
end
def asset_file=(custom_asset_file)
begin
method = custom_asset_file.method(:named)
if method.arity == 1
@asset_file = custom_asset_file
else
raise InlineSvg::Configuration::Invalid.new("asset_file should implement the #named method with arity 1")
end
rescue NameError
raise InlineSvg::Configuration::Invalid.new("asset_file should implement the #named method")
end
end
def asset_finder=(finder)
@asset_finder = if finder.respond_to?(:find_asset)
finder
elsif finder.class.name == "Propshaft::Assembly"
InlineSvg::PropshaftAssetFinder
else
# fallback to a naive static asset finder
# (sprokects >= 3.0 && config.assets.precompile = false
# See: https://github.com/jamesmartin/inline_svg/issues/25
InlineSvg::StaticAssetFinder
end
asset_finder
end
def svg_not_found_css_class=(css_class)
if css_class.present? && css_class.is_a?(String)
@svg_not_found_css_class = css_class
end
end
def add_custom_transformation(options)
if incompatible_transformation?(options.fetch(:transform))
raise InlineSvg::Configuration::Invalid.new("#{options.fetch(:transform)} should implement the .create_with_value and #transform methods")
end
@custom_transformations.merge!(Hash[ *[options.fetch(:attribute, :no_attribute), options] ])
end
def raise_on_file_not_found=(value)
@raise_on_file_not_found = value
end
def raise_on_file_not_found?
!!@raise_on_file_not_found
end
private
def incompatible_transformation?(klass)
!klass.is_a?(Class) || !klass.respond_to?(:create_with_value) || !klass.instance_methods.include?(:transform)
end
end
@configuration = InlineSvg::Configuration.new
class << self
attr_reader :configuration
def configure
if block_given?
yield configuration
else
raise InlineSvg::Configuration::Invalid.new('Please set configuration options with a block')
end
end
def reset_configuration!
@configuration = InlineSvg::Configuration.new
end
end
end
inline_svg-1.10.0/LICENSE.txt 0000644 0000041 0000041 00000002055 15145733677 015625 0 ustar www-data www-data Copyright (c) 2014 James Martin
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
inline_svg-1.10.0/spec/ 0000755 0000041 0000041 00000000000 15145733677 014732 5 ustar www-data www-data inline_svg-1.10.0/spec/propshaft_asset_finder_spec.rb 0000644 0000041 0000041 00000001651 15145733677 023030 0 ustar www-data www-data require_relative '../lib/inline_svg'
describe InlineSvg::PropshaftAssetFinder do
context "when the file is not found" do
it "returns nil" do
stub_const('Rails', double('Rails').as_null_object)
expect(::Rails.application.assets.load_path).to receive(:find).with('some-file').and_return(nil)
expect(InlineSvg::PropshaftAssetFinder.find_asset('some-file').pathname).to be_nil
end
end
context "when the file is found" do
it "returns fully qualified file paths from Propshaft" do
stub_const('Rails', double('Rails').as_null_object)
asset = double('Asset')
expect(asset).to receive(:path).and_return(Pathname.new('/full/path/to/some-file'))
expect(::Rails.application.assets.load_path).to receive(:find).with('some-file').and_return(asset)
expect(InlineSvg::PropshaftAssetFinder.find_asset('some-file').pathname).to eq Pathname('/full/path/to/some-file')
end
end
end
inline_svg-1.10.0/spec/inline_svg_spec.rb 0000644 0000041 0000041 00000010656 15145733677 020436 0 ustar www-data www-data require_relative '../lib/inline_svg'
class MyCustomTransform
def self.create_with_value(value); end
def transform(doc); end
end
class MyInvalidCustomTransformKlass
def transform(doc); end
end
class MyInvalidCustomTransformInstance
def self.create_with_value(value); end
end
class MyCustomAssetFile
def self.named(filename); end
end
describe InlineSvg do
describe "configuration" do
context "when a block is not given" do
it "complains" do
expect do
InlineSvg.configure
end.to raise_error(InlineSvg::Configuration::Invalid)
end
end
context "asset finder" do
it "allows an asset finder to be assigned" do
sprockets = double('SomethingLikeSprockets', find_asset: 'some asset')
InlineSvg.configure do |config|
config.asset_finder = sprockets
end
expect(InlineSvg.configuration.asset_finder).to eq sprockets
end
it "falls back to StaticAssetFinder when the provided asset finder does not implement #find_asset" do
InlineSvg.configure do |config|
config.asset_finder = 'Not a real asset finder'
end
expect(InlineSvg.configuration.asset_finder).to eq InlineSvg::StaticAssetFinder
end
end
context "configuring a custom asset file" do
it "falls back to the built-in asset file implementation by default" do
expect(InlineSvg.configuration.asset_file).to eq(InlineSvg::AssetFile)
end
it "adds a collaborator that meets the interface specification" do
InlineSvg.configure do |config|
config.asset_file = MyCustomAssetFile
end
expect(InlineSvg.configuration.asset_file).to eq MyCustomAssetFile
end
it "rejects a collaborator that does not conform to the interface spec" do
bad_asset_file = double("bad_asset_file")
expect do
InlineSvg.configure do |config|
config.asset_file = bad_asset_file
end
end.to raise_error(InlineSvg::Configuration::Invalid, /asset_file should implement the #named method/)
end
it "rejects a collaborator that implements the correct interface with the wrong arity" do
bad_asset_file = double("bad_asset_file", named: nil)
expect do
InlineSvg.configure do |config|
config.asset_file = bad_asset_file
end
end.to raise_error(InlineSvg::Configuration::Invalid, /asset_file should implement the #named method with arity 1/)
end
end
context "configuring the default svg-not-found class" do
it "sets the class name" do
InlineSvg.configure do |config|
config.svg_not_found_css_class = 'missing-svg'
end
expect(InlineSvg.configuration.svg_not_found_css_class).to eq 'missing-svg'
end
end
context "configuring custom transformation" do
it "allows a custom transformation to be added" do
InlineSvg.configure do |config|
config.add_custom_transformation(attribute: :my_transform, transform: MyCustomTransform)
end
expect(InlineSvg.configuration.custom_transformations).to eq({my_transform: {attribute: :my_transform, transform: MyCustomTransform}})
end
it "rejects transformations that do not implement .create_with_value" do
expect do
InlineSvg.configure do |config|
config.add_custom_transformation(attribute: :irrelevant, transform: MyInvalidCustomTransformKlass)
end
end.to raise_error(InlineSvg::Configuration::Invalid, /#{MyInvalidCustomTransformKlass} should implement the .create_with_value and #transform methods/)
end
it "rejects transformations that does not implement #transform" do
expect do
InlineSvg.configure do |config|
config.add_custom_transformation(attribute: :irrelevant, transform: MyInvalidCustomTransformInstance)
end
end.to raise_error(InlineSvg::Configuration::Invalid, /#{MyInvalidCustomTransformInstance} should implement the .create_with_value and #transform methods/)
end
it "rejects transformations that are not classes" do
expect do
InlineSvg.configure do |config|
config.add_custom_transformation(attribute: :irrelevant, transform: :not_a_class)
end
end.to raise_error(InlineSvg::Configuration::Invalid, /#{:not_a_class} should implement the .create_with_value and #transform methods/)
end
end
end
end
inline_svg-1.10.0/spec/webpack_asset_finder_spec.rb 0000644 0000041 0000041 00000001504 15145733677 022433 0 ustar www-data www-data require_relative '../lib/inline_svg'
describe InlineSvg::WebpackAssetFinder do
context "when the file is not found" do
it "returns nil" do
stub_const('Rails', double('Rails').as_null_object)
stub_const('Webpacker', double('Webpacker').as_null_object)
expect(::Webpacker.manifest).to receive(:lookup).with('some-file').and_return(nil)
expect(described_class.find_asset('some-file').pathname).to be_nil
end
end
context "when Shakapacker is defined" do
it "uses the new spelling" do
stub_const('Rails', double('Rails').as_null_object)
stub_const('Shakapacker', double('Shakapacker').as_null_object)
expect(::Shakapacker.manifest).to receive(:lookup).with('some-file').and_return(nil)
expect(described_class.find_asset('some-file').pathname).to be_nil
end
end
end
inline_svg-1.10.0/spec/cached_asset_file_spec.rb 0000644 0000041 0000041 00000005643 15145733677 021706 0 ustar www-data www-data # frozen_string_literal: true
require 'pathname'
require_relative '../lib/inline_svg'
describe InlineSvg::CachedAssetFile do
let(:fixture_path) { Pathname.new(File.expand_path("../files/static_assets", __FILE__)) }
it "loads assets under configured paths" do
known_document = File.read(fixture_path.join("assets0", "known-document.svg"))
asset_loader = InlineSvg::CachedAssetFile.new(paths: fixture_path.join("assets0"))
expect(asset_loader.named("known-document.svg")).to eq(known_document)
end
it "does not include assets outside of configured paths" do
asset_loader = InlineSvg::CachedAssetFile.new(paths: fixture_path.join("assets0"))
expect(fixture_path.join("assets1", "other-document.svg")).to be_file
expect do
asset_loader.named("other-document.svg")
end.to raise_error InlineSvg::AssetFile::FileNotFound
end
it "differentiates two files with the same name" do
known_document_0 = File.read(fixture_path.join("assets0", "known-document.svg"))
known_document_1 = File.read(fixture_path.join("assets1", "known-document.svg"))
expect(known_document_0).not_to eq(known_document_1)
asset_loader = InlineSvg::CachedAssetFile.new(paths: fixture_path)
expect(known_document_0).to eq(asset_loader.named("assets0/known-document.svg"))
expect(known_document_1).to eq(asset_loader.named("assets1/known-document.svg"))
end
it "chooses the closest exact matching file when similar files exist in the same path" do
known_document = File.read(fixture_path.join("assets0", "known-document.svg"))
known_document_2 = File.read(fixture_path.join("assets0", "known-document-two.svg"))
expect(known_document).not_to eq(known_document_2)
asset_loader = InlineSvg::CachedAssetFile.new(paths: fixture_path.join("assets0"), filters: /\.svg/)
expect(asset_loader.named("known-document")).to eq(known_document)
expect(asset_loader.named("known-document-two")).to eq(known_document_2)
end
it "filters wanted files by simple string matching" do
known_document_0 = File.read(fixture_path.join("assets0", "known-document.svg"))
known_document_1 = File.read(fixture_path.join("assets1", "known-document.svg"))
asset_loader = InlineSvg::CachedAssetFile.new(paths: fixture_path, filters: "assets1")
expect do
asset_loader.named("assets0/known-document.svg")
end.to raise_error InlineSvg::AssetFile::FileNotFound
expect(known_document_1).to eq(asset_loader.named("assets1/known-document.svg"))
end
it "filters wanted files by regex matching" do
known_document_1 = File.read(fixture_path.join("assets1", "known-document.svg"))
asset_loader = InlineSvg::CachedAssetFile.new(paths: fixture_path, filters: ["assets1", /\.svg/])
expect do
asset_loader.named("assets1/some-file.txt")
end.to raise_error InlineSvg::AssetFile::FileNotFound
expect(known_document_1).to eq(asset_loader.named("assets1/known-document.svg"))
end
end
inline_svg-1.10.0/spec/files/ 0000755 0000041 0000041 00000000000 15145733677 016034 5 ustar www-data www-data inline_svg-1.10.0/spec/files/example.svg 0000644 0000041 0000041 00000000172 15145733677 020210 0 ustar www-data www-data
inline_svg-1.10.0/spec/files/static_assets/ 0000755 0000041 0000041 00000000000 15145733677 020705 5 ustar www-data www-data inline_svg-1.10.0/spec/files/static_assets/assets1/ 0000755 0000041 0000041 00000000000 15145733677 022270 5 ustar www-data www-data inline_svg-1.10.0/spec/files/static_assets/assets1/known-document.svg 0000644 0000041 0000041 00000000042 15145733677 025755 0 ustar www-data www-data
inline_svg-1.10.0/spec/files/static_assets/assets1/other-document.svg 0000644 0000041 0000041 00000000244 15145733677 025746 0 ustar www-data www-data
inline_svg-1.10.0/spec/files/static_assets/assets1/some-file.txt 0000644 0000041 0000041 00000000024 15145733677 024705 0 ustar www-data www-data Some file contents.
inline_svg-1.10.0/spec/files/static_assets/assets0/ 0000755 0000041 0000041 00000000000 15145733677 022267 5 ustar www-data www-data inline_svg-1.10.0/spec/files/static_assets/assets0/known-document.svg 0000644 0000041 0000041 00000000037 15145733677 025760 0 ustar www-data www-data
inline_svg-1.10.0/spec/files/static_assets/assets0/known-document-two.svg 0000644 0000041 0000041 00000000045 15145733677 026566 0 ustar www-data www-data
inline_svg-1.10.0/spec/files/static_assets/assets0/some-document.svg 0000644 0000041 0000041 00000001463 15145733677 025573 0 ustar www-data www-data
inline_svg-1.10.0/spec/id_generator_spec.rb 0000644 0000041 0000041 00000000543 15145733677 020735 0 ustar www-data www-data require_relative '../lib/inline_svg/id_generator'
describe InlineSvg::IdGenerator do
it "generates a hexencoded ID based on a salt and a random value" do
randomizer = -> { "some-random-value" }
expect(InlineSvg::IdGenerator.generate("some-base", "some-salt", randomness: randomizer)).
to eq("at2c17mkqnvopy36iccxspura7wnreqf")
end
end
inline_svg-1.10.0/spec/finds_asset_paths_spec.rb 0000644 0000041 0000041 00000006272 15145733677 022001 0 ustar www-data www-data require 'pathname'
require_relative '../lib/inline_svg'
describe InlineSvg::FindsAssetPaths do
context "when sprockets finder returns an object which supports only the pathname method" do
it "returns fully qualified file paths from Sprockets" do
sprockets = double('SprocketsDouble')
expect(sprockets).to receive(:find_asset).with('some-file').
and_return(double(pathname: Pathname('/full/path/to/some-file')))
InlineSvg.configure do |config|
config.asset_finder = sprockets
end
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('/full/path/to/some-file')
end
end
context "when sprockets finder returns an object which supports only the filename method" do
it "returns fully qualified file paths from Sprockets" do
sprockets = double('SprocketsDouble')
expect(sprockets).to receive(:find_asset).with('some-file').
and_return(double(filename: Pathname('/full/path/to/some-file')))
InlineSvg.configure do |config|
config.asset_finder = sprockets
end
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('/full/path/to/some-file')
end
end
context "when asset is not found" do
it "returns nil" do
sprockets = double('SprocketsDouble')
expect(sprockets).to receive(:find_asset).with('some-file').and_return(nil)
InlineSvg.configure do |config|
config.asset_finder = sprockets
end
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to be_nil
end
end
context "when propshaft finder returns an object which supports only the pathname method" do
it "returns fully qualified file paths from Propshaft" do
propshaft = double('PropshaftDouble')
expect(propshaft).to receive(:find_asset).with('some-file').
and_return(double(pathname: Pathname('/full/path/to/some-file')))
InlineSvg.configure do |config|
config.asset_finder = propshaft
end
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('/full/path/to/some-file')
end
end
context "when webpack finder returns an object with a relative asset path" do
it "returns the fully qualified file path" do
webpacker = double('WebpackerDouble')
expect(webpacker).to receive(:find_asset).with('some-file').
and_return(double(filename: Pathname('/full/path/to/some-file')))
InlineSvg.configure do |config|
config.asset_finder = webpacker
end
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('/full/path/to/some-file')
end
end
context "when webpack finder returns an object with an absolute http asset path" do
it "returns the fully qualified file path" do
webpacker = double('WebpackerDouble')
expect(webpacker).to receive(:find_asset).with('some-file').
and_return(double(filename: Pathname('https://my-fancy-domain.test/full/path/to/some-file')))
InlineSvg.configure do |config|
config.asset_finder = webpacker
end
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('https://my-fancy-domain.test/full/path/to/some-file')
end
end
end
inline_svg-1.10.0/spec/helpers/ 0000755 0000041 0000041 00000000000 15145733677 016374 5 ustar www-data www-data inline_svg-1.10.0/spec/helpers/inline_svg_spec.rb 0000644 0000041 0000041 00000025643 15145733677 022102 0 ustar www-data www-data require 'inline_svg'
class WorkingCustomTransform < InlineSvg::CustomTransformation
def transform(doc)
doc = Nokogiri::XML::Document.parse(doc.to_html)
svg = doc.at_css 'svg'
svg['custom'] = value
doc
end
end
describe InlineSvg::ActionView::Helpers do
let(:helper) { ( Class.new { include InlineSvg::ActionView::Helpers } ).new }
shared_examples "inline_svg helper" do |helper_method:|
context "when passed the name of an SVG that does not exist" do
after(:each) do
InlineSvg.reset_configuration!
end
context "and configured to raise" do
it "raises an exception" do
InlineSvg.configure do |config|
config.raise_on_file_not_found = true
end
allow(InlineSvg::AssetFile).to receive(:named).
with('some-missing-file.svg').
and_raise(InlineSvg::AssetFile::FileNotFound.new)
expect {
helper.send(helper_method, 'some-missing-file.svg')
}.to raise_error(InlineSvg::AssetFile::FileNotFound)
end
end
it "returns an empty, html safe, SVG document as a placeholder" do
allow(InlineSvg::AssetFile).to receive(:named).
with('some-missing-file.svg').
and_raise(InlineSvg::AssetFile::FileNotFound.new)
output = helper.send(helper_method, 'some-missing-file.svg')
expect(output).to eq ""
expect(output).to be_html_safe
end
it "escapes malicious input" do
malicious = "-->