- 
      
 - 
        
Save raybrownco/0047ef8d7eaec0bf31bb to your computer and use it in GitHub Desktop.  
| # Middleman - Inline SVG Helper | |
| # ------------------------------------------------------------------------------ | |
| # | |
| # Installation | |
| # ------------ | |
| # 1. Save this file at `[project_root]/helpers/image_helpers.rb` | |
| # 2. Open the project's Gemfile and add this line: `gem "oga"` | |
| # 3. Run `bundle install` from the command line | |
| # | |
| # Note: Restart your local Middleman server (if it's running) before continuing | |
| # | |
| # Usage | |
| # ----- | |
| # Embed SVG files into your template files like so: | |
| # | |
| # ``` | |
| # <%= inline_svg "name/of/file.svg" %> | |
| # ``` | |
| # | |
| # The helper also allows you to pass attributes to add to the SVG tag, like so: | |
| # | |
| # Input: | |
| # ``` | |
| # <%= inline_svg "name/of/file.svg", class: "foo", data: {bar: "baz"} %> | |
| # ``` | |
| # | |
| # Output: | |
| # ``` | |
| # <svg <!-- existing attributes --> class="foo" data-bar="baz"> | |
| # <!-- SVG contents --> | |
| # </svg> | |
| # ``` | |
| # | |
| # Acknowledgements and Contributors | |
| # -------------------------- | |
| # This was initally adapted from the work of James Martin | |
| # and Steven Harley, which you can read more about here: | |
| # https://robots.thoughtbot.com/organized-workflow-for-svg | |
| # | |
| # Further improvements were made based on contributions by: | |
| # | |
| # * Cecile Veneziani (@cveneziani) | |
| # * Allan White (@allanwhite) | |
| # | |
| # Thanks for improving this helper! Have questions or concerns? | |
| # Feel free to fork the Gist or comment on it here: | |
| # https://gist.github.com/bitmanic/0047ef8d7eaec0bf31bb | |
| module ImageHelpers | |
| def inline_svg(relative_image_path, optional_attributes = {}) | |
| image_path = File.join(config[:source], config[:images_dir], relative_image_path) | |
| # If the image was found... | |
| if File.exists?(image_path) | |
| # Open the image | |
| image = File.open(image_path, 'r') { |f| f.read } | |
| # Return the image if no optional attributes were passed in | |
| return image if optional_attributes.empty? | |
| # Otherwise, parse the image | |
| document = Oga.parse_xml(image) | |
| svg = document.css('svg').first | |
| # Then, add the attributes | |
| # NOTE: This allows for hash-based values, but we're only going one level | |
| # deep right now. If you know a great way to dig `N` levels deeper, | |
| # feel free to post about it on the Gist. | |
| optional_attributes.each do |attribute, value| | |
| case value | |
| when Hash | |
| value.each do |subattribute, subvalue| | |
| unless subvalue.class == Hash | |
| svg.set( | |
| "#{attribute} #{subattribute}".parameterize, | |
| subvalue.html_safe | |
| ) | |
| end | |
| end | |
| else | |
| svg.set(attribute.to_sym, value.html_safe) | |
| end | |
| end | |
| # Finally, return the image | |
| document.to_xml | |
| # If the file wasn't found... | |
| else | |
| # Embed an inline SVG image with an error message | |
| %( | |
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 30" | |
| width="400px" height="30px" | |
| > | |
| <text font-size="16" x="8" y="20" fill="#cc0000"> | |
| Error: '#{relative_image_path}' could not be found. | |
| </text> | |
| <rect | |
| x="1" y="1" width="398" height="28" fill="none" | |
| stroke-width="1" stroke="#cc0000" | |
| /> | |
| </svg> | |
| ) | |
| end | |
| end | |
| end | 
Oh never mind. It's me being dumb. Didn't realized you used sprockets to locate it by filename and not url.
modified this as follows:
# lib/image_helpers.rb
module ImageHelpers
  # usage <%= inline_svg("logo"); %> assuming logo.svg is stored at source/images/svg/logo.svg
  def inline_svg(filename, options = {})
    asset = "source/images/svg/#{filename}.svg"
    if File.exists?(asset)
      file = File.open(asset, 'r') { |f| f.read }
      doc = Nokogiri::HTML::DocumentFragment.parse(file)
      svg = doc.at_css("svg")
      if options[:class].present?
        svg["class"] = options[:class]
      end
      doc
    else
      %(
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 30"
          width="400px" height="30px"
        >
          <text font-size="16" x="8" y="20" fill="#cc0000">
            Error: '#{filename}' could not be found.
          </text>
          <rect
            x="1" y="1" width="398" height="28" fill="none"
            stroke-width="1" stroke="#cc0000"
          />
        </svg>
      )
    end
  end
endThen add the following to your config.rb
...
require "lib/image_helpers"
helpers ImageHelpersFor the ones that don't like to add the huge Nokogiri as dependency, I propose an alternative with Oga.
By just replacing Nokogiri implementation:
doc = Nokogiri::HTML::DocumentFragment.parse(file)
svg = doc.at_css("svg")
if options[:class].present?
  svg["class"] = options[:class]
end
docWith Oga implementation:
css_class = options[:class]
return file if css_class.nil?
document        = Oga.parse_xml(file)
svg             = document.css('svg').first
class_attribute = svg.attribute(:class)
if class_attribute
  class_attribute.value = css_class
else
  class_attribute = Oga::XML::Attribute.new(name: :class, value: css_class)
  svg.add_attribute(class_attribute)
end
document.to_xmlBTW I also avoid parsing the XML if no class option is provided. I directly return the content of the file =)
Refactored "a bit" :)
css_class = options[:class]
return file if css_class.nil?
document = Oga.parse_xml(file)
svg      = document.css('svg').first
svg.set(:class, css_class)
document.to_xmlIs there a way to modify this helper so I can use it with HAML-SVGs? (I have to set the xlink:href of an image with the image_path helper.)
Example SVG:
%svg{viewbox: '0 0 200 200', xmlns: 'http//www.w3.org/2000/svg', 'xmlns:xlink' => 'http//www.w3.org/1999/xlink'}
  %image#image{height: '20', width: '20', 'xlink:href' => image_path('image.jpg')}I found this gist and approach really very helpful, and am using it now in our middleman-driven site. I made a few tweaks someone might find useful:
# /helpers/image_helpers.rb 
module ImageHelpers
  # usage <%= inline_svg("logo"); %> assuming logo.svg is stored at source/assets/icons/logo.svg
  def inline_svg(filename, options = {})
    asset = "source/assets/icons/#{filename}.svg"
    if File.exists?(asset)
      file = File.open(asset, 'r') { |f| f.read }
      # we pass svg-targeting css classes through here. The class targets fill, stroke, poly, circle, etc. 
      css_class = options[:class]
      aspect_ratio = options[:preserveAspectRatio]
      # added some aspect-ratio settings; default covers most of our svg use cases. We want all our SVG icon artwork to scale to fit. This could be an argument if needed.
      radio_default = "xMidYMid meet"
      return file if css_class.nil?
      document = Oga.parse_xml(file)
      svg      = document.css('svg').first
      svg.set(:class, css_class)
      svg.set(:preserveAspectRatio, radio_default)
      document.to_xml
    else
      # added a little more helpful info here so it's easier to track down a problem.
      puts "inline_svg '#{asset}' at #{current_page.url} could not be found!"
      %(
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 60" width="400px" height="60px">
          <text font-size="12" x="8" y="20" fill="#cc0000">
            Error: '#{asset}' could not be found.
          </text>
          <rect x="1" y="1" width="398" height="38" fill="none" stroke-width="1" stroke="#cc0000" />
        </svg>
      )
    end
  end
endI've updated this Gist to perform better and support more attributes. This work relies on the improvements made by @cveneziani and @allanwhite. Thanks so much for your contributions!
I've placed my assets in a assets folder. I'm then including the file by
<%= inline_svg("/assets/images/my-image.svg") %>.I get a not found triggered from the helper. Is this setup invalid?
My folder structure