web_resource_bundler 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.gitignore +21 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +10 -0
- data/README +118 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/lib/web_resource_bundler.rb +195 -0
- data/lib/web_resource_bundler/content_management/block_data.rb +57 -0
- data/lib/web_resource_bundler/content_management/block_parser.rb +63 -0
- data/lib/web_resource_bundler/content_management/css_url_rewriter.rb +23 -0
- data/lib/web_resource_bundler/content_management/resource_file.rb +35 -0
- data/lib/web_resource_bundler/exceptions.rb +26 -0
- data/lib/web_resource_bundler/file_manager.rb +30 -0
- data/lib/web_resource_bundler/filters.rb +9 -0
- data/lib/web_resource_bundler/filters/base_filter.rb +28 -0
- data/lib/web_resource_bundler/filters/bundle_filter.rb +58 -0
- data/lib/web_resource_bundler/filters/bundle_filter/resource_packager.rb +49 -0
- data/lib/web_resource_bundler/filters/cdn_filter.rb +48 -0
- data/lib/web_resource_bundler/filters/image_encode_filter.rb +56 -0
- data/lib/web_resource_bundler/filters/image_encode_filter/css_generator.rb +85 -0
- data/lib/web_resource_bundler/filters/image_encode_filter/image_data.rb +51 -0
- data/lib/web_resource_bundler/rails_app_helpers.rb +65 -0
- data/lib/web_resource_bundler/settings.rb +46 -0
- data/lib/web_resource_bundler/web_resource_bundler_init.rb +17 -0
- data/spec/public/foo.css +4 -0
- data/spec/public/images/good.jpg +0 -0
- data/spec/public/images/logo.jpg +0 -0
- data/spec/public/images/sdfo.jpg +0 -0
- data/spec/public/images/too_big_image.jpg +0 -0
- data/spec/public/marketing.js +14 -0
- data/spec/public/salog20.js +6 -0
- data/spec/public/sample.css +6 -0
- data/spec/public/seal.js +10 -0
- data/spec/public/set_cookies.js +8 -0
- data/spec/public/styles/boo.css +4 -0
- data/spec/public/styles/for_import.css +7 -0
- data/spec/public/temp.css +1 -0
- data/spec/public/test.css +2 -0
- data/spec/sample_block_helper.rb +81 -0
- data/spec/spec_helper.rb +82 -0
- data/spec/web_resource_bundler/content_management/block_data_spec.rb +33 -0
- data/spec/web_resource_bundler/content_management/block_parser_spec.rb +100 -0
- data/spec/web_resource_bundler/content_management/css_url_rewriter_spec.rb +27 -0
- data/spec/web_resource_bundler/content_management/resource_file_spec.rb +37 -0
- data/spec/web_resource_bundler/file_manager_spec.rb +60 -0
- data/spec/web_resource_bundler/filters/bundle_filter/filter_spec.rb +40 -0
- data/spec/web_resource_bundler/filters/bundle_filter/resource_packager_spec.rb +41 -0
- data/spec/web_resource_bundler/filters/cdn_filter_spec.rb +76 -0
- data/spec/web_resource_bundler/filters/image_encode_filter/css_generator_spec.rb +104 -0
- data/spec/web_resource_bundler/filters/image_encode_filter/filter_spec.rb +73 -0
- data/spec/web_resource_bundler/filters/image_encode_filter/image_data_spec.rb +53 -0
- data/spec/web_resource_bundler/settings_spec.rb +45 -0
- data/spec/web_resource_bundler/web_resource_bundler_spec.rb +90 -0
- data/web_resource_bundler.gemspec +111 -0
- metadata +146 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'base64'
|
2
|
+
module WebResourceBundler
|
3
|
+
module Filters
|
4
|
+
module ImageEncodeFilter
|
5
|
+
#ImageData contains info about image found in css files
|
6
|
+
class ImageData
|
7
|
+
MAX_RAND_FOR_ID = 10000
|
8
|
+
attr_reader :extension, :id, :path, :exist, :url
|
9
|
+
|
10
|
+
def initialize(url, folder)
|
11
|
+
@url = url
|
12
|
+
#computing absolute file path using folder of css file
|
13
|
+
@path = File.join(folder, url)
|
14
|
+
if File.file?(@path)
|
15
|
+
@exist = true
|
16
|
+
else
|
17
|
+
@exist = false
|
18
|
+
end
|
19
|
+
if WebResourceBundler::Bundler.logger and !@path.include?('://') and !@exist
|
20
|
+
WebResourceBundler::Bundler.logger.info("Image not found #{@path}")
|
21
|
+
end
|
22
|
+
if @exist
|
23
|
+
@size = File.size(@path)
|
24
|
+
name, @extension = File.basename(@path).split('.')
|
25
|
+
#id is a filename plus random number - to support uniqueness
|
26
|
+
@id = name + rand(MAX_RAND_FOR_ID).to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def size
|
31
|
+
@size / 1024
|
32
|
+
end
|
33
|
+
|
34
|
+
#constructs part of css header with data for current image
|
35
|
+
def construct_mhtml_image_data(separator)
|
36
|
+
if @exist
|
37
|
+
result = separator + "\n"
|
38
|
+
result += "Content-Location:" + @id + "\n"
|
39
|
+
result += "Content-Transfer-Encoding:base64" + "\n\n"
|
40
|
+
result += encoded + "\n\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def encoded
|
45
|
+
return nil unless @exist
|
46
|
+
Base64.encode64(File.read(@path)).gsub("\n", '')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module WebResourceBundler::RailsAppHelpers
|
2
|
+
|
3
|
+
def web_resource_bundler_process(&block)
|
4
|
+
#getting ActionView::NonConcattingString
|
5
|
+
#but we want simple string to escape problems
|
6
|
+
result = String.new(capture(&block))
|
7
|
+
#result is original block content by default
|
8
|
+
version = Rails::VERSION::STRING
|
9
|
+
if !params['no_bundler'] and WebResourceBundler::Bundler.instance.settings_correct
|
10
|
+
#we want to keep original string unchanged so we can return same content on error
|
11
|
+
block_data = WebResourceBundler::Bundler.instance.process(result.dup)
|
12
|
+
#if everything ok with bundling we should construct resulted html content and change result
|
13
|
+
result = construct_block(block_data, WebResourceBundler::Bundler.instance.settings) if block_data
|
14
|
+
end
|
15
|
+
case
|
16
|
+
when version >= '3.0.0' then return raw(result)
|
17
|
+
when (version >= '2.2.0' and version < '2.4.0') then concat(result)
|
18
|
+
else
|
19
|
+
concat(result, block.binding)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def construct_block(block_data, settings)
|
24
|
+
result = ""
|
25
|
+
#we should include only mhtml files if browser IE 7 or 6
|
26
|
+
if mhtml_should_be_added?
|
27
|
+
styles = block_data.files.select {|f| [WebResourceBundler::ResourceFileType::MHTML, WebResourceBundler::ResourceFileType::IE_CSS].include?(f.type)}
|
28
|
+
else
|
29
|
+
#it normal browser - so just including base64 css
|
30
|
+
styles = block_data.files.select {|f| f.type == WebResourceBundler::ResourceFileType::CSS}
|
31
|
+
end
|
32
|
+
styles.each do |file|
|
33
|
+
url = File.join('/', file.path)
|
34
|
+
result << stylesheet_link_tag(url)
|
35
|
+
result << "\n"
|
36
|
+
end
|
37
|
+
block_data.scripts.each do |file|
|
38
|
+
url = File.join('/', file.path)
|
39
|
+
result << javascript_include_tag(url)
|
40
|
+
result << "\n"
|
41
|
+
end
|
42
|
+
result << block_data.inline_block unless block_data.inline_block.blank?
|
43
|
+
block_data.child_blocks.each do |block|
|
44
|
+
result << construct_block(block, settings)
|
45
|
+
end
|
46
|
+
unless block_data.condition.empty?
|
47
|
+
result = "<!--#{block_data.condition}>\n #{result}<![endif]-->\n"
|
48
|
+
end
|
49
|
+
#removing unnecessary new line symbols
|
50
|
+
result.gsub!(/\n(\s)+/, "\n")
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def mhtml_should_be_added?
|
55
|
+
result = false
|
56
|
+
pattern = /MSIE (.*?);/
|
57
|
+
header = request.headers['HTTP_USER_AGENT']
|
58
|
+
match = pattern.match(header)
|
59
|
+
if match and match[1] <= '7.0'
|
60
|
+
result = true
|
61
|
+
end
|
62
|
+
return result
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module WebResourceBundler
|
2
|
+
class Settings
|
3
|
+
@@defaults = {
|
4
|
+
:cache_dir => 'cache',
|
5
|
+
:base64_filter => {
|
6
|
+
:max_image_size => 23, #kbytes
|
7
|
+
:protocol => 'http',
|
8
|
+
:domain => 'localhost:3000'
|
9
|
+
},
|
10
|
+
:bundle_filter => {
|
11
|
+
:md5_additional_data => []
|
12
|
+
},
|
13
|
+
:cdn_filter => {
|
14
|
+
:http_hosts => ['http://localhost:3000'],
|
15
|
+
:https_hosts => ['https://localhost:3000']
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize(hash = {})
|
20
|
+
@settings = hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def set(hash)
|
24
|
+
@settings.merge!(hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](i)
|
28
|
+
@settings[i]
|
29
|
+
end
|
30
|
+
|
31
|
+
def []=(i , v)
|
32
|
+
@settings[i] = v
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(m, *args, &block)
|
36
|
+
m=m.to_s
|
37
|
+
if /.*=\z/.match(m)
|
38
|
+
@settings[m[0..-2].to_sym] = args[0]
|
39
|
+
else
|
40
|
+
@settings[m.to_sym]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'web_resource_bundler'
|
2
|
+
require 'yaml'
|
3
|
+
root_dir = Rails.root #or RAILS_ROOT if you are using older rails version than 3
|
4
|
+
environment = Rails.env
|
5
|
+
settings = { }
|
6
|
+
settings_file_path = File.join(root_dir,'config', 'web_resource_bundler.yml')
|
7
|
+
if File.exist?(settings_file_path)
|
8
|
+
settings_file = File.open(settings_file_path)
|
9
|
+
all_settings = YAML::load(settings_file)
|
10
|
+
if all_settings[environment]
|
11
|
+
settings = all_settings[environment]
|
12
|
+
settings[:resource_dir] = File.join(root_dir, 'public')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
WebResourceBundler::Bundler.instance.set_settings(settings)
|
17
|
+
ActionView::Base.send(:include, WebResourceBundler::RailsAppHelpers)
|
data/spec/public/foo.css
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,14 @@
|
|
1
|
+
function loadScript(scriptURL) {
|
2
|
+
var newScript = document.createElement("script");
|
3
|
+
newScript.src = scriptURL;
|
4
|
+
document.body.appendChild(newScript);
|
5
|
+
}
|
6
|
+
|
7
|
+
function setCookie (name, value, expires, path, domain, secure) {
|
8
|
+
var rateCookie = name + "=" + escape(value) +
|
9
|
+
((expires) ? "; expires=" + expires.toGMTString() : "") +
|
10
|
+
((path) ? "; path=" + path : "") +
|
11
|
+
((domain) ? "; domain=" + domain : "") +
|
12
|
+
((secure) ? "; secure" : "");
|
13
|
+
document.cookie = rateCookie;
|
14
|
+
}
|
data/spec/public/seal.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
function fixPopupInIE() {
|
2
|
+
var style = document.createElement('style');
|
3
|
+
style.setAttribute("type", "text/css");
|
4
|
+
document.body.appendChild(style);
|
5
|
+
|
6
|
+
document.getElementById('wrapper').style.position = 'absolute';
|
7
|
+
document.getElementById('shadow').style.position = 'absolute';
|
8
|
+
document.getElementById('container').style.position = 'absolute';
|
9
|
+
document.getElementById('popup-loading').style.position = 'absolute';
|
10
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
background: url('./images/1.png');background-image: url('./images/1.png');
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class SampleBlockHelper
|
2
|
+
def initialize(styles, scripts, settings)
|
3
|
+
@styles = styles
|
4
|
+
@scripts = scripts
|
5
|
+
@settings = settings
|
6
|
+
end
|
7
|
+
|
8
|
+
def construct_js_link(path)
|
9
|
+
"<script src = \"#{path}\" type=\"text/javascript\"></script>"
|
10
|
+
end
|
11
|
+
|
12
|
+
def construct_css_link(path)
|
13
|
+
"<link href = \"#{path}\" media=\"screen\" rel=\"Stylesheet\" type=\"text/css\" />"
|
14
|
+
end
|
15
|
+
|
16
|
+
def sample_inline_block
|
17
|
+
"this is inline block content" +
|
18
|
+
"<script>abracadabra</script><style>abracadabra</style>"
|
19
|
+
end
|
20
|
+
|
21
|
+
def construct_links_block(styles, scripts)
|
22
|
+
block = ""
|
23
|
+
styles.each do |path|
|
24
|
+
block += construct_css_link(path)
|
25
|
+
end
|
26
|
+
scripts.each do |path|
|
27
|
+
block += construct_js_link(path)
|
28
|
+
end
|
29
|
+
block
|
30
|
+
end
|
31
|
+
|
32
|
+
def construct_resource_file(type, file, content = nil)
|
33
|
+
resource_file = WebResourceBundler::ResourceFile.new(type, file)
|
34
|
+
if content
|
35
|
+
resource_file.content = content
|
36
|
+
else
|
37
|
+
resource_file.content = File.read(File.join(@settings.resource_dir, file))
|
38
|
+
end
|
39
|
+
resource_file
|
40
|
+
end
|
41
|
+
|
42
|
+
def sample_cond_block
|
43
|
+
"<!-- [if IE 7] >" +
|
44
|
+
construct_links_block(@styles[(@styles.size / 2)..-1], @scripts[(@scripts.size / 2)..-1]) +
|
45
|
+
sample_inline_block +
|
46
|
+
"<! [endif] -->"
|
47
|
+
end
|
48
|
+
|
49
|
+
def sample_block
|
50
|
+
block = construct_links_block(@styles[0..(@styles.size / 2 - 1)], @scripts[0..(@scripts.size / 2 - 1)]) + "\n"
|
51
|
+
block += sample_inline_block
|
52
|
+
block += sample_cond_block
|
53
|
+
end
|
54
|
+
|
55
|
+
def sample_block_data
|
56
|
+
data = BlockData.new
|
57
|
+
@styles[0..(@styles.size / 2 - 1)].each do |file|
|
58
|
+
data.files << construct_resource_file(ResourceFileType::CSS, file)
|
59
|
+
end
|
60
|
+
@scripts[0..(@scripts.size / 2 - 1)].each do |file|
|
61
|
+
data.files << construct_resource_file(ResourceFileType::JS, file)
|
62
|
+
end
|
63
|
+
data.inline_block = sample_inline_block
|
64
|
+
data.child_blocks << child_block_data
|
65
|
+
data
|
66
|
+
end
|
67
|
+
|
68
|
+
def child_block_data
|
69
|
+
child = BlockData.new("[if IE 7]")
|
70
|
+
@styles[(@styles.size / 2)..(@styles.size - 1)].each do |file|
|
71
|
+
child.files << construct_resource_file(ResourceFileType::CSS, file)
|
72
|
+
end
|
73
|
+
@scripts[(@scripts.size / 2)..(@scripts.size - 1)].each do |file|
|
74
|
+
child.files << construct_resource_file(ResourceFileType::JS, file)
|
75
|
+
end
|
76
|
+
child.inline_block = sample_inline_block
|
77
|
+
child
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#require 'simplecov'
|
2
|
+
#SimpleCov.start
|
3
|
+
require File.join(File.dirname(__FILE__), "../lib/web_resource_bundler")
|
4
|
+
require 'fileutils'
|
5
|
+
require File.join(File.dirname(__FILE__), 'sample_block_helper')
|
6
|
+
require 'logger'
|
7
|
+
include WebResourceBundler
|
8
|
+
|
9
|
+
def clean_cache_dir
|
10
|
+
cache_dir_path = File.join(File.dirname(__FILE__), '/public/cache')
|
11
|
+
FileUtils.rm_rf(cache_dir_path) if File.exist?(cache_dir_path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def styles
|
15
|
+
["/sample.css","/foo.css", "/test.css", "/styles/boo.css"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def scripts
|
19
|
+
["/set_cookies.js", "/seal.js", "/salog20.js", "/marketing.js"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def settings
|
23
|
+
Settings.new(settings_hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
def settings_hash
|
27
|
+
{
|
28
|
+
:resource_dir => File.join(File.dirname(__FILE__), '/public'),
|
29
|
+
:cache_dir => 'cache',
|
30
|
+
:log_path => File.join(File.dirname(__FILE__), '/bundler.log'),
|
31
|
+
:base64_filter => {
|
32
|
+
:use => true,
|
33
|
+
:max_image_size => 23, #kbytes
|
34
|
+
:protocol => 'http',
|
35
|
+
:domain => 'localhost:3000'
|
36
|
+
},
|
37
|
+
:bundle_filter => {
|
38
|
+
:use => true,
|
39
|
+
:md5_additional_data => ['localhost:3000', 'http'],
|
40
|
+
:filename_additional_data => ['en']
|
41
|
+
},
|
42
|
+
:cdn_filter => {
|
43
|
+
:use => true,
|
44
|
+
:http_hosts => ['http://localhost:3000'],
|
45
|
+
:https_hosts => ['https://localhost:3000']
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def bundle_settings
|
51
|
+
settings.bundle_filter.merge(common_settings)
|
52
|
+
end
|
53
|
+
|
54
|
+
def cdn_settings
|
55
|
+
settings.cdn_filter.merge(common_settings)
|
56
|
+
end
|
57
|
+
|
58
|
+
def base64_settings
|
59
|
+
settings.base64_filter.merge(common_settings)
|
60
|
+
end
|
61
|
+
|
62
|
+
def common_settings
|
63
|
+
{:resource_dir => settings.resource_dir, :cache_dir => settings.cache_dir}
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
Spec::Runner.configure do |config|
|
68
|
+
config.before(:all) do
|
69
|
+
config.mock_with :rspec
|
70
|
+
end
|
71
|
+
config.before(:all) do
|
72
|
+
@sample_block_helper = SampleBlockHelper.new(styles, scripts, settings)
|
73
|
+
@logger = Logger.new(STDOUT)
|
74
|
+
end
|
75
|
+
|
76
|
+
config.after(:all) do
|
77
|
+
File.delete(settings.log_path) if File.exist?(settings.log_path)
|
78
|
+
log_path = File.expand_path('../log', settings.resource_dir)
|
79
|
+
FileUtils.rm_rf(log_path) if File.exist?(log_path)
|
80
|
+
clean_cache_dir
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "../../spec_helper"))
|
2
|
+
describe WebResourceBundler::BlockData do
|
3
|
+
describe "#apply_filter" do
|
4
|
+
it "applies filter to block_data, its childs, and theirs childs etc." do
|
5
|
+
filter = mock("filter")
|
6
|
+
block_data = @sample_block_helper.sample_block_data
|
7
|
+
filter.should_receive(:apply!).with(block_data)
|
8
|
+
filter.should_receive(:apply!).with(block_data.child_blocks.first)
|
9
|
+
filters = [filter]
|
10
|
+
block_data.apply_filters(filters)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#all_childs" do
|
15
|
+
it "creates array of block data and all its childs recursively" do
|
16
|
+
block_data = @sample_block_helper.sample_block_data
|
17
|
+
BlockData.all_childs(block_data).size.should == 2
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#clone" do
|
22
|
+
it "creates deep clone of block data" do
|
23
|
+
block_data = @sample_block_helper.sample_block_data
|
24
|
+
clon = block_data.clone
|
25
|
+
block_data.object_id.should_not == clon.object_id
|
26
|
+
((block_data.files.map { |f| f.object_id })& clon.files.map {|f| f.object_id}).should be_empty
|
27
|
+
child = block_data.child_blocks[0]
|
28
|
+
child_copy = clon.child_blocks[0]
|
29
|
+
child.object_id.should_not == child_copy.object_id
|
30
|
+
((child.files.map { |f| f.object_id }) & child_copy.files.map {|f| f.object_id}).should be_empty
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|