web_resource_bundler 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/.bundle/config +2 -0
  2. data/.gitignore +21 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +10 -0
  5. data/README +118 -0
  6. data/Rakefile +29 -0
  7. data/VERSION +1 -0
  8. data/lib/web_resource_bundler.rb +195 -0
  9. data/lib/web_resource_bundler/content_management/block_data.rb +57 -0
  10. data/lib/web_resource_bundler/content_management/block_parser.rb +63 -0
  11. data/lib/web_resource_bundler/content_management/css_url_rewriter.rb +23 -0
  12. data/lib/web_resource_bundler/content_management/resource_file.rb +35 -0
  13. data/lib/web_resource_bundler/exceptions.rb +26 -0
  14. data/lib/web_resource_bundler/file_manager.rb +30 -0
  15. data/lib/web_resource_bundler/filters.rb +9 -0
  16. data/lib/web_resource_bundler/filters/base_filter.rb +28 -0
  17. data/lib/web_resource_bundler/filters/bundle_filter.rb +58 -0
  18. data/lib/web_resource_bundler/filters/bundle_filter/resource_packager.rb +49 -0
  19. data/lib/web_resource_bundler/filters/cdn_filter.rb +48 -0
  20. data/lib/web_resource_bundler/filters/image_encode_filter.rb +56 -0
  21. data/lib/web_resource_bundler/filters/image_encode_filter/css_generator.rb +85 -0
  22. data/lib/web_resource_bundler/filters/image_encode_filter/image_data.rb +51 -0
  23. data/lib/web_resource_bundler/rails_app_helpers.rb +65 -0
  24. data/lib/web_resource_bundler/settings.rb +46 -0
  25. data/lib/web_resource_bundler/web_resource_bundler_init.rb +17 -0
  26. data/spec/public/foo.css +4 -0
  27. data/spec/public/images/good.jpg +0 -0
  28. data/spec/public/images/logo.jpg +0 -0
  29. data/spec/public/images/sdfo.jpg +0 -0
  30. data/spec/public/images/too_big_image.jpg +0 -0
  31. data/spec/public/marketing.js +14 -0
  32. data/spec/public/salog20.js +6 -0
  33. data/spec/public/sample.css +6 -0
  34. data/spec/public/seal.js +10 -0
  35. data/spec/public/set_cookies.js +8 -0
  36. data/spec/public/styles/boo.css +4 -0
  37. data/spec/public/styles/for_import.css +7 -0
  38. data/spec/public/temp.css +1 -0
  39. data/spec/public/test.css +2 -0
  40. data/spec/sample_block_helper.rb +81 -0
  41. data/spec/spec_helper.rb +82 -0
  42. data/spec/web_resource_bundler/content_management/block_data_spec.rb +33 -0
  43. data/spec/web_resource_bundler/content_management/block_parser_spec.rb +100 -0
  44. data/spec/web_resource_bundler/content_management/css_url_rewriter_spec.rb +27 -0
  45. data/spec/web_resource_bundler/content_management/resource_file_spec.rb +37 -0
  46. data/spec/web_resource_bundler/file_manager_spec.rb +60 -0
  47. data/spec/web_resource_bundler/filters/bundle_filter/filter_spec.rb +40 -0
  48. data/spec/web_resource_bundler/filters/bundle_filter/resource_packager_spec.rb +41 -0
  49. data/spec/web_resource_bundler/filters/cdn_filter_spec.rb +76 -0
  50. data/spec/web_resource_bundler/filters/image_encode_filter/css_generator_spec.rb +104 -0
  51. data/spec/web_resource_bundler/filters/image_encode_filter/filter_spec.rb +73 -0
  52. data/spec/web_resource_bundler/filters/image_encode_filter/image_data_spec.rb +53 -0
  53. data/spec/web_resource_bundler/settings_spec.rb +45 -0
  54. data/spec/web_resource_bundler/web_resource_bundler_spec.rb +90 -0
  55. data/web_resource_bundler.gemspec +111 -0
  56. metadata +146 -0
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "1"
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gem "rspec", '1.3.1'
data/Gemfile.lock ADDED
@@ -0,0 +1,10 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ rspec (1.3.1)
5
+
6
+ PLATFORMS
7
+ ruby
8
+
9
+ DEPENDENCIES
10
+ rspec (= 1.3.1)
data/README ADDED
@@ -0,0 +1,118 @@
1
+ =================== WebResourceBundler gem README ============================
2
+
3
+ Content:
4
+ 1. Purpose
5
+ 2. Functional description
6
+ 3. Installation
7
+ 4. Usage
8
+
9
+ =================== 1. Purpose ===============================================
10
+
11
+ The main purpose of WebResourceBundler gem is to minimize request & response
12
+ round-trips count. This could be done by bundling particular resource (css or js) in
13
+ one file. Encoding images in base64 and putting then in css directly.
14
+
15
+ =================== 2. Functional description ================================
16
+
17
+ WebResourceBundler parse your head html block, finding all css and js resource
18
+ files.
19
+ It can bundle resource of particular type in one single file. Base64 filter
20
+ encodes images in base64 putting them in css directly. Separate files for IE
21
+ and other browsers created. Conditional comments (like <!--[if IE 6]>) also
22
+ supported. You can use external image hosts to server images in css:
23
+ cdn filter can rewrite image urls for you. Resulted filename is a md5(filenames.sort)
24
+
25
+ =================== 3. Installation ==========================================
26
+
27
+ gem install web_resource_bundler
28
+
29
+ It's pretty easy, yeah?
30
+
31
+ =================== 4. Usage =================================================
32
+ Firstly you should create your settings file in config dir.
33
+ You can set separate settings for each environment
34
+
35
+ -------------- config/web_resource_bundler.yml --------------
36
+ development:
37
+ :base64_filter:
38
+ :use: true
39
+ :max_image_size: 23
40
+ :protocol: http
41
+ :domain: localhost:3000
42
+ :bundle_filter:
43
+ :use: true
44
+ :cdn_filter:
45
+ :use: true
46
+ :http_hosts: ['http://localhost:3000']
47
+ :https_hosts: ['https://localhost:3000']
48
+ ------------------------------------------------------------
49
+
50
+ Then you should create initializer file in
51
+ /path/to/your/rails_app/config/initializers/ directory
52
+ Let's say it will be web_resource_bundler_init.rb
53
+ Then you should put content like this in it.
54
+
55
+ ------------- config/initializers/web_resource_bundler_init.rb ---------------
56
+ require 'web_resource_bundler'
57
+ require 'yaml'
58
+ root_dir = Rails.root #or RAILS_ROOT if you are using older rails version than 3
59
+ environment = Rails.env #or RAILS_ENV in case rails <= 2.3
60
+ settings = { }
61
+ settings_file_path = File.join(root_dir, 'config', 'web_resource_bundler.yml')
62
+ if File.exist?(settings_file_path)
63
+ settings_file = File.open(settings_file_path)
64
+ all_settings = YAML::load(settings_file)
65
+ if all_settings[environment]
66
+ settings = all_settings[environment]
67
+ settings[:resource_dir] = File.join(root_dir, 'public')
68
+ end
69
+ end
70
+
71
+ WebResourceBundler::Bundler.instance.set_settings(settings)
72
+ ActionView::Base.send(:include, WebResourceBundler::RailsAppHelpers)
73
+ ------------------------------------------------------------------------------
74
+
75
+ Now in your view files you can call web_resource_bundler_process helper like this:
76
+
77
+ <head>
78
+ <% web_resource_bundler_process do %>
79
+ <%= stylesheet_link_tag :scaffold %>
80
+ <%= javascript_include_tag :defaults %>
81
+ <link type="text/css" rel="stylesheet" href="/stylesheets/somestyle.css"/>
82
+ <%=yield :head %>
83
+ <!--[if lte IE 7]>
84
+ <link type="text/css" rel="stylesheet" href="/stylesheets/ie7fix.css"/>
85
+ <link type="text/css" rel="stylesheet" href="/stylesheets/pngfix.css"/>
86
+ <![endif]-->
87
+
88
+ <% end %>
89
+ </head>
90
+ Notice:
91
+
92
+ For Rails < 3
93
+ you should use <% web_resource_bundler_process do %>
94
+
95
+ And For Rails >= 3
96
+ use <%= web_resource_bundler_process do %>
97
+
98
+
99
+ And as result you'll have
100
+
101
+
102
+ <link href="/cache/base64_style_d880a502addaa493b889c0970616430b.css?1290594873" media="screen" rel="stylesheet" type="text/css" />
103
+ <script src="/cache/script_275d311037da40e9c9b8c919a8c08b55.js?1290594873" type="text/javascript"></script>
104
+
105
+ <!--[if lte IE 7]>
106
+ <link href="/cache/base64_ie_style_d880a502addaa493b889c0970616430b.css?1290594873" media="screen" rel="stylesheet" type="text/css" />
107
+ <![endif]-->
108
+
109
+ <!--[if lte IE 7]>
110
+ <link type="text/css" rel="stylesheet" href="/cache/base64_style_ad801w02addaa493b889c0970616430b"/>
111
+ <![endif]-->
112
+
113
+ !!!
114
+ Don't forget to clean your cache directory after deploy to clean old bundles
115
+
116
+
117
+ To disable bundling and see raw results add no_bundler param
118
+ mysite.com/?no_bundler=1
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "web_resource_bundler"
9
+ gem.summary = %Q{lib for css and js content bundling and managment}
10
+ gem.description = %Q{this lib could bundle you css/js files in single file, encode images in base64, rewrite images urls to your cdn hosts}
11
+ gem.email = "anotheroneman@yahoo.com"
12
+ gem.homepage = "http://github.com/gregolsen/web-bundler"
13
+ gem.authors = ["gregolsen"]
14
+ gem.add_development_dependency "rspec", "1.3.1"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ namespace :spec do
23
+
24
+ desc "Run specs with RCov"
25
+ Spec::Rake::SpecTask.new('rcov' ) do |t|
26
+ t.spec_files = FileList['spec/**/*_spec.rb' ]
27
+ t.rcov = true
28
+ end
29
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.13
@@ -0,0 +1,195 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), 'web_resource_bundler')
2
+ require 'content_management/block_parser'
3
+ require 'content_management/block_data'
4
+ require 'content_management/css_url_rewriter'
5
+ require 'content_management/resource_file'
6
+
7
+ require 'settings'
8
+ require 'file_manager'
9
+ require 'logger'
10
+ require 'filters'
11
+ require 'exceptions'
12
+ require 'rails_app_helpers'
13
+ require 'yaml'
14
+ require 'singleton'
15
+
16
+ module WebResourceBundler
17
+ class Bundler
18
+ include Singleton
19
+
20
+ attr_reader :settings, :settings_correct, :filters
21
+ @@logger = nil
22
+ def self.logger
23
+ @@logger
24
+ end
25
+
26
+ def initialize
27
+ @filters = {}
28
+ @settings = nil
29
+ @file_manager = nil
30
+ @parser = BlockParser.new
31
+ @@logger = nil
32
+ @settings_correct = false
33
+ end
34
+
35
+ #could be used also when settings are different on each request
36
+ def set_settings(settings)
37
+ #all methods user call from rails should not raise any exception
38
+ begin
39
+ @settings = Settings.new settings
40
+ if @settings.resource_dir
41
+ @@logger = create_logger(@settings)
42
+ unless @settings.cache_dir
43
+ @settings.cache_dir = 'cache'
44
+ end
45
+ #if file manager is nil it should be created
46
+ unless @file_manager
47
+ @file_manager = FileManager.new(@settings.resource_dir, @settings.cache_dir)
48
+ else
49
+ #if manager already exist than its settings chaged
50
+ @file_manager.resource_dir, @file_manager.cache_dir = @settings.resource_dir, @settings.cache_dir
51
+ end
52
+ set_filters(@settings, @file_manager)
53
+ #used to determine if bundler in correct state and could be used
54
+ @settings_correct = true
55
+ end
56
+ rescue Exception => e
57
+ @@logger.error("Incorrect settings initialization, #{settings}\n#{e.to_s}") if @@logger
58
+ @settings_correct = false
59
+ end
60
+ end
61
+
62
+ #main method to process html text block
63
+ def process(block)
64
+ begin
65
+ filters = filters_array
66
+ #parsing html text block, creating BlockData instance
67
+ block_data = @parser.parse(block)
68
+ #if filters set and no bundle files exists we should process block data
69
+ unless filters.empty? or bundle_upto_date?(block_data)
70
+ #reading files content and populating block_data
71
+ read_resources!(block_data)
72
+ #applying filters to block_data
73
+ block_data.apply_filters(filters)
74
+ #writing resulted files with filtered content on disk
75
+ write_files_on_disk(block_data)
76
+ @@logger.info("files written on disk")
77
+ return block_data
78
+ end
79
+ #bundle up to date, returning existing block with modified file names
80
+ block_data.apply_filters(filters)
81
+ return block_data
82
+ rescue Exceptions::WebResourceBundlerError => e
83
+ @@logger.error(e.to_s)
84
+ return nil
85
+ rescue Exception => e
86
+ @@logger.error(e.backtrace.join("\n") + "Unknown error occured: " + e.to_s)
87
+ return nil
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ #giving filters array in right sequence (bundle filter should be first)
94
+ def filters_array
95
+ filters = []
96
+ %w{bundle_filter base64_filter cdn_filter}.each do |key|
97
+ filters << @filters[key.to_sym] if @filters[key.to_sym]
98
+ end
99
+ filters
100
+ end
101
+
102
+ #creates filters or change their settings
103
+ def set_filters(settings, file_manager)
104
+ #common settings same for all filters
105
+ common_sets = {
106
+ :resource_dir => settings.resource_dir,
107
+ :cache_dir => settings.cache_dir
108
+ }
109
+ #used to craete filters
110
+ filters_data = {
111
+ :bundle_filter => 'BundleFilter',
112
+ :base64_filter => 'ImageEncodeFilter',
113
+ :cdn_filter => 'CdnFilter'
114
+ }
115
+ filters_data.each_pair do |key, filter_class|
116
+ if settings[key] and settings[key][:use]
117
+ filter_settings = settings[key].merge(common_sets)
118
+ if @filters[key]
119
+ @filters[key].set_settings(filter_settings)
120
+ else
121
+ #creating filter instance with settings
122
+ @filters[key] = eval("Filters::" + filter_class + "::Filter").new(filter_settings, file_manager)
123
+ end
124
+ else
125
+ #this filter turned off in settings so should be deleted
126
+ @filters.delete(key)
127
+ end
128
+ end
129
+ @filters
130
+ end
131
+
132
+ def create_logger(settings)
133
+ begin
134
+ #creating default log file in rails log directory called web_resource_bundler.log
135
+ unless settings.log_path
136
+ log_dir = File.expand_path('../log', settings.resource_dir)
137
+ log_name = 'web_resource_bundler.log'
138
+ settings[:log_path] = File.join(log_dir, log_name)
139
+ Dir.mkdir(log_dir) unless File.exist?(log_dir)
140
+ end
141
+ file = File.open(settings.log_path, File::WRONLY | File::APPEND | File::CREAT)
142
+ logger = Logger.new(file)
143
+ rescue Exception => e
144
+ logger = Logger.new(STDOUT)
145
+ logger.error("Can't create log file, check log path: #{settings.log_path}\n#{e.to_s}")
146
+ end
147
+ logger
148
+ end
149
+
150
+ #checks if resulted files exist for current @filters and block data
151
+ def bundle_upto_date?(block_data)
152
+ #we don't want to change original parsed block data
153
+ #so just making a clone, using overriden clone method in BlockData
154
+ block_data_copy = block_data.clone
155
+ #modifying clone to obtain resulted files
156
+ #apply_filters will just compute resulted file paths
157
+ #because block_data isn't populated with files content yet
158
+ block_data_copy.apply_filters(filters_array)
159
+ #cheking if resulted files exist on disk in cache folder
160
+ block_data_copy.files.each do |file|
161
+ return false unless File.exist?(File.join(@settings.resource_dir, file.path))
162
+ end
163
+ true
164
+ end
165
+
166
+ #reads block data resource files content from disk and populating block_data
167
+ def read_resources!(block_data)
168
+ #iterating through each resource files
169
+ block_data.files.each do |file|
170
+ content = @file_manager.get_content(file.path)
171
+ #rewriting url to absolute if content is css
172
+ WebResourceBundler::CssUrlRewriter.rewrite_content_urls!(file.path, content) if file.type[:ext] == 'css'
173
+ file.content = content
174
+ end
175
+ #making the same for each child blocks, recursively
176
+ block_data.child_blocks.each do |block|
177
+ read_resources!(block)
178
+ end
179
+ end
180
+
181
+ #recursive method to write all resulted files on disk
182
+ def write_files_on_disk(block_data)
183
+ @file_manager.create_cache_dir
184
+ block_data.files.each do |file|
185
+ File.open(File.join(@settings.resource_dir, file.path), "w") do |f|
186
+ f.print(file.content)
187
+ end
188
+ end
189
+ block_data.child_blocks.each do |block|
190
+ write_files_on_disk(block)
191
+ end
192
+ end
193
+
194
+ end
195
+ end
@@ -0,0 +1,57 @@
1
+ module WebResourceBundler
2
+ class BlockData
3
+ attr_accessor :files, :inline_block, :condition, :child_blocks
4
+
5
+ def initialize(condition = "")
6
+ @inline_block = ""
7
+ @files = []
8
+ @condition = condition
9
+ @child_blocks = []
10
+ end
11
+
12
+ def styles
13
+ @files.select do |f|
14
+ [WebResourceBundler::ResourceFileType::CSS,
15
+ WebResourceBundler::ResourceFileType::IE_CSS].include?(f.type)
16
+ end
17
+ end
18
+
19
+ def scripts
20
+ @files.select {|f| f.type == WebResourceBundler::ResourceFileType::JS}
21
+ end
22
+
23
+ def clone
24
+ clon = self.dup
25
+ clon.files = self.files.map {|f| f.clone}
26
+ if clon.child_blocks.size > 0
27
+ clon.child_blocks = self.child_blocks.map do |block|
28
+ block.clone
29
+ end
30
+ else
31
+ clon.child_blocks = []
32
+ end
33
+ clon
34
+ end
35
+
36
+ def self.all_childs(block_data)
37
+ result = []
38
+ result << block_data
39
+ block_data.child_blocks.each do |child|
40
+ result += BlockData.all_childs(child)
41
+ end
42
+ return result
43
+ end
44
+
45
+ def apply_filters(filters)
46
+ unless filters.empty?
47
+ filters.each do |filter|
48
+ items = BlockData.all_childs(self)
49
+ items.each do |block_data|
50
+ filter.apply!(block_data)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ end