Skip to content

Instantly share code, notes, and snippets.

@lengarvey
Created November 12, 2012 04:59
Show Gist options
  • Save lengarvey/4057595 to your computer and use it in GitHub Desktop.
Save lengarvey/4057595 to your computer and use it in GitHub Desktop.
A Rack middleware based fix for IE's weird CSS selector limit issue in Rails.
-# put this in your layout
-# what this does it is loads the split_asset application.css file using the correct sprockets name
/[if lt IE 10]
= stylesheet_link_tag stylesheet_path('application').gsub(/\/assets\//, '/split_assets/')
# put this line of code into application.rb
config.middleware.use('StyleSplit', '/split_assets')
# Put this file somewhere in your rails loadpath. I dropped it into lib.
#
# based on code outlined at: http://bindle.me/blog/index.php/200/splitting-the-asset-destroying-arcane-ie-bugs-on-the-rails-rack
# and also based on: https://gist.github.com/1131536
#
class StyleSplit
def initialize(app, path)
@app = app
@path = path
end
def call(env)
if env['PATH_INFO'] =~ Regexp.new('^' + @path + '/([^/]*).css')
filename = File.join(::Rails.public_path, ::Rails.application.config.assets.prefix, $1 + '.css')
css = StyleSplit.split_css(filename)
[200, {
'Content-Type' => 'text/css',
'Content-Size' => css.bytesize.to_s,
'Cache-Control' => 'public, max-age=31536000'
}, [css]]
else
@app.call(env)
end
end
def self.split_css(infile, max_selectors = 4095)
charset_statement, rules = self.read_rules(infile)
return if rules.nil?
rulesets = self.partition_rules(rules, max_selectors)
charset_statement + rulesets[1].join('')
end
def self.read_rules(infile)
raise "file could not be found: #{infile}" unless File.exists? infile
rules = IO.readlines(infile, "}")
return [nil, nil] if rules.first.nil?
charset_statement, rules[0] = rules.first.partition(/^\@charset[^;]+;/)[1,2]
return [charset_statement, rules]
end
# return an array of rulesets, each of which contains no more than max_selectors
def self.partition_rules(rules, max_selectors)
selectors_count = 0
rulesets = []
ruleset = []
rules.each do |rule|
rule_selectors_count = count_selectors_of_rule rule
selectors_count += rule_selectors_count
# Nothing happens until the selectors limit is reached for the first time
if selectors_count > max_selectors
# finish ruleset
rulesets << ruleset
# Prepare next ruleset
ruleset = []
# Reset count with current rule count
selectors_count = rule_selectors_count
end
ruleset << rule
end
rulesets << ruleset unless ruleset.empty? # final ruleset
rulesets
end
def self.count_selectors_of_rule(rule)
rule.partition(/\{/).first.scan(/,/).count.to_i + 1
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment