diff --git a/BUGS-zeevex.txt b/BUGS-zeevex.txt new file mode 100644 index 0000000..c9371ac --- /dev/null +++ b/BUGS-zeevex.txt @@ -0,0 +1,32 @@ +In Zeevex forked version. + +**** Stylesheet link tag :cache does not work - 2010-11-17 + + if a construct like this is used: + + <%= stylesheet_link_tag "foo", "bar", :cache => true %> + + Rails will likely throw an error like this: + + Errno::ENOENT: No such file or directory - /zeevex/sites/testengine/releases/20101117175544/public/testing/805270d93/stylesheets/web_app_theme.css 1 minute ago +test/purchases#index + + Backtrace: + + [GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:668:in `read' + +[GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:668:in `join_asset_file_contents' + +[GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:668:in `collect' + +[GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:668:in `join_asset_file_contents' + +[GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:673:in `write_asset_file_contents' + +[GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:673:in `open' + +[GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:673:in `write_asset_file_contents' + +[GEM_ROOT]/gems/actionpack-2.3.10/lib/action_view/helpers/asset_tag_helper.rb:433:in `stylesheet_link_tag' + + diff --git a/Gemfile.lock b/Gemfile.lock index 52dc8f3..c6b7d46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,24 +7,35 @@ PATH GEM remote: http://rubygems.org/ specs: + actionmailer (2.3.9) + actionpack (= 2.3.9) actionpack (2.3.9) activesupport (= 2.3.9) rack (~> 1.1.0) + activerecord (2.3.9) + activesupport (= 2.3.9) + activeresource (2.3.9) + activesupport (= 2.3.9) activesupport (2.3.9) ansi (1.2.2) - facets (2.8.4) - mocha (0.9.8) + mocha (0.9.9) rake rack (1.1.0) + rails (2.3.9) + actionmailer (= 2.3.9) + actionpack (= 2.3.9) + activerecord (= 2.3.9) + activeresource (= 2.3.9) + activesupport (= 2.3.9) + rake (>= 0.8.3) rake (0.8.7) redgreen (1.2.2) right_aws (2.0.0) right_http_connection (>= 1.2.1) right_http_connection (1.2.4) shoulda (2.11.3) - turn (0.7.0) - ansi (>= 1.1.0) - facets (>= 2.8.0) + turn (0.8.1) + ansi (>= 1.2.2) PLATFORMS ruby @@ -34,6 +45,7 @@ DEPENDENCIES activesupport (= 2.3.9) cloudfront_asset_host! mocha + rails (= 2.3.9) redgreen right_aws shoulda diff --git a/VERSION b/VERSION index 9084fa2..43d6e42 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 +z1.1.0.5 diff --git a/cloudfront_asset_host.gemspec b/cloudfront_asset_host.gemspec index 18096c3..ec74e7d 100644 --- a/cloudfront_asset_host.gemspec +++ b/cloudfront_asset_host.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = %q{cloudfront_asset_host} - s.version = "1.1.0" + s.version = "1.1.1.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Menno van der Sman"] @@ -37,7 +37,8 @@ Gem::Specification.new do |s| "test/cloudfront_asset_host_test.rb", "test/css_rewriter_test.rb", "test/test_helper.rb", - "test/uploader_test.rb" + "test/uploader_test.rb", + "test/taghelper_ext_test.rb", ] s.homepage = %q{http://github.com/menno/cloudfront_asset_host} s.rdoc_options = ["--charset=UTF-8"] @@ -57,6 +58,7 @@ Gem::Specification.new do |s| if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, ["= 2.3.9"]) s.add_development_dependency(%q, ["= 2.3.9"]) s.add_development_dependency(%q, ["= 2.3.9"]) s.add_development_dependency(%q, [">= 0"]) @@ -65,6 +67,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["= 2.3.9"]) s.add_dependency(%q, ["= 2.3.9"]) s.add_dependency(%q, ["= 2.3.9"]) s.add_dependency(%q, [">= 0"]) @@ -74,6 +77,7 @@ Gem::Specification.new do |s| end else s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["= 2.3.9"]) s.add_dependency(%q, ["= 2.3.9"]) s.add_dependency(%q, ["= 2.3.9"]) s.add_dependency(%q, [">= 0"]) diff --git a/lib/cloudfront_asset_host.rb b/lib/cloudfront_asset_host.rb index dd2237d..8cff6a5 100644 --- a/lib/cloudfront_asset_host.rb +++ b/lib/cloudfront_asset_host.rb @@ -53,7 +53,7 @@ def configure self.bucket = nil self.cname = nil self.key_prefix = "" - self.s3_config = "#{RAILS_ROOT}/config/s3.yml" + self.s3_config = "#{Rails.root}/config/s3.yml" self.s3_logging = false self.enabled = false @@ -113,10 +113,13 @@ def bucket_host end def enable! - if enabled - ActionController::Base.asset_host = Proc.new { |source, request| CloudfrontAssetHost.asset_host(source, request) } + if enabled && !ActionView::Helpers::AssetTagHelper.private_method_defined?(:rewrite_asset_path_without_cloudfront) ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :rewrite_asset_path, :cloudfront) ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :rails_asset_id, :cloudfront) + ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :compute_asset_host, :cloudfront) + ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :compute_public_path, :cloudfront) + ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :stylesheet_link_tag, :cloudfront) + ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :javascript_include_tag, :cloudfront) end end diff --git a/lib/cloudfront_asset_host/asset_tag_helper_ext.rb b/lib/cloudfront_asset_host/asset_tag_helper_ext.rb index 4262538..4d45cd1 100644 --- a/lib/cloudfront_asset_host/asset_tag_helper_ext.rb +++ b/lib/cloudfront_asset_host/asset_tag_helper_ext.rb @@ -1,15 +1,61 @@ module ActionView - module Helpers - module AssetTagHelper + module Helpers + module AssetTagHelper + + + def javascript_include_tag_with_cloudfront(*sources) + if sources.last.is_a?(::Hash) && (sources.last[:cache] || sources.last[:recursive]) + without_cloudfront do + javascript_include_tag_without_cloudfront(*sources) + end + else + javascript_include_tag_without_cloudfront(*sources) + end + end + + def stylesheet_link_tag_with_cloudfront(*sources) + if sources.last.is_a?(::Hash) && (sources.last[:cache] || sources.last[:recursive]) + without_cloudfront do + stylesheet_link_tag_without_cloudfront(*sources) + end + else + stylesheet_link_tag_without_cloudfront(*sources) + end + end private + def compute_asset_host_with_cloudfront(source) + if !CloudfrontAssetHost.enabled || CloudfrontAssetHost.disable_cdn_for_source?(source) + compute_asset_host_without_cloudfront(source) + else + request = controller.respond_to?(:request) && controller.request + CloudfrontAssetHost.asset_host(source, request) + end + end + + def rewrite_host_and_protocol_with_cloudfront(source, has_request) + if !CloudfrontAssetHost.enabled || CloudfrontAssetHost.disable_cdn_for_source?(source) + rewrite_host_and_protocol_without_cloudfront(source, has_request) + else + host = CloudfrontAssetHost.asset_host(source, has_request) + if has_request && host.present? && !is_uri?(host) + host = "#{controller.request.protocol}#{host}" + end + "#{host}#{source}" + end + end + # Override asset_id so it calculates the key by md5 instead of modified-time - def rails_asset_id_with_cloudfront(source) + def rails_asset_id_with_cloudfront(source, *rails3args) + if !CloudfrontAssetHost.enabled || CloudfrontAssetHost.disable_cdn_for_source?(source) + return rails_asset_id_without_cloudfront(source, *rails3args) + end + if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source]) asset_id else - path = File.join(ASSETS_DIR, source) + path = File.join(Rails.configuration.assets_dir, source) rewrite_path = File.exist?(path) && !CloudfrontAssetHost.disable_cdn_for_source?(source) asset_id = rewrite_path ? CloudfrontAssetHost.key_for_path(path) : '' @@ -24,15 +70,37 @@ def rails_asset_id_with_cloudfront(source) end # Override asset_path so it prepends the asset_id - def rewrite_asset_path_with_cloudfront(source) - asset_id = rails_asset_id(source) - if asset_id.blank? - source + def rewrite_asset_path_with_cloudfront(source, *rails3args) + if !CloudfrontAssetHost.enabled || CloudfrontAssetHost.disable_cdn_for_source?(source) + rewrite_asset_path_without_cloudfront(source, *rails3args) else - "/#{asset_id}#{source}" + asset_id = rails_asset_id_with_cloudfront(source, *rails3args) + if asset_id.blank? + source + else + "/#{asset_id}#{source}" + end end end + + def compute_public_path_with_cloudfront(source, dir, ext = nil, include_host = true) + if source =~ %r{^([-a-z]+:)?//} + return source + else + compute_public_path_without_cloudfront(source, dir, ext, include_host) + end + end + + def without_cloudfront + begin + old = CloudfrontAssetHost.enabled + CloudfrontAssetHost.enabled = false + yield + ensure + CloudfrontAssetHost.enabled = old + end + end - end - end + end + end end diff --git a/lib/cloudfront_asset_host/css_rewriter.rb b/lib/cloudfront_asset_host/css_rewriter.rb index 62dbd67..b17ed57 100644 --- a/lib/cloudfront_asset_host/css_rewriter.rb +++ b/lib/cloudfront_asset_host/css_rewriter.rb @@ -9,7 +9,7 @@ module CssRewriter class << self # matches optional quoted url() - ReplaceRexeg = /url\(["']?([^\)\?"']+)(\?[^"']*)?["']?\)/i + ReplaceRexeg = /url\(["']?([^\)\?"']+)(\?[^"'\)]*)?["']?\)/i # Returns the path to the temporary file that contains the # rewritten stylesheet diff --git a/lib/cloudfront_asset_host/mime_types.yml b/lib/cloudfront_asset_host/mime_types.yml index 60db193..735ee92 100644 --- a/lib/cloudfront_asset_host/mime_types.yml +++ b/lib/cloudfront_asset_host/mime_types.yml @@ -149,3 +149,12 @@ - php - config - txt +"application/vnd.ms-fontobject": + - eot +"font/woff": + - woff +"font/truetype": + - ttf +"font/opentype": + - otf + \ No newline at end of file diff --git a/lib/cloudfront_asset_host/uploader.rb b/lib/cloudfront_asset_host/uploader.rb index b7d237a..9c85939 100644 --- a/lib/cloudfront_asset_host/uploader.rb +++ b/lib/cloudfront_asset_host/uploader.rb @@ -108,11 +108,10 @@ def current_paths def headers_for_path(extension, gzip = false) mime = ext_to_mime[extension] || 'application/octet-stream' headers = { - 'Content-Type' => mime, - 'Cache-Control' => "max-age=#{10.years.to_i}", - 'Expires' => 1.year.from_now.utc.to_s + 'content-type' => mime, + 'cache-control' => "max-age=#{10.years.to_i}", } - headers['Content-Encoding'] = 'gzip' if gzip + headers['content-encoding'] = 'gzip' if gzip headers end @@ -135,7 +134,7 @@ def s3 def config @config ||= begin - config = YAML::load_file(CloudfrontAssetHost.s3_config) + config = YAML::load(ERB.new(IO.read(CloudfrontAssetHost.s3_config)).result) config.has_key?(Rails.env) ? config[Rails.env] : config end end diff --git a/lib/tasks/cloudfront_asset_host.rake b/lib/tasks/cloudfront_asset_host.rake new file mode 100644 index 0000000..a32bebe --- /dev/null +++ b/lib/tasks/cloudfront_asset_host.rake @@ -0,0 +1,14 @@ +namespace :cloudfront do + desc <<-DESC + Upload assets to Cloudfront CDN. + + Set VERBOSE=true for detailed output + FORCE=true to force upload of unchanged assets + DRYRUN=true to simulate an upload' + DESC + task :upload => :environment do + CloudfrontAssetHost::Uploader.upload!(:verbose => ENV["VERBOSE"] == "true", + :dryrun => ENV["DRYRUN"] == "true", + :force_write => ENV["FORCE"] == "true") + end +end diff --git a/test/app/public/javascripts/.gitignore b/test/app/public/javascripts/.gitignore new file mode 100644 index 0000000..24cf3f5 --- /dev/null +++ b/test/app/public/javascripts/.gitignore @@ -0,0 +1 @@ +all.js diff --git a/test/app/public/stylesheets/.gitignore b/test/app/public/stylesheets/.gitignore new file mode 100644 index 0000000..bdf326e --- /dev/null +++ b/test/app/public/stylesheets/.gitignore @@ -0,0 +1 @@ +all.css diff --git a/test/app/public/stylesheets/style.css b/test/app/public/stylesheets/style.css index 0762e7b..0f01096 100644 --- a/test/app/public/stylesheets/style.css +++ b/test/app/public/stylesheets/style.css @@ -1,6 +1,7 @@ -body { background-image: url(../images/image.png); } -body { background-image: url(/images/image.png); } -body { background-image: url('/images/image.png'); } -body { background-image: url("/images/image.png"); } -body { background-image: url("/images/image.png?98732857"); } -body { background-image: url("/images/image.png?fo0=bar"); } \ No newline at end of file +body { background: url(../images/image.png) repeat-x; } +body { background: url(/images/image.png) repeat-x; } +body { background: url('/images/image.png') repeat-x; } +body { background: url("/images/image.png") repeat-x; } +body { background: url("/images/image.png?98732857") repeat-x; } +body { background: url("/images/image.png?fo0=bar") repeat-x; } +body { background: url(/images/image.png?1264616492) repeat-x;border-left:0;border-right:0;bottom:0;display:none;left:0;margin-left:0;margin-right:0;padding-left:0;padding-right:0;padding-top:10px;position:fixed;text-align:center;width:100%;z-index:10000;}#catface.catface-ie{bottom:expression((0 - document.documentElement.offsetHeight+(document.documentElement.clientHeight ? document.documentElement.clientHeight:document.body.clientHeight)+(document.documentElement.scrollTop ? document.documentElement.scrollTop:document.body.scrollTop))+'px');left:0;overflow:hidden;position:absolute;right:0;width:expression((document.documentElement.clientWidth ? document.documentElement.clientWidth:document.body.clientWidth)+'px');} \ No newline at end of file diff --git a/test/cloudfront_asset_host_test.rb b/test/cloudfront_asset_host_test.rb index c7c5c8b..9e973b8 100644 --- a/test/cloudfront_asset_host_test.rb +++ b/test/cloudfront_asset_host_test.rb @@ -1,4 +1,4 @@ -require 'test_helper' +require File.join(File.dirname(__FILE__), 'test_helper') class CloudfrontAssetHostTest < Test::Unit::TestCase @@ -15,6 +15,9 @@ class CloudfrontAssetHostTest < Test::Unit::TestCase should "add methods to asset-tag-helper" do assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rails_asset_id_with_cloudfront') assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rewrite_asset_path_with_cloudfront') + assert ActionView::Helpers::AssetTagHelper.private_method_defined?('compute_asset_host_with_cloudfront') + assert ActionView::Helpers::AssetTagHelper.public_method_defined?('stylesheet_link_tag_with_cloudfront') + assert ActionView::Helpers::AssetTagHelper.public_method_defined?('javascript_include_tag_with_cloudfront') end should "not enable itself by default" do @@ -108,15 +111,17 @@ class CloudfrontAssetHostTest < Test::Unit::TestCase end end - should "set the asset_host" do - assert ActionController::Base.asset_host.is_a?(Proc) + should "not set the asset_host" do + assert !ActionController::Base.asset_host.is_a?(Proc) end should "alias methods in asset-tag-helper" do assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rails_asset_id_without_cloudfront') assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rewrite_asset_path_without_cloudfront') + assert ActionView::Helpers::AssetTagHelper.private_method_defined?('compute_asset_host_without_cloudfront') assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rails_asset_id') assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rewrite_asset_path') + assert ActionView::Helpers::AssetTagHelper.private_method_defined?('compute_asset_host') end end diff --git a/test/css_rewriter_test.rb b/test/css_rewriter_test.rb index 4b87bbe..47d0a11 100644 --- a/test/css_rewriter_test.rb +++ b/test/css_rewriter_test.rb @@ -1,4 +1,4 @@ -require 'test_helper' +require File.join(File.dirname(__FILE__), 'test_helper') class CssRewriterTest < Test::Unit::TestCase @@ -19,10 +19,9 @@ class CssRewriterTest < Test::Unit::TestCase tmp = CloudfrontAssetHost::CssRewriter.rewrite_stylesheet(@stylesheet_path) contents = File.read(tmp.path) contents.split("\n").each do |line| - assert_equal "body { background-image: url(http://assethost.com/d41d8cd98/images/image.png); }", line + assert_match Regexp.new('body \{ background: url\(http://assethost.com/d41d8cd98/images/image.png\) repeat-x;.*\}$'), line end end - end end diff --git a/test/taghelper_ext_test.rb b/test/taghelper_ext_test.rb new file mode 100644 index 0000000..7236118 --- /dev/null +++ b/test/taghelper_ext_test.rb @@ -0,0 +1,110 @@ +require File.join(File.dirname(__FILE__), 'test_helper') + +require 'action_view' + +require 'action_view/test_case' +require "action_controller/test_process" +require 'action_view/helpers/asset_tag_helper' + +# ActionView::Helpers is for recent rails version, ActionView::Base for older ones (in which case ActionView::Helpers::AssetTagHelper is also needed for tests...) +ActionView::Helpers.class_eval { include ActionView::Helpers::AssetTagHelper } # For recent rails version... +ActionView::Base.class_eval { include ActionView::Helpers::AssetTagHelper } # ...and for older ones +ActionView::TestCase.class_eval { include ActionView::Helpers::AssetTagHelper } if defined? ActionView::TestCase # ...for tests in older versions + +class TagHelperExtTest < ActionView::TestCase + + CF_TAG_PATTERN = %r{.*(src|href)="http://assethost.com/[a-fA-F0-9]+/.*} + + def is_cloudfront_tag(tag) + !! tag.match(CF_TAG_PATTERN) + end + + def shared_teardown + ActionView::Helpers::AssetTagHelper.class_eval do + @@asset_timestamps_cache = {} + end + ['javascripts/all.js', 'stylesheets/all.css'].each do |name| + file = File.join(Rails.public_path, name) + File.delete(file) if File.exist?(file) + end + end + + context "A configured uploader with no exclusions" do + setup do + CloudfrontAssetHost.configure do |config| + config.cname = "assethost.com" + config.bucket = "bucketname" + config.key_prefix = "" + config.s3_config = "#{RAILS_ROOT}/config/s3.yml" + config.exclude_pattern = nil + config.enabled = true + end + end + + + teardown do + shared_teardown + end + + should "use assethost for included asset path of image" do + assert_equal "http://assethost.com/d41d8cd98/images/image.png", image_path("image.png") + end + + should "use assethost for included asset path of stylesheet" do + assert_equal "http://assethost.com/ad8b295cb/stylesheets/style.css", stylesheet_path("style.css") + end + + should "use cloudfront javascript paths if :cache is NOT true" do + assert_match CF_TAG_PATTERN, javascript_include_tag("application.js") + end + + should "use cloudfront stylesheet paths if :cache is NOT true" do + assert_match CF_TAG_PATTERN, stylesheet_link_tag("style.css") + end + + should "use non-cloudfront javascript paths if :cache => true" do + assert_no_match CF_TAG_PATTERN, javascript_include_tag("application.js", :cache => true) + end + + should "use non-cloudfront stylesheet paths if :cache => true" do + assert_no_match CF_TAG_PATTERN, stylesheet_link_tag("style.css", :cache => true) + end + end + + context "A configured uploader excluding /style/" do + setup do + CloudfrontAssetHost.configure do |config| + config.cname = "assethost.com" + config.bucket = "bucketname" + config.key_prefix = "" + config.s3_config = "#{RAILS_ROOT}/config/s3.yml" + config.exclude_pattern = /style/ + config.enabled = true + end + end + + teardown do + shared_teardown + end + + should "allow standard Rails path generation for an excluded asset" do + assert_match %r{^/stylesheets/style.css\?\d+$}, stylesheet_path("style.css") + end + + should "use cloudfront javascript paths if :cache is NOT true" do + assert_match CF_TAG_PATTERN, javascript_include_tag("application.js") + end + + should "use non-cloudfront javascript paths if :cache => true" do + assert_no_match CF_TAG_PATTERN, javascript_include_tag("application.js", :cache => true) + end + + should "use non-cloudfront asset paths for excluded asset if :cache is NOT true" do + assert_no_match CF_TAG_PATTERN, stylesheet_link_tag("style.css") + end + + should "use non-cloudfront stylesheet paths if :cache => true" do + assert_no_match CF_TAG_PATTERN, stylesheet_link_tag("style.css", :cache => true) + end + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 9afe681..337e357 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,6 +2,12 @@ require 'bundler' Bundler.setup +# get ref to test dir and then move to parent dir +testdir = File.expand_path(File.dirname(__FILE__)) +Dir.chdir File.join(testdir, "..") + +$LOAD_PATH.unshift(File.join(testdir, '..', 'lib')) + require 'active_support' require 'action_controller' @@ -11,7 +17,11 @@ begin require 'redgreen'; rescue LoadError; end begin require 'turn'; rescue LoadError; end -RAILS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), 'app')) +RAILS_ROOT = File.expand_path(File.join(testdir, 'app')) + +class NullBacktraceCleaner + def clean(x); x; end +end module Rails class << self @@ -26,7 +36,13 @@ def public_path def env "production" end + + def backtrace_cleaner + @@backtrace_cleaner ||= begin + NullBacktraceCleaner.new + end + end end end -require File.join(File.dirname(__FILE__), '..', 'lib', 'cloudfront_asset_host') +require File.join(testdir, '..', 'lib', 'cloudfront_asset_host') diff --git a/test/uploader_test.rb b/test/uploader_test.rb index 80ec883..5b3dd45 100644 --- a/test/uploader_test.rb +++ b/test/uploader_test.rb @@ -1,4 +1,4 @@ -require 'test_helper' +require File.join(File.dirname(__FILE__), 'test_helper') class UploaderTest < Test::Unit::TestCase @@ -130,7 +130,7 @@ class UploaderTest < Test::Unit::TestCase css_path = CloudfrontAssetHost::Uploader.rewritten_css_path(path) File.read(css_path).split("\n").each do |line| - assert_equal "body { background-image: url(http://assethost.com/d41d8cd98/images/image.png); }", line + assert_match Regexp.new('^body \{ background: url\(http://assethost.com/d41d8cd98/images/image.png\) repeat-x;.*\}'), line end end