Skip to content

Instantly share code, notes, and snippets.

@machty
Last active December 10, 2015 08:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save machty/4409814 to your computer and use it in GitHub Desktop.
Save machty/4409814 to your computer and use it in GitHub Desktop.
Rake Pipeline filter for syncing already-published static content on production with the generated local file via Nokogiri and css selectors
require "rake-pipeline-web-filters"
require "./remote-replace-filter"
# specify output, other configuration
input "app" do
match "slim/whatever.slim" do
tilt { |input| input.sub(/\.slim$/, '.html') }
copy "index.html"
# If you don't want to perform the remote query while using
# `rakep server`, you can append "defined?(::Rake::Pipeline::Server)"
# to this line.
# NOTE: make sure this line comes after the last filter to change
# the output path (e.g. copy "index.html" from above) so that the filter
# can determine the remote location of the already published file.
remote_replace "http://www.yourstaticsite.com", ".content"
end
end
require "rake-pipeline"
require "rake-pipeline-web-filters"
require "pathname"
require 'net/http'
require 'uri'
# A filter that uses Nokogiri to replace input content
# with remote content.
# Use case: you deploy a static website that is maintained
# by someone editing HTML and pushing via FTP, and you need
# to make a change to non-content portion of the page. So that
# you don't overwrite the client-generated content, you can
# use this filter to specify the CSS selectors that will be
# copied from the remote server into your generated output.
class RemoteReplaceFilter < Rake::Pipeline::Filter
class MissingLocalContent < StandardError; end
include Rake::Pipeline::Web::Filters::FilterWithDependencies
# @param [String] host (required) a valid http address that'll
# be used in conjunction with the relative path
# of the input file to resolve the remote file
#
# selectors: CSS selectors
# @param [Proc] block the output name generator block
def initialize(host, *selectors, &block)
super(&block)
@host = Pathname.new host
raise ArgumentError, "at least one CSS selector required" if selectors.empty? || selectors.first.empty?
@selectors = selectors
end
# The body of the filter. Query the remote equivalent of
# each input file and swap out the local file's selected content
# with the remote file's selected conttent.
#
# @param [Array] inputs an Array of FileWrapper objects.
# @param [FileWrapper] output a FileWrapper object
def generate_output(inputs, output)
inputs.each do |input|
remote_url = @host.join(input.path).to_s
remote_doc = Nokogiri::HTML(Net::HTTP.get(URI.parse(remote_url)))
local_doc = Nokogiri::HTML(input.read)
# Replace content for each selector
@selectors.each do |sel|
remote_element = remote_doc.at_css(sel)
remote_content = remote_element ? remote_element.content : "Couldn't find remote content for selector: #{sel}"
local_element = local_doc.at_css(sel)
raise MissingLocalContent, "Couldn't find local element in #{input.path} via selector: #{sel}" unless local_element
local_element.content = remote_content
end
# Write the replaced HTML to output
output.write local_doc
end
end
def external_dependencies
[ "nokogiri" ]
end
end
# Add convenience function so that all you have to put in your
# Asset file is `remote_replace` vs filter(RemoteReplaceFilter)...
Rake::Pipeline::DSL::PipelineDSL.module_eval do
# Add a new {RemoteReplaceFilter} to the pipeline.
# @see RemoteReplaceFilter#initialize
def remote_replace(*args, &block)
filter(RemoteReplaceFilter, *args, &block)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment