sinatra-support 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md CHANGED
@@ -1,3 +1,9 @@
1
+ v1.2.0 - May 27, 2011
2
+ ---------------------
3
+
4
+ ### Added:
5
+ * `Sinatra::CompressedJS` for serving compressed JS files
6
+
1
7
  v1.1.3 - May 27, 2011
2
8
  ---------------------
3
9
 
@@ -14,6 +14,7 @@ module Sinatra
14
14
  autoload :I18nSupport, File.expand_path('../support/i18nsupport', __FILE__)
15
15
  autoload :MultiRender, File.expand_path('../support/multirender', __FILE__)
16
16
  autoload :CompassSupport, File.expand_path('../support/compasssupport', __FILE__)
17
+ autoload :CompressedJS, File.expand_path('../support/compressedjs', __FILE__)
17
18
 
18
19
  module Support
19
20
  end
@@ -0,0 +1,244 @@
1
+ # Serves compressed JS.
2
+ #
3
+ # == Usage example
4
+ #
5
+ # Assuming you have JavaScript files stored in +./app/js+, and you want to serve
6
+ # the compressed JS in +http://yoursite.com/js/compressed.js+:
7
+ #
8
+ # # CompressedJS is recommended to be used alongside JsSupport to serve
9
+ # # raw JS files in development mode. This will make your /app/js/*.js
10
+ # # accessible via http://yoursite.com/js/*.js.
11
+ #
12
+ # register Sinatra::JsSupport
13
+ # serve_js '/js', from: './app/js'
14
+ #
15
+ # Then load the {CompressedJS} plugin:
16
+ #
17
+ # register Sinatra::CompressedJS
18
+ #
19
+ # serve_compressed_js :app_js, # The name (used later in your views)
20
+ # :prefix => '/js', # Where the individual files can be accessed at
21
+ # :root => './app/js', # The root of your files
22
+ # :path => '/js/compressed.js', # The URL where the compressed JS will served at
23
+ # :files => # List of files
24
+ # Dir['./app/js/vendor/*.js'] +
25
+ # Dir['./app/js/app/**.js']
26
+ #
27
+ # Note that +:prefix+ and +:root+ are the same things passed onto {JsSupport#serve_js serve_js}.
28
+ #
29
+ # In your template view, add this before +</body>+:
30
+ #
31
+ # <%= settings.app_js.to_html %>
32
+ #
33
+ # (The name +app_js+ comes from the first parameter passed to
34
+ # {#serve_compressed_js}.)
35
+ #
36
+ # In development mode, this will probably output:
37
+ #
38
+ # <script src='/js/vendor/jquery.js?1293847189'></script>
39
+ # <script src='/js/app/application.js?1293847189'></script>
40
+ # <!-- This is {:prefix} + {files, stripped of :root} -->
41
+ #
42
+ # But in production mode:
43
+ #
44
+ # <script src='/js/compressed.js?1293847189'></script>
45
+ # <!-- This is {:path} given in serve_compressed_js -->
46
+ #
47
+ # == Caveats
48
+ #
49
+ # You will need the JsMin gem.
50
+ #
51
+ # # Gemfile
52
+ # gem "jsmin", "~> 1.0.1"
53
+ #
54
+ # CompressedJS supports CoffeeScript. If you do use CoffeeScript, be
55
+ # sure to add the approprate gem to your project.
56
+ #
57
+ # # Gemfile
58
+ # gem "coffee-script", require: "coffee_script"
59
+ #
60
+ # == Caching
61
+ #
62
+ # CompressedJS will cache compressed and combined scripts. This means that compression
63
+ # will only happen once in your application's runtime.
64
+ #
65
+ # == Why no support for CSS compression?
66
+ #
67
+ # If you use Sass/SCSS anyway, you can achieve the same thing by changing Sass's
68
+ # :output settings to :compressed. If you also use +@import+ in Sass/SCSS, you can
69
+ # easily consolidate all CSS into one file without any need for a Sinatra plugin.
70
+ #
71
+ # == Extending with another JS compressor
72
+ #
73
+ # This is unsupported, but you may change the JsFiles.compress function to use
74
+ # something else.
75
+ #
76
+ # # Sample custom compression (probably doesn't work)
77
+ # def JsFiles.compress(str)
78
+ # file = Tempfile.new { |f| f.write str }
79
+ # output = Tempfile.new
80
+ #
81
+ # system "closure-compiler #{file.path} #{output.path}"
82
+ # output.read
83
+ # end
84
+ #
85
+ module Sinatra::CompressedJS
86
+ def self.registered(app)
87
+ app.set :jsfiles_cache_max_age, 86400*30 unless app.respond_to?(:jsfile_cache_max_age)
88
+ end
89
+
90
+ def serve_compressed_js(name, options={})
91
+ jsfiles = JsFiles.new(options)
92
+
93
+ set name.to_sym, jsfiles
94
+
95
+ serve_jsfiles options[:path], jsfiles
96
+ end
97
+
98
+ protected
99
+ # Adds a route
100
+ def serve_jsfiles(path, jsfiles)
101
+ get path do
102
+ content_type :js
103
+ last_modified jsfiles.mtime
104
+ etag jsfiles.mtime.to_i
105
+ cache_control :public, :must_revalidate, :max_age => settings.jsfiles_cache_max_age
106
+
107
+ jsfiles.compressed
108
+ end
109
+ end
110
+ end
111
+
112
+ # A list of JavaScript files.
113
+ #
114
+ # This is a general-purpose class usually used for minification
115
+ # of JS assets.
116
+ #
117
+ # This is often used with Sinatra, but can work with any other
118
+ # web framework.
119
+ #
120
+ # === Usage example
121
+ #
122
+ # files = Dir['public/js/jquery.*.js']
123
+ # files += Dir['public/js/app.*.js']
124
+ #
125
+ # js_files = JsFiles.new(:files => files,
126
+ # :prefix => '/javascript',
127
+ # :root => './app/js')
128
+ #
129
+ # js_files.mtime #=> (Time) 2010-09-02 8:00PM
130
+ #
131
+ # You can use #to_html in views:
132
+ #
133
+ # <!-- Shows <script> tags -->
134
+ # <%= js_files.to_html %>
135
+ #
136
+ # Getting the data (for rake tasks perhaps):
137
+ #
138
+ # File.open('public/scripts.js', 'w') do |f|
139
+ # f << js_files.combined
140
+ # end
141
+ #
142
+ # File.open('public/scripts.min.js', 'w') do |f|
143
+ # f << js_files.compressed
144
+ # end
145
+ #
146
+ class JsFiles
147
+ attr_reader :files
148
+ attr_reader :app
149
+ attr_reader :prefix
150
+ attr_reader :root
151
+ attr_reader :path
152
+
153
+ # Creates a JsFiles object based on the list of given files.
154
+ #
155
+ # @param files [Array] A list of string file paths.
156
+ # @example
157
+ #
158
+ # files = Dir['public/js/jquery.*.js']
159
+ # $js_files = JsFiles.new(files)
160
+ #
161
+ def initialize(options={})
162
+ @app = options[:app]
163
+ @files = options[:files]
164
+ @prefix = options[:prefix] || '/js/'
165
+ @path = options[:path] || @prefix + 'app.js'
166
+ @root = options[:root] || '/app/js'
167
+ @root = File.expand_path(@root)
168
+
169
+ raise "Files must be an array" unless @files.is_a?(Array)
170
+ end
171
+
172
+ # @group Metadata methods
173
+
174
+ # Returns the the modified time of the entire package.
175
+ #
176
+ # @return [Time] The last modified time of the most recent file.
177
+ #
178
+ def mtime
179
+ @files.map { |f| File.mtime(f) }.max
180
+ end
181
+
182
+ # Returns a list of the URLs for the package.
183
+ #
184
+ # @example
185
+ #
186
+ # -# This is the same as calling #to_html.
187
+ # - Main.js_files.hrefs.each do |href|
188
+ # %script{:src => href}
189
+ #
190
+ def hrefs
191
+ @files.map { |f|
192
+ path = File.expand_path(f)
193
+ path.gsub! /\.[^\.]*$/, ''
194
+ path.gsub! /^#{@root}/, ''
195
+ File.join @prefix, path + ".js?#{File.mtime(f).to_i}"
196
+ }
197
+ end
198
+
199
+ # Returns the <script> tags for the development version.
200
+ def to_development_html
201
+ hrefs.map { |href| "<script type='text/javascript' src='#{href}'></script>" }.join("\n")
202
+ end
203
+
204
+ # Returns the <script> tag for the production version.
205
+ def to_production_html
206
+ "<script type='text/javascript' src='%s?%s'></script>" % [path, mtime.to_i]
207
+ end
208
+
209
+ # Returns the <script> tags, using development or production as needed.
210
+ def to_html
211
+ production? ? to_production_html : to_development_html
212
+ end
213
+
214
+ # @group Output methods
215
+
216
+ # Returns the combined source of all the files.
217
+ def combined
218
+ @combined ||= @files.map { |file|
219
+ contents = File.open(file) { |f| f.read }
220
+ contents = coffee_compile(contents) if file =~ /\.coffee$/
221
+ contents
222
+ }.join("\n")
223
+ end
224
+
225
+ # Returns a combined, minifed source of all the files.
226
+ def compressed
227
+ require 'jsmin'
228
+ @compressed ||= self.class.compress(combined)
229
+ end
230
+
231
+ def self.compress(str)
232
+ JSMin.minify(str).strip
233
+ end
234
+
235
+ protected
236
+ def coffee_compile(str)
237
+ require 'coffee_script' unless defined?(::CoffeeScript)
238
+ CoffeeScript.compile(str)
239
+ end
240
+
241
+ def production?
242
+ app && app.production?
243
+ end
244
+ end
@@ -1,6 +1,6 @@
1
1
  module Sinatra
2
2
  module Support
3
- VERSION = "1.1.3"
3
+ VERSION = "1.2.0"
4
4
 
5
5
  def self.version
6
6
  VERSION
@@ -0,0 +1 @@
1
+ alert("hello");
@@ -0,0 +1 @@
1
+ alert("hi");
@@ -0,0 +1 @@
1
+ alert 2
data/test/helper.rb CHANGED
@@ -19,11 +19,26 @@ class Test::Unit::TestCase
19
19
  end
20
20
 
21
21
  def self.fixture_path(*a)
22
- root = File.expand_path('../fixtures', __FILE__)
23
- File.join root, a
22
+ fx *a
24
23
  end
25
24
 
26
25
  def fixture_path(*a)
27
- self.class.fixture_path *a
26
+ fx *a
28
27
  end
28
+
29
+ def save_and_open_page
30
+ f = Tempfile.new(['', '.html'])
31
+ path = f.path
32
+ f.close!
33
+ f.unlink
34
+
35
+ File.open(path, 'w') { |f| f.write last_response.body }
36
+
37
+ system "open \"#{path}\""
38
+ end
39
+ end
40
+
41
+ def fx(*a)
42
+ root = File.expand_path('../fixtures', __FILE__)
43
+ File.join root, a
29
44
  end
@@ -0,0 +1,82 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ Encoding.default_external = 'utf-8'
4
+
5
+ class CompressedJSApp < Test::Unit::TestCase
6
+ include Rack::Test::Methods
7
+
8
+ class App < Sinatra::Base
9
+ register Sinatra::CompressedJS
10
+ register Sinatra::JsSupport
11
+
12
+ serve_js '/js', from: fx('compressed_js/js')
13
+
14
+ serve_compressed_js :app_js,
15
+ :path => '/js/app.js',
16
+ :prefix => '/js',
17
+ :root => fx('compressed_js/js'),
18
+ :files =>
19
+ Dir[fx('compressed_js/js/*.{js,coffee}')].sort
20
+
21
+ get('/to_development_html') { settings.app_js.to_development_html }
22
+ get('/to_production_html') { settings.app_js.to_production_html }
23
+ end
24
+
25
+ def app
26
+ App.new
27
+ end
28
+
29
+ COMPRESSED = "alert(\"hello\");alert(\"hi\");(function(){alert(2);}).call(this);"
30
+
31
+ test "JsFiles#files" do
32
+ assert_equal \
33
+ Dir[fx('compressed_js/js/*.{js,coffee}')].sort,
34
+ App.app_js.files
35
+ end
36
+
37
+ test "JsFiles#compressed" do
38
+ assert_equal COMPRESSED, App.app_js.compressed
39
+ end
40
+
41
+ test "JsFiles#mtime" do
42
+ assert App.app_js.mtime.to_i > 0
43
+ end
44
+
45
+ test "go" do
46
+ get '/js/hi.js'
47
+ control = 'alert("hi");'
48
+
49
+ assert_equal control, last_response.body.strip
50
+ end
51
+
52
+ test "coffeescript support" do
53
+ get '/js/yo.js'
54
+ control = 'alert(2);';
55
+
56
+ assert_equal control, last_response.body.strip
57
+ end
58
+
59
+ test "compressed" do
60
+ get '/js/app.js'
61
+ control = COMPRESSED
62
+
63
+ assert_equal control, last_response.body.strip
64
+ end
65
+
66
+ test "#to_development_html" do
67
+ get '/to_development_html'
68
+
69
+ control = /<script type='text\/javascript' src='\/js\/hello\.js\?[0-9]+'>/
70
+ assert_match control, last_response.body
71
+
72
+ control = /<script type='text\/javascript' src='\/js\/hi\.js\?[0-9]+'>/
73
+ assert_match control, last_response.body
74
+ end
75
+
76
+ test "#to_production_html" do
77
+ get '/to_production_html'
78
+
79
+ control = /<script type='text\/javascript' src='\/js\/app\.js\?[0-9]+'>/
80
+ assert_match control, last_response.body
81
+ end
82
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: sinatra-support
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.1.3
5
+ version: 1.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Cyril David
@@ -91,6 +91,28 @@ dependencies:
91
91
  version: "0"
92
92
  type: :development
93
93
  version_requirements: *id007
94
+ - !ruby/object:Gem::Dependency
95
+ name: coffee-script
96
+ prerelease: false
97
+ requirement: &id008 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ~>
101
+ - !ruby/object:Gem::Version
102
+ version: 2.1.1
103
+ type: :development
104
+ version_requirements: *id008
105
+ - !ruby/object:Gem::Dependency
106
+ name: jsmin
107
+ prerelease: false
108
+ requirement: &id009 !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ~>
112
+ - !ruby/object:Gem::Version
113
+ version: 1.0.1
114
+ type: :development
115
+ version_requirements: *id009
94
116
  description: Sinatra-support includes many helpers for forms, errors and many amazing things.
95
117
  email:
96
118
  - cyx.ucron@gmail.com
@@ -104,6 +126,7 @@ extra_rdoc_files: []
104
126
  files:
105
127
  - lib/sinatra/support/compasssupport.rb
106
128
  - lib/sinatra/support/compat-1.8.6.rb
129
+ - lib/sinatra/support/compressedjs.rb
107
130
  - lib/sinatra/support/country.rb
108
131
  - lib/sinatra/support/countryhelpers.rb
109
132
  - lib/sinatra/support/csssupport.rb
@@ -119,6 +142,9 @@ files:
119
142
  - lib/sinatra/support/useragenthelpers.rb
120
143
  - lib/sinatra/support/version.rb
121
144
  - lib/sinatra/support.rb
145
+ - test/fixtures/compressed_js/js/hello.js
146
+ - test/fixtures/compressed_js/js/hi.js
147
+ - test/fixtures/compressed_js/js/yo.coffee
122
148
  - test/fixtures/css/style-less.less
123
149
  - test/fixtures/css/style-sass.sass
124
150
  - test/fixtures/css/style-scss.scss
@@ -131,6 +157,7 @@ files:
131
157
  - test/fixtures/multirender/views_2/home.haml
132
158
  - test/helper.rb
133
159
  - test/test_compass_app.rb
160
+ - test/test_compressed_js_app.rb
134
161
  - test/test_country.rb
135
162
  - test/test_css.rb
136
163
  - test/test_date.rb