mack-asset_packager 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,25 @@
1
+ Asset Packager
2
+ ===========================================================
3
+
4
+ Summary:
5
+ Allow application to bundle up (and compress) asset files and include the compressed file in the view.
6
+ This will make the application more efficient, because the browser won't need to make multiple
7
+ request for each single asset files.
8
+ The packaging/compression will only be performed in production mode, or when configatron.mack.asset_packager.disable_bundle_merge is not set.
9
+
10
+ Usage:
11
+ First you will need to define asset group/bundle that you want to package up by doing the following:
12
+ 1. Create an assets.rb file in config/initializers folder
13
+ 2. In that file do the following:
14
+ assets_mgr.my_bundle_1 do |a|
15
+ a.add_js "foo.js"
16
+ a.add_js "jquery"
17
+ a.add_css "scaffold"
18
+ end
19
+ So the above code will create a new bundle called my_bundle_1, and in that group, you'll add 2 javascript files and 1 css file.
20
+ 3. Once you have that defined, next step would be in your layout file (or whereever you refer to the asset file):
21
+ <%= stylesheet 'my_bundle_1 %>
22
+ <%= javascript 'my_bundle_1 %>
23
+ 4. That's it. When run in development mode, the javascript tag will be evaluated into 4 <script> tags, and the stylesheet scriptlet
24
+ will get evaluated into 1 <stylesheet> tag. In production (assuming disable_bundle_merge configuration is not set), then
25
+ each scriptlet tag will get evaluated into 1 tag (each will refer to the packaged and compressed asset file)
@@ -0,0 +1,14 @@
1
+ fl = File.join(File.dirname(__FILE__), "mack-asset_packager")
2
+
3
+ [:package_collection, :link_helpers, :cssmin].each do |f|
4
+ file = File.join(fl, "#{f}.rb")
5
+ # puts "requiring #{file}"
6
+ require file
7
+ end
8
+
9
+ # default configuration
10
+ if Mack.env == "production"
11
+ configatron.mack.assets.set_default(:enable_bundle_merge, true)
12
+ else
13
+ configatron.mack.assets.set_default(:enable_bundle_merge, false)
14
+ end
@@ -0,0 +1,49 @@
1
+ class CSSMin
2
+
3
+ attr_accessor :data
4
+ def initialize(content)
5
+ self.data = content
6
+ end
7
+
8
+ def minimize
9
+ # find all elements in between { and }, and remove \n and \t
10
+ # self.data.scan(/\{([.|\s\S]*?)\}/).flatten.each do |elt|
11
+ # str = elt.gsub("\n", "")
12
+ # str = str.gsub("\t", "")
13
+ # self.data.gsub!(elt, str)
14
+ # end
15
+
16
+ self.data = self.data.squeeze(" ").squeeze("\t").squeeze("\n")
17
+ self.data.gsub!("\t", "")
18
+
19
+ # remove comments in the form of /* ... */
20
+ self.data.gsub!(/\/\*[.|\s|\S]*?\*\//, "")
21
+
22
+ # remove newline before and after {
23
+ self.data.gsub!("\n\{", " \{")
24
+ self.data.gsub!("\{\n", " \{")
25
+
26
+ # remove newline after ,
27
+ self.data.gsub!(",\n", ", ")
28
+
29
+ # remove spaces after/before ...
30
+ self.data.gsub!(": ", ":")
31
+ self.data.gsub!(" :", ":")
32
+ self.data.gsub!(", ", ",")
33
+ self.data.gsub!(" ,", ",")
34
+ self.data.gsub!("; ", ";")
35
+ self.data.gsub!(" ;", ";")
36
+ self.data.gsub!(";\}", "\}")
37
+ self.data.gsub!(" \}", "\}")
38
+ self.data.gsub!("\{ ", "\{")
39
+ self.data.gsub!(" \{", "\{")
40
+
41
+ # remove all new line, but add one after }
42
+ self.data.gsub!("\n", "")
43
+ self.data.gsub!("\}", "\}\n")
44
+
45
+ self.data = self.data.squeeze(" ").squeeze("\t").squeeze("\n").strip
46
+
47
+ return self.data
48
+ end
49
+ end
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/ruby
2
+ # jsmin.rb 2007-07-20
3
+ # Author: Uladzislau Latynski
4
+ # This work is a translation from C to Ruby of jsmin.c published by
5
+ # Douglas Crockford. Permission is hereby granted to use the Ruby
6
+ # version under the same conditions as the jsmin.c on which it is
7
+ # based.
8
+ #
9
+ # /* jsmin.c
10
+ # 2003-04-21
11
+ #
12
+ # Copyright (c) 2002 Douglas Crockford (www.crockford.com)
13
+ #
14
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
15
+ # this software and associated documentation files (the "Software"), to deal in
16
+ # the Software without restriction, including without limitation the rights to
17
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
18
+ # of the Software, and to permit persons to whom the Software is furnished to do
19
+ # so, subject to the following conditions:
20
+ #
21
+ # The above copyright notice and this permission notice shall be included in all
22
+ # copies or substantial portions of the Software.
23
+ #
24
+ # The Software shall be used for Good, not Evil.
25
+ #
26
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ # SOFTWARE.
33
+
34
+ EOF = -1
35
+ $theA = ""
36
+ $theB = ""
37
+
38
+ # isAlphanum -- return true if the character is a letter, digit, underscore,
39
+ # dollar sign, or non-ASCII character
40
+ def isAlphanum(c)
41
+ return false if !c || c == EOF
42
+ return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
43
+ (c >= 'A' && c <= 'Z') || c == '_' || c == '$' ||
44
+ c == '\\' || c[0] > 126)
45
+ end
46
+
47
+ # get -- return the next character from stdin. Watch out for lookahead. If
48
+ # the character is a control character, translate it to a space or linefeed.
49
+ def get()
50
+ c = $stdin.getc
51
+ return EOF if(!c)
52
+ c = c.chr
53
+ return c if (c >= " " || c == "\n" || c.unpack("c") == EOF)
54
+ return "\n" if (c == "\r")
55
+ return " "
56
+ end
57
+
58
+ # Get the next character without getting it.
59
+ def peek()
60
+ lookaheadChar = $stdin.getc
61
+ $stdin.ungetc(lookaheadChar)
62
+ return lookaheadChar.chr
63
+ end
64
+
65
+ # mynext -- get the next character, excluding comments.
66
+ # peek() is used to see if a '/' is followed by a '/' or '*'.
67
+ def mynext()
68
+ c = get
69
+ if (c == "/")
70
+ if(peek == "/")
71
+ while(true)
72
+ c = get
73
+ if (c <= "\n")
74
+ return c
75
+ end
76
+ end
77
+ end
78
+ if(peek == "*")
79
+ get
80
+ while(true)
81
+ case get
82
+ when "*"
83
+ if (peek == "/")
84
+ get
85
+ return " "
86
+ end
87
+ when EOF
88
+ raise "Unterminated comment"
89
+ end
90
+ end
91
+ end
92
+ end
93
+ return c
94
+ end
95
+
96
+
97
+ # action -- do something! What you do is determined by the argument: 1
98
+ # Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B.
99
+ # (Delete A). 3 Get the next B. (Delete B). action treats a string as a
100
+ # single character. Wow! action recognizes a regular expression if it is
101
+ # preceded by ( or , or =.
102
+ def action(a)
103
+ if(a==1)
104
+ $stdout.write $theA
105
+ end
106
+ if(a==1 || a==2)
107
+ $theA = $theB
108
+ if ($theA == "\'" || $theA == "\"")
109
+ while (true)
110
+ $stdout.write $theA
111
+ $theA = get
112
+ break if ($theA == $theB)
113
+ raise "Unterminated string literal" if ($theA <= "\n")
114
+ if ($theA == "\\")
115
+ $stdout.write $theA
116
+ $theA = get
117
+ end
118
+ end
119
+ end
120
+ end
121
+ if(a==1 || a==2 || a==3)
122
+ $theB = mynext
123
+ if ($theB == "/" && ($theA == "(" || $theA == "," || $theA == "=" ||
124
+ $theA == ":" || $theA == "[" || $theA == "!" ||
125
+ $theA == "&" || $theA == "|" || $theA == "?" ||
126
+ $theA == "{" || $theA == "}" || $theA == ";" ||
127
+ $theA == "\n"))
128
+ $stdout.write $theA
129
+ $stdout.write $theB
130
+ while (true)
131
+ $theA = get
132
+ if ($theA == "/")
133
+ break
134
+ elsif ($theA == "\\")
135
+ $stdout.write $theA
136
+ $theA = get
137
+ elsif ($theA <= "\n")
138
+ raise "Unterminated RegExp Literal"
139
+ end
140
+ $stdout.write $theA
141
+ end
142
+ $theB = mynext
143
+ end
144
+ end
145
+ end
146
+
147
+ # jsmin -- Copy the input to the output, deleting the characters which are
148
+ # insignificant to JavaScript. Comments will be removed. Tabs will be
149
+ # replaced with spaces. Carriage returns will be replaced with linefeeds.
150
+ # Most spaces and linefeeds will be removed.
151
+ def jsmin
152
+ $theA = "\n"
153
+ action(3)
154
+ while ($theA != EOF)
155
+ case $theA
156
+ when " "
157
+ if (isAlphanum($theB))
158
+ action(1)
159
+ else
160
+ action(2)
161
+ end
162
+ when "\n"
163
+ case ($theB)
164
+ when "{","[","(","+","-"
165
+ action(1)
166
+ when " "
167
+ action(3)
168
+ else
169
+ if (isAlphanum($theB))
170
+ action(1)
171
+ else
172
+ action(2)
173
+ end
174
+ end
175
+ else
176
+ case ($theB)
177
+ when " "
178
+ if (isAlphanum($theA))
179
+ action(1)
180
+ else
181
+ action(3)
182
+ end
183
+ when "\n"
184
+ case ($theA)
185
+ when "}","]",")","+","-","\"","\\", "'", '"'
186
+ action(1)
187
+ else
188
+ if (isAlphanum($theA))
189
+ action(1)
190
+ else
191
+ action(3)
192
+ end
193
+ end
194
+ else
195
+ action(1)
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ ARGV.each do |anArg|
202
+ $stdout.write "// #{anArg}\n"
203
+ end
204
+
205
+ jsmin
@@ -0,0 +1,51 @@
1
+ module Mack
2
+ module ViewHelpers
3
+ module LinkHelpers # :nodoc:
4
+ alias_method :old_javascripts, :javascript
5
+ alias_method :old_stylesheets, :stylesheet
6
+
7
+ def javascript(files, options = {})
8
+ process_file(files, 'javascripts', options)
9
+ end
10
+
11
+ def stylesheet(files, options = {})
12
+ process_file(files, 'stylesheets', options)
13
+ end
14
+
15
+ private
16
+ def process_file(files, asset_type, options)
17
+ files = [files].flatten
18
+ data_arr = []
19
+ contents(files, asset_type).each do |file|
20
+ data_arr << self.send("old_#{asset_type}", file, options)
21
+ end
22
+ return data_arr.join("\n")
23
+ end
24
+
25
+ def contents(files, asset_type)
26
+ return [files].flatten if !Mack::Assets::PackageCollection.instance.merge?
27
+ files = files.collect {|s| s.to_s}
28
+ processed_files = []
29
+
30
+ # Ask the package_collection to compress the bundles.
31
+ # the package_collection will only perform the compression once
32
+ Mack::Assets::PackageCollection.instance.compress_bundles
33
+
34
+ # find all bundled files listed in the file_list
35
+ groups = assets_mgr.groups_by_asset_type(asset_type)
36
+ groups.each do |group|
37
+ if files.include?(group.to_s)
38
+ processed_files << "#{group.to_s}.#{Mack::Assets::PackageCollection.instance.extension(asset_type)}"
39
+ files.delete(group.to_s)
40
+ end
41
+ end
42
+
43
+ # now for all files that's not part of the bundle
44
+ # just add them to the list
45
+ processed_files += files
46
+ return processed_files
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,95 @@
1
+ module Mack
2
+ module Assets
3
+ class PackageCollection # :nodoc:
4
+ include Singleton
5
+
6
+ attr_accessor :bundle_processed
7
+
8
+ def merge?
9
+ return configatron.mack.assets.enable_bundle_merge
10
+ end
11
+
12
+ def initialized
13
+ self.bundle_processed = false
14
+ end
15
+
16
+ def destroy_compressed_bundles
17
+ assets_mgr.groups_by_asset_type(:javascripts).each do |group|
18
+ path = Mack::Paths.javascripts(group + ".js")
19
+ if File.exists?(path)
20
+ FileUtils.rm(path)
21
+ end
22
+ end
23
+ assets_mgr.groups_by_asset_type(:stylesheets).each do |group|
24
+ path = Mack::Paths.stylesheets(group + ".css")
25
+ if File.exists?(path)
26
+ FileUtils.rm(path)
27
+ end
28
+ end
29
+ end
30
+
31
+ def compress_bundles
32
+ if !self.bundle_processed and merge?
33
+ self.bundle_processed = true
34
+ assets_mgr.groups_by_asset_type(:javascripts).each do |group|
35
+ compress(group, :javascripts)
36
+ end
37
+
38
+ assets_mgr.groups_by_asset_type(:stylesheets).each do |group|
39
+ compress(group, :stylesheets)
40
+ end
41
+ end
42
+ end
43
+
44
+ def extension(asset_type)
45
+ return "js" if asset_type.to_s == "javascripts"
46
+ return "css" if asset_type.to_s == "stylesheets"
47
+ return ""
48
+ end
49
+
50
+ private
51
+ def compress(group, asset_type)
52
+ base_dir = Mack::Paths.public(asset_type)
53
+ file_name = "#{group}.#{extension(asset_type)}"
54
+ file_path = File.join(base_dir, file_name)
55
+
56
+ # check if we need to delete the existing file.
57
+ delete_file(file_path, true)
58
+
59
+ # if the file still exists, that means it's not time to expire the previously compressed doc
60
+ # just return the file_name
61
+ return file_name if File.exists?(file_path)
62
+
63
+ # now read data from all the files defined in the bundle
64
+ raw = ""
65
+ assets_mgr.send(asset_type, group).each do |file|
66
+ path = File.join(base_dir, file)
67
+ raw += File.read(path)
68
+ end
69
+
70
+ # save it to some tmp file, and compress it
71
+ jsmin_path = File.join(File.dirname(__FILE__), "jsmin.rb")
72
+ tmp_file = File.join(base_dir, "#{asset_type}_#{rand(10000)}")
73
+ File.open(tmp_file, "w") { |f| f.write(raw) }
74
+ if asset_type.to_s == "javascripts"
75
+ `ruby #{jsmin_path} < #{tmp_file} > #{file_path} \n`
76
+ elsif asset_type.to_s == "stylesheets"
77
+ min_data = CSSMin.new(raw).minimize
78
+ File.open(file_path, "w") { |f| f.write(min_data) }
79
+ end
80
+ # now that we're done, let's delete the tmp file
81
+ delete_file(tmp_file)
82
+
83
+ return file_name
84
+ end
85
+
86
+ def delete_file(name, check_time = false)
87
+ begin
88
+ FileUtils.rm(name) if !check_time or (check_time and File.ctime(name) < ASSET_LOAD_TIME)
89
+ rescue Exception => ex
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,13 @@
1
+ namespace :assets do
2
+ namespace :packager do
3
+ desc "Rebuild asset bundles"
4
+ task :rebuild => :environment do
5
+ Mack::Assets::PackageCollection.instance.compress_bundles
6
+ end
7
+
8
+ desc "Destroy asset bundles"
9
+ task :destroy => :environment do
10
+ Mack::Assets::PackageCollection.instance.destroy_compressed_bundles
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.join(File.dirname(__FILE__), "mack-asset_packager", "tasks", "*.rake")).each do |f|
2
+ load(f)
3
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mack-asset_packager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ platform: ruby
6
+ authors:
7
+ - Darsono Sutedja
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-06 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Asset Packager for Mack Framework
17
+ email: darsono.sutedja@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/mack-asset_packager/cssmin.rb
26
+ - lib/mack-asset_packager/jsmin.rb
27
+ - lib/mack-asset_packager/link_helpers.rb
28
+ - lib/mack-asset_packager/package_collection.rb
29
+ - lib/mack-asset_packager/tasks/asset_packager.rake
30
+ - lib/mack-asset_packager.rb
31
+ - lib/mack-asset_packager_tasks.rb
32
+ - README
33
+ has_rdoc: true
34
+ homepage: http://www.mackframework.com
35
+ post_install_message:
36
+ rdoc_options: []
37
+
38
+ require_paths:
39
+ - lib
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project: magrathea
56
+ rubygems_version: 1.3.0
57
+ signing_key:
58
+ specification_version: 2
59
+ summary: Asset Packager
60
+ test_files: []
61
+