Skip to content

Instantly share code, notes, and snippets.

@brand-it
Last active August 24, 2018 20:34
Show Gist options
  • Save brand-it/854ad5cbfcc03b3c3429df4bd67401b9 to your computer and use it in GitHub Desktop.
Save brand-it/854ad5cbfcc03b3c3429df4bd67401b9 to your computer and use it in GitHub Desktop.
This is a converter for Paperclip to from old url Patters to a new one. This can be helpful if you have to change maybe the bucket name or just change the path in which they are stored.
require 'paperclip_pattern_processor'
processor = PaperclipPatternProcessor.new
Page.find_each do |page|
urls = page.body.scan(/"(https?:\/\/[\S]+s3.amazonaws.com[\S]+?)"/).flatten.uniq
urls.each do |url|
page.body.gsub!(url, processor.upgrade_url(url))
end
page.save!
end
# frozen_string_literal: true
# This is a converter for Paperclip to from old url Patters to a new one
# There is only one public method and that is #upgrade_url
#
#
# ===== Usage
# processor = PaperclipPatternProcessor.new
#
# processor.upgrade_url('https://tinderbox-dev.s3.amazonaws.com/assets/medias/26_web.jpg?1535044187')
# https://tinderbox-dev.s3.amazonaws.com/contents/media/26/documents/web/300ccdce-5bae-4369-bef3-60a6ddf11f03/1hQv4Ff.jpg?1535044187
#
#
class PaperclipPatternProcessor
attr_accessor :pattern_details
PatternDetail = Struct.new(
:model, :pattern, :legacy_s3_path, :interpolator_keys, :attachment_name
) do
def uniq?(pattern_details)
pattern_details.find do |pattern_detail|
pattern_detail.model == model && pattern_detail.attachment_name == attachment_name
end.nil?
end
end
URLData = Struct.new(:pattern_detail, :path, :match_data) do
def model_record
@model_record ||= if get(:class)
else
pattern_detail.model
end.find_by(id: get(:id))
end
def get(key)
return unless match_data.names.include?(key.to_s)
match_data[key].presence
end
def model
return if get(:class).nil?
get(:class).tr(' ', '_').classify.constantize
rescue NameError => exception
Rails.logger.error("PaperclipPatternProcessor - NameError: #{exception.message}")
nil
end
def model_invalid?
return true if model.present? && model != pattern_detail.model
return true if model_record.nil?
false
end
end
def initialize
Rails.application.eager_load!
self.pattern_details = []
setup
end
# Upgrade URL will try to find a model in question and replace the new one
# upgrade_url('/somethings/123124/nefity.jpg')
#
# All paths require a ID in them. That is the only way to find the record needed to upgrade the URL
# if you don't have a ID in your path then sorry to say your out of luck.
#
def upgrade_url(url)
extract_data(url).each do |url_data|
begin
next if url_data.model_invalid?
attachment = url_data.model_record.send(url_data.pattern_detail.attachment_name)
return attachment.url(url_data.get(:style))
rescue NoMethodError => exception
# This should not happen as the model_invalid? check should ensure everything responds correctly
Rails.logger.error(
"PaperclipPatternProcessor - NoMethodError: #{exception.message} #{url_data.inspect}"
)
end
end
url # if we can't update just return original URL
end
private
def extract_data(path)
find_all_matching_pattern(path).map do |pattern_detail|
URLData.new(pattern_detail, path, pattern_detail.pattern.match(path))
end
end
def find_all_matching_pattern(path)
pattern_details.reject do |pattern_detail|
pattern_detail.pattern.match(path).nil?
end
end
def any_digit
"(?<#{__callee__}>\\d+)"
end
alias id any_digit
alias position any_digit
alias account_id any_digit
alias media_id any_digit
def document_folder
"(?<#{__callee__}>proposal/documents|proposal_documents)"
end
def any_non_whitespace_character
"(?<#{__callee__}>\\S+)"
end
alias extension any_non_whitespace_character
alias style any_non_whitespace_character
alias class any_non_whitespace_character
def build_pattern(path, keys)
pattern = path.dup
pattern.gsub!('.', '\.')
keys.each do |key|
value = send(key)
pattern.gsub!(":#{key}", value)
end
/#{pattern}/
end
def get_keys_for_model(model, attachment_name, legacy_s3_path)
model.new.send(attachment_name).interpolator.all.select do |x|
legacy_s3_path.include?(":#{x}")
end
end
def build_legacy_hash(model, attachment_name, legacy_s3_path)
keys = get_keys_for_model(model, attachment_name, legacy_s3_path)
interpolator_keys.merge(keys)
{
legacy_s3_path: legacy_s3_path,
interpolator_keys: keys,
pattern: build_pattern(legacy_s3_path, keys)
}
end
def real_model(model)
model = model.superclass while model.superclass != ActiveRecord::Base
model
end
def setup
ActiveRecord::Base.descendants.each do |model|
next unless model.method_defined?(:attached_file_keys)
model.attachment_definitions.each do |attachment_name, options|
next unless model.new.send(attachment_name).send(:path_option) == ':s3_path'
keys = get_keys_for_model(model, attachment_name, options[:legacy_s3_path])
pattern_detail = PatternDetail.new(
real_model(model),
build_pattern(options[:legacy_s3_path], keys),
options[:legacy_s3_path],
keys,
attachment_name
)
pattern_details.push(pattern_detail) if pattern_detail.uniq?(pattern_details)
end
end
end
end
@brand-it
Copy link
Author

This code requires rails. has been tested using Rails 4.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment