sinatra-support 1.1.3 → 1.2.0

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.
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