dynamic_assets 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. data/README.rdoc +11 -31
  2. data/app/helpers/dynamic_assets_helpers.rb +3 -1
  3. data/lib/dynamic_assets/reference.rb +45 -15
  4. data/lib/dynamic_assets/reference/stylesheet_reference.rb +3 -1
  5. metadata +6 -53
  6. data/spec/dummy_rails_app/app/controllers/application_controller.rb +0 -3
  7. data/spec/dummy_rails_app/app/helpers/application_helper.rb +0 -2
  8. data/spec/dummy_rails_app/config/application.rb +0 -42
  9. data/spec/dummy_rails_app/config/boot.rb +0 -6
  10. data/spec/dummy_rails_app/config/environment.rb +0 -5
  11. data/spec/dummy_rails_app/config/environments/development.rb +0 -26
  12. data/spec/dummy_rails_app/config/environments/production.rb +0 -49
  13. data/spec/dummy_rails_app/config/environments/test.rb +0 -35
  14. data/spec/dummy_rails_app/config/initializers/backtrace_silencers.rb +0 -7
  15. data/spec/dummy_rails_app/config/initializers/inflections.rb +0 -10
  16. data/spec/dummy_rails_app/config/initializers/mime_types.rb +0 -5
  17. data/spec/dummy_rails_app/config/initializers/secret_token.rb +0 -7
  18. data/spec/dummy_rails_app/config/initializers/session_store.rb +0 -8
  19. data/spec/dummy_rails_app/config/routes.rb +0 -58
  20. data/spec/dummy_rails_app/db/seeds.rb +0 -7
  21. data/spec/dummy_rails_app/spec/spec_helper.rb +0 -27
  22. data/spec/dummy_rails_app/test/performance/browsing_test.rb +0 -9
  23. data/spec/dummy_rails_app/test/test_helper.rb +0 -13
  24. data/spec/helpers/dynamic_assets_helpers_spec.rb +0 -164
  25. data/spec/lib/dynamic_assets/config_spec.rb +0 -148
  26. data/spec/lib/dynamic_assets/manager_spec.rb +0 -9
  27. data/spec/lib/dynamic_assets/stylesheet_reference_spec.rb +0 -139
  28. data/spec/spec_helper.rb +0 -24
  29. data/spec/support/matchers/string_matchers.rb +0 -61
data/README.rdoc CHANGED
@@ -7,21 +7,19 @@ Out of the box it can (optionally):
7
7
  * Combine all CSS files into one for faster downloading.
8
8
  * Combine all JavaScript files into one.
9
9
  * Minify assets to make them smaller.
10
- * Run your CSS or JS assets through ERB, like views.
10
+ * Run your CSS or JS assets through ERB so they can reference helpers, like views.
11
11
  * Run your CSS assets through a {Sass}[http://sass-lang.com/] pre-processor (sass or scss).
12
- * Run them through ERB then Sass, what the heck. (Actually, this can be useful, like to allow
13
- your app to set some Sass variables.)
12
+ * Run them through ERB then Sass, which can be useful to allow your app to set some Sass variables.
14
13
  * Combine, minify, and pre-process in memory instead of on disk, to accommodate read-only
15
14
  filesystems (e.g. Heroku).
16
15
  * Set Cache-Control and Expires headers for far-future expiration,
17
- allowing browsers and front-end caches like Varnish to hold assets for a long time.
18
- * Allow assets to be grouped, much like
16
+ allowing browsers and front-end caches like Varnish or {Rack::Cache}[http://rtomayko.github.com/rack-cache/]
17
+ to hold assets for a long time.
18
+ * Group assets, much like
19
19
  {Scott Becker's venerable asset_packager}[http://synthesis.sbecker.net/pages/asset_packager].
20
20
  (Example: You may want a set of stylesheets for your main interface, and another set for your admin
21
21
  interface, maybe with some overlap. With DynamicAssets, your normal users won't pay the
22
22
  penalty of downloading your admin styles.)
23
- * Allow CSS assets to refer to static images through relative URLs. That is, it doesn't break URLs
24
- embedded in CSS.
25
23
  * Invalidate caches and CDNs by inserting a SHA1 signature into the asset URL path instead of using the
26
24
  Rails scheme of appending a URL timestamp. Some asset servers (notably Amazon CloudFront) will
27
25
  drop parameters from the URL, so cache-busting requires path-changing, and since assets are often
@@ -40,6 +38,11 @@ dynamically is useful enough that multiple people have thought of implementing i
40
38
 
41
39
  gem "dynamic_assets"
42
40
 
41
+ And if you're planning to use Sass or SCSS, add this, too:
42
+
43
+ gem "haml", "~> 3.0"
44
+
45
+
43
46
  2. Put your CSS files in <tt>app/assets/stylesheets</tt> and your JS files in
44
47
  <tt>app/assets/javascripts</tt>. Each filename's extension triggers an
45
48
  optional pre-processor:
@@ -124,34 +127,11 @@ dynamically is useful enough that multiple people have thought of implementing i
124
127
  <link href="/assets/stylesheets/1302901403/sidebar.css" media="screen" rel="stylesheet" type="text/css" />
125
128
 
126
129
 
127
- == Variables for ERB
128
-
129
- By default, assets are served by a small controller whose routes are added to
130
- your app automatically when the gem is loaded, but you can easily create your
131
- own controller if you prefer. One reason to do this would be to inject variables
132
- into an asset via ERB, like this:
133
-
134
- class AssetsController < ApplicationController
135
- include DynamicAssets::Controller
136
-
137
- def show_stylesheet
138
- @background_color = '#FFE'
139
- render_asset :stylesheets, params[:name], "text/css"
140
- end
141
- end
142
-
143
- Now in app/assets/stylesheets/application.css.erb you could do this:
144
-
145
- body {
146
- background-color: <%= @background_color %>;
147
- }
148
-
149
-
150
130
  == Static Image URLs Embedded in CSS
151
131
 
152
132
  Suppose you install a JavaScript plugin that comes with a stylesheet and
153
133
  some images. The stylesheet, thing.css, may reference one of its images
154
- like this:
134
+ with a relative URL, like this:
155
135
 
156
136
  div.thing {
157
137
  background: url(fancy_background.png);
@@ -31,7 +31,9 @@ protected
31
31
  def asset_path(asset_ref)
32
32
  path_args = []
33
33
  path_args << asset_ref.name
34
- path_args << { :signature => asset_ref.signature } if asset_ref.signature.present?
34
+
35
+ signature = asset_ref.signature binding
36
+ path_args << { :signature => signature } if signature.present?
35
37
 
36
38
  case asset_ref
37
39
  when DynamicAssets::StylesheetReference then stylesheet_asset_path *path_args
@@ -50,23 +50,22 @@ module DynamicAssets
50
50
  @member_root = File.find_existing(possible_roots) || possible_roots.first
51
51
  end
52
52
 
53
- # Optionally pass context from which ERB can pull instance variables.
54
- def content(context = nil)
53
+ def content(context, for_signature = false)
55
54
  @context = context
56
- s = combine_content
57
- s = minify s if DynamicAssets::Manager.minify?
55
+ s = combine_content for_signature
56
+ s = minify s if DynamicAssets::Manager.minify? && !for_signature
58
57
  s
59
58
  end
60
59
 
61
- def signature
62
- # Note that the signature is based on the context-free
63
- # content. The context must depend on external factors
64
- # in the route, like the domain name, since the signature
65
- # will not change when the context changes. To force a
66
- # change in signature, set or update the ASSET_VERSION
60
+ def signature(context)
61
+ # Note that the signature is based on the context at the time the
62
+ # asset helper is called, which is different from the context at
63
+ # the time of asset rendering.
64
+ #
65
+ # To force a change in signature, set or update the ASSET_VERSION
67
66
  # config variable.
68
67
 
69
- @signature ||= ((ENV['ASSET_VERSION'] || "") + Digest::SHA1.hexdigest(content))
68
+ (ENV['ASSET_VERSION'] || "") + Digest::SHA1.hexdigest(content(context, true))
70
69
  end
71
70
 
72
71
  def minify(content_string)
@@ -104,16 +103,26 @@ module DynamicAssets
104
103
  File.extname(path) == ".erb"
105
104
  end
106
105
 
107
- def combine_content
106
+ def combine_content(for_signature)
108
107
  member_names.map do |member_name|
109
- read_member member_name
108
+ read_member member_name, for_signature
110
109
  end.join "\n"
111
110
  end
112
111
 
113
- def read_member(member_name)
112
+ def read_member(member_name, for_signature)
114
113
  path = path_for_member_name member_name
115
114
  content_string = get_raw_content path
116
- content_string = ERB.new(content_string).result(@context) if @context && path_is_erb?(path)
115
+
116
+ if path_is_erb?(path)
117
+ raise "ERB requires a context" unless @context
118
+ begin
119
+ content_string = ERB.new(content_string).result @context
120
+ rescue StandardError => e
121
+ raise e.exception(parse_erb_error(e, path, content_string) ||
122
+ "Error in ERB #{path}, unknown line number: #{e}")
123
+ end
124
+ end
125
+
117
126
  content_string
118
127
  end
119
128
 
@@ -125,5 +134,26 @@ module DynamicAssets
125
134
  File.open(path, "r") { |f| f.read }
126
135
  end
127
136
 
137
+ def parse_erb_error(error, path, content_string)
138
+ # Exception parsing inspired by HelpfulERB
139
+
140
+ return nil unless error.backtrace.first =~ /^[^:]+:(\d+):in /
141
+
142
+ line_number = $1.to_i
143
+ lines = content_string.split /\n/
144
+
145
+ min = [line_number - 5, 0].max
146
+ max = [line_number + 1, lines.length].min
147
+
148
+ width = max.to_s.size
149
+
150
+ message = "Error in ERB '#{path}' at line #{line_number}:\n\n" +
151
+ (min..max).map do |i|
152
+ n = i + 1
153
+ marker = n == line_number ? "*" : ""
154
+ "%2s %#{width}i %s" % [marker, n, lines[i]]
155
+ end.join("\n") +
156
+ "\n\n#{error.class}: #{error.message}"
157
+ end
128
158
  end
129
159
  end
@@ -28,8 +28,10 @@ module DynamicAssets
28
28
  protected
29
29
 
30
30
  # Overridden to transform URLs embedded in the CSS
31
- def read_member(member_name)
31
+ def read_member(member_name, for_signature)
32
32
  content_string = super
33
+ return content_string if for_signature
34
+
33
35
  format = format_for_member_name member_name
34
36
 
35
37
  content_string = case format
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: dynamic_assets
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.5.2
5
+ version: 0.6.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Robert Davis
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-25 00:00:00 -05:00
13
+ date: 2011-06-05 00:00:00 -05:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -53,7 +53,7 @@ dependencies:
53
53
  requirements:
54
54
  - - ~>
55
55
  - !ruby/object:Gem::Version
56
- version: 1.5.2
56
+ version: 1.6.2
57
57
  type: :development
58
58
  prerelease: false
59
59
  version_requirements: *id004
@@ -113,30 +113,6 @@ files:
113
113
  - lib/dynamic_assets/reference/javascript_reference.rb
114
114
  - lib/dynamic_assets/reference/stylesheet_reference.rb
115
115
  - README.rdoc
116
- - spec/dummy_rails_app/app/controllers/application_controller.rb
117
- - spec/dummy_rails_app/app/helpers/application_helper.rb
118
- - spec/dummy_rails_app/config/application.rb
119
- - spec/dummy_rails_app/config/boot.rb
120
- - spec/dummy_rails_app/config/environment.rb
121
- - spec/dummy_rails_app/config/environments/development.rb
122
- - spec/dummy_rails_app/config/environments/production.rb
123
- - spec/dummy_rails_app/config/environments/test.rb
124
- - spec/dummy_rails_app/config/initializers/backtrace_silencers.rb
125
- - spec/dummy_rails_app/config/initializers/inflections.rb
126
- - spec/dummy_rails_app/config/initializers/mime_types.rb
127
- - spec/dummy_rails_app/config/initializers/secret_token.rb
128
- - spec/dummy_rails_app/config/initializers/session_store.rb
129
- - spec/dummy_rails_app/config/routes.rb
130
- - spec/dummy_rails_app/db/seeds.rb
131
- - spec/dummy_rails_app/spec/spec_helper.rb
132
- - spec/dummy_rails_app/test/performance/browsing_test.rb
133
- - spec/dummy_rails_app/test/test_helper.rb
134
- - spec/helpers/dynamic_assets_helpers_spec.rb
135
- - spec/lib/dynamic_assets/config_spec.rb
136
- - spec/lib/dynamic_assets/manager_spec.rb
137
- - spec/lib/dynamic_assets/stylesheet_reference_spec.rb
138
- - spec/spec_helper.rb
139
- - spec/support/matchers/string_matchers.rb
140
116
  has_rdoc: true
141
117
  homepage: http://github.com/davisre/dynamic_assets
142
118
  licenses:
@@ -151,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
151
127
  requirements:
152
128
  - - ">="
153
129
  - !ruby/object:Gem::Version
154
- hash: -3222917560639699619
130
+ hash: 1850481041292794772
155
131
  segments:
156
132
  - 0
157
133
  version: "0"
@@ -168,28 +144,5 @@ rubygems_version: 1.6.2
168
144
  signing_key:
169
145
  specification_version: 3
170
146
  summary: Allow your Rails 3 app to package and process your CSS and JS assets on the fly.
171
- test_files:
172
- - spec/dummy_rails_app/app/controllers/application_controller.rb
173
- - spec/dummy_rails_app/app/helpers/application_helper.rb
174
- - spec/dummy_rails_app/config/application.rb
175
- - spec/dummy_rails_app/config/boot.rb
176
- - spec/dummy_rails_app/config/environment.rb
177
- - spec/dummy_rails_app/config/environments/development.rb
178
- - spec/dummy_rails_app/config/environments/production.rb
179
- - spec/dummy_rails_app/config/environments/test.rb
180
- - spec/dummy_rails_app/config/initializers/backtrace_silencers.rb
181
- - spec/dummy_rails_app/config/initializers/inflections.rb
182
- - spec/dummy_rails_app/config/initializers/mime_types.rb
183
- - spec/dummy_rails_app/config/initializers/secret_token.rb
184
- - spec/dummy_rails_app/config/initializers/session_store.rb
185
- - spec/dummy_rails_app/config/routes.rb
186
- - spec/dummy_rails_app/db/seeds.rb
187
- - spec/dummy_rails_app/spec/spec_helper.rb
188
- - spec/dummy_rails_app/test/performance/browsing_test.rb
189
- - spec/dummy_rails_app/test/test_helper.rb
190
- - spec/helpers/dynamic_assets_helpers_spec.rb
191
- - spec/lib/dynamic_assets/config_spec.rb
192
- - spec/lib/dynamic_assets/manager_spec.rb
193
- - spec/lib/dynamic_assets/stylesheet_reference_spec.rb
194
- - spec/spec_helper.rb
195
- - spec/support/matchers/string_matchers.rb
147
+ test_files: []
148
+
@@ -1,3 +0,0 @@
1
- class ApplicationController < ActionController::Base
2
- protect_from_forgery
3
- end
@@ -1,2 +0,0 @@
1
- module ApplicationHelper
2
- end
@@ -1,42 +0,0 @@
1
- require File.expand_path('../boot', __FILE__)
2
-
3
- require 'rails/all'
4
-
5
- # If you have a Gemfile, require the gems listed there, including any gems
6
- # you've limited to :test, :development, or :production.
7
- Bundler.require(:default, Rails.env) if defined?(Bundler)
8
-
9
- module DummyRailsApp
10
- class Application < Rails::Application
11
- # Settings in config/environments/* take precedence over those specified here.
12
- # Application configuration should go into files in config/initializers
13
- # -- all .rb files in that directory are automatically loaded.
14
-
15
- # Custom directories with classes and modules you want to be autoloadable.
16
- # config.autoload_paths += %W(#{config.root}/extras)
17
-
18
- # Only load the plugins named here, in the order given (default is alphabetical).
19
- # :all can be used as a placeholder for all plugins not explicitly named.
20
- # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
21
-
22
- # Activate observers that should always be running.
23
- # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
24
-
25
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
26
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
27
- # config.time_zone = 'Central Time (US & Canada)'
28
-
29
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
30
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
31
- # config.i18n.default_locale = :de
32
-
33
- # JavaScript files you want as :defaults (application.js is always included).
34
- # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
35
-
36
- # Configure the default encoding used in templates for Ruby 1.9.
37
- config.encoding = "utf-8"
38
-
39
- # Configure sensitive parameters which will be filtered from the log file.
40
- config.filter_parameters += [:password]
41
- end
42
- end
@@ -1,6 +0,0 @@
1
- require 'rubygems'
2
-
3
- # Set up gems listed in the Gemfile.
4
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5
-
6
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
@@ -1,5 +0,0 @@
1
- # Load the rails application
2
- require File.expand_path('../application', __FILE__)
3
-
4
- # Initialize the rails application
5
- DummyRailsApp::Application.initialize!
@@ -1,26 +0,0 @@
1
- DummyRailsApp::Application.configure do
2
- # Settings specified here will take precedence over those in config/application.rb
3
-
4
- # In the development environment your application's code is reloaded on
5
- # every request. This slows down response time but is perfect for development
6
- # since you don't have to restart the webserver when you make code changes.
7
- config.cache_classes = false
8
-
9
- # Log error messages when you accidentally call methods on nil.
10
- config.whiny_nils = true
11
-
12
- # Show full error reports and disable caching
13
- config.consider_all_requests_local = true
14
- config.action_view.debug_rjs = true
15
- config.action_controller.perform_caching = false
16
-
17
- # Don't care if the mailer can't send
18
- config.action_mailer.raise_delivery_errors = false
19
-
20
- # Print deprecation notices to the Rails logger
21
- config.active_support.deprecation = :log
22
-
23
- # Only use best-standards-support built into browsers
24
- config.action_dispatch.best_standards_support = :builtin
25
- end
26
-
@@ -1,49 +0,0 @@
1
- DummyRailsApp::Application.configure do
2
- # Settings specified here will take precedence over those in config/application.rb
3
-
4
- # The production environment is meant for finished, "live" apps.
5
- # Code is not reloaded between requests
6
- config.cache_classes = true
7
-
8
- # Full error reports are disabled and caching is turned on
9
- config.consider_all_requests_local = false
10
- config.action_controller.perform_caching = true
11
-
12
- # Specifies the header that your server uses for sending files
13
- config.action_dispatch.x_sendfile_header = "X-Sendfile"
14
-
15
- # For nginx:
16
- # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
17
-
18
- # If you have no front-end server that supports something like X-Sendfile,
19
- # just comment this out and Rails will serve the files
20
-
21
- # See everything in the log (default is :info)
22
- # config.log_level = :debug
23
-
24
- # Use a different logger for distributed setups
25
- # config.logger = SyslogLogger.new
26
-
27
- # Use a different cache store in production
28
- # config.cache_store = :mem_cache_store
29
-
30
- # Disable Rails's static asset server
31
- # In production, Apache or nginx will already do this
32
- config.serve_static_assets = false
33
-
34
- # Enable serving of images, stylesheets, and javascripts from an asset server
35
- # config.action_controller.asset_host = "http://assets.example.com"
36
-
37
- # Disable delivery errors, bad email addresses will be ignored
38
- # config.action_mailer.raise_delivery_errors = false
39
-
40
- # Enable threaded mode
41
- # config.threadsafe!
42
-
43
- # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
44
- # the I18n.default_locale when a translation can not be found)
45
- config.i18n.fallbacks = true
46
-
47
- # Send deprecation notices to registered listeners
48
- config.active_support.deprecation = :notify
49
- end
@@ -1,35 +0,0 @@
1
- DummyRailsApp::Application.configure do
2
- # Settings specified here will take precedence over those in config/application.rb
3
-
4
- # The test environment is used exclusively to run your application's
5
- # test suite. You never need to work with it otherwise. Remember that
6
- # your test database is "scratch space" for the test suite and is wiped
7
- # and recreated between test runs. Don't rely on the data there!
8
- config.cache_classes = true
9
-
10
- # Log error messages when you accidentally call methods on nil.
11
- config.whiny_nils = true
12
-
13
- # Show full error reports and disable caching
14
- config.consider_all_requests_local = true
15
- config.action_controller.perform_caching = false
16
-
17
- # Raise exceptions instead of rendering exception templates
18
- config.action_dispatch.show_exceptions = false
19
-
20
- # Disable request forgery protection in test environment
21
- config.action_controller.allow_forgery_protection = false
22
-
23
- # Tell Action Mailer not to deliver emails to the real world.
24
- # The :test delivery method accumulates sent emails in the
25
- # ActionMailer::Base.deliveries array.
26
- config.action_mailer.delivery_method = :test
27
-
28
- # Use SQL instead of Active Record's schema dumper when creating the test database.
29
- # This is necessary if your schema can't be completely dumped by the schema dumper,
30
- # like if you have constraints or database-specific column types
31
- # config.active_record.schema_format = :sql
32
-
33
- # Print deprecation notices to the stderr
34
- config.active_support.deprecation = :stderr
35
- end
@@ -1,7 +0,0 @@
1
- # Be sure to restart your server when you modify this file.
2
-
3
- # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4
- # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5
-
6
- # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7
- # Rails.backtrace_cleaner.remove_silencers!
@@ -1,10 +0,0 @@
1
- # Be sure to restart your server when you modify this file.
2
-
3
- # Add new inflection rules using the following format
4
- # (all these examples are active by default):
5
- # ActiveSupport::Inflector.inflections do |inflect|
6
- # inflect.plural /^(ox)$/i, '\1en'
7
- # inflect.singular /^(ox)en/i, '\1'
8
- # inflect.irregular 'person', 'people'
9
- # inflect.uncountable %w( fish sheep )
10
- # end
@@ -1,5 +0,0 @@
1
- # Be sure to restart your server when you modify this file.
2
-
3
- # Add new mime types for use in respond_to blocks:
4
- # Mime::Type.register "text/richtext", :rtf
5
- # Mime::Type.register_alias "text/html", :iphone
@@ -1,7 +0,0 @@
1
- # Be sure to restart your server when you modify this file.
2
-
3
- # Your secret key for verifying the integrity of signed cookies.
4
- # If you change this key, all old signed cookies will become invalid!
5
- # Make sure the secret is at least 30 characters and all random,
6
- # no regular words or you'll be exposed to dictionary attacks.
7
- DummyRailsApp::Application.config.secret_token = '9c84a3a25344a4bb6033fb01225d7af245dc5f965cadf01502ca1badd308b46940020af7a9d1986005e4b3d02472356d49e62cc87f4c3320e6fcf3a8dbaa94e0'
@@ -1,8 +0,0 @@
1
- # Be sure to restart your server when you modify this file.
2
-
3
- DummyRailsApp::Application.config.session_store :cookie_store, :key => '_dummy_rails_app_session'
4
-
5
- # Use the database for sessions instead of the cookie-based default,
6
- # which shouldn't be used to store highly confidential information
7
- # (create the session table with "rails generate session_migration")
8
- # DummyRailsApp::Application.config.session_store :active_record_store
@@ -1,58 +0,0 @@
1
- DummyRailsApp::Application.routes.draw do
2
- # The priority is based upon order of creation:
3
- # first created -> highest priority.
4
-
5
- # Sample of regular route:
6
- # match 'products/:id' => 'catalog#view'
7
- # Keep in mind you can assign values other than :controller and :action
8
-
9
- # Sample of named route:
10
- # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
11
- # This route can be invoked with purchase_url(:id => product.id)
12
-
13
- # Sample resource route (maps HTTP verbs to controller actions automatically):
14
- # resources :products
15
-
16
- # Sample resource route with options:
17
- # resources :products do
18
- # member do
19
- # get 'short'
20
- # post 'toggle'
21
- # end
22
- #
23
- # collection do
24
- # get 'sold'
25
- # end
26
- # end
27
-
28
- # Sample resource route with sub-resources:
29
- # resources :products do
30
- # resources :comments, :sales
31
- # resource :seller
32
- # end
33
-
34
- # Sample resource route with more complex sub-resources
35
- # resources :products do
36
- # resources :comments
37
- # resources :sales do
38
- # get 'recent', :on => :collection
39
- # end
40
- # end
41
-
42
- # Sample resource route within a namespace:
43
- # namespace :admin do
44
- # # Directs /admin/products/* to Admin::ProductsController
45
- # # (app/controllers/admin/products_controller.rb)
46
- # resources :products
47
- # end
48
-
49
- # You can have the root of your site routed with "root"
50
- # just remember to delete public/index.html.
51
- # root :to => "welcome#index"
52
-
53
- # See how all your routes lay out with "rake routes"
54
-
55
- # This is a legacy wild controller route that's not recommended for RESTful applications.
56
- # Note: This route will make all actions in every controller accessible via GET requests.
57
- # match ':controller(/:action(/:id(.:format)))'
58
- end
@@ -1,7 +0,0 @@
1
- # This file should contain all the record creation needed to seed the database with its default values.
2
- # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
- #
4
- # Examples:
5
- #
6
- # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
7
- # Mayor.create(:name => 'Daley', :city => cities.first)
@@ -1,27 +0,0 @@
1
- # This file is copied to spec/ when you run 'rails generate rspec:install'
2
- ENV["RAILS_ENV"] ||= 'test'
3
- require File.expand_path("../../config/environment", __FILE__)
4
- require 'rspec/rails'
5
-
6
- # Requires supporting ruby files with custom matchers and macros, etc,
7
- # in spec/support/ and its subdirectories.
8
- Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
9
-
10
- RSpec.configure do |config|
11
- # == Mock Framework
12
- #
13
- # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
14
- #
15
- # config.mock_with :mocha
16
- # config.mock_with :flexmock
17
- # config.mock_with :rr
18
- config.mock_with :rspec
19
-
20
- # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
21
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
22
-
23
- # If you're not using ActiveRecord, or you'd prefer not to run each of your
24
- # examples within a transaction, remove the following line or assign false
25
- # instead of true.
26
- config.use_transactional_fixtures = true
27
- end
@@ -1,9 +0,0 @@
1
- require 'test_helper'
2
- require 'rails/performance_test_help'
3
-
4
- # Profiling results for each test method are written to tmp/performance.
5
- class BrowsingTest < ActionDispatch::PerformanceTest
6
- def test_homepage
7
- get '/'
8
- end
9
- end
@@ -1,13 +0,0 @@
1
- ENV["RAILS_ENV"] = "test"
2
- require File.expand_path('../../config/environment', __FILE__)
3
- require 'rails/test_help'
4
-
5
- class ActiveSupport::TestCase
6
- # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
7
- #
8
- # Note: You'll currently still have to declare fixtures explicitly in integration tests
9
- # -- they do not yet inherit this setting
10
- fixtures :all
11
-
12
- # Add more helper methods to be used by all tests here...
13
- end
@@ -1,164 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DynamicAssetsHelpers do
4
-
5
- describe "#stylesheet_asset_tag" do
6
- subject { helper.stylesheet_asset_tag *args }
7
-
8
- context "when called with no arguments" do
9
- let(:args) { [] }
10
-
11
- it "fails with an ArgumentError" do
12
- expect { subject }.to raise_error ArgumentError
13
- end
14
- end
15
-
16
- context "when called with a group_key" do
17
- let(:args) { [group_key] }
18
- let(:group_key) { :base }
19
-
20
- context "when the DynamicAssets::Manager says the given group key is associated with 3 stylesheets" do
21
-
22
- before do
23
- DynamicAssets::Manager.stub(:asset_references_for_group_key).with(:stylesheets, group_key).
24
- and_return [
25
- DynamicAssets::StylesheetReference.new.tap { |r| r.stub(:name => "a", :signature => 123) },
26
- DynamicAssets::StylesheetReference.new.tap { |r| r.stub(:name => "b", :signature => 456) },
27
- DynamicAssets::StylesheetReference.new.tap { |r| r.stub(:name => "c", :signature => 789) }
28
- ]
29
- end
30
-
31
- it "is three link tags" do
32
- subject.scan('<link ').length.should == 3
33
- end
34
-
35
- it 'is three tags with type="text/css"' do
36
- subject.scan('type="text/css"').length.should == 3
37
- end
38
-
39
- it 'is three tags with rel"stylesheet"' do
40
- subject.scan('rel="stylesheet"').length.should == 3
41
- end
42
-
43
- it 'is three tags with media="screen"' do
44
- subject.scan('media="screen"').length.should == 3
45
- end
46
-
47
- context "when the arguments also include HTML attributes" do
48
- before { args << { :media => "print", :id => "foo" } }
49
-
50
- it "is three links, each of which has the given attributes" do
51
- subject.scan('media="print"').length.should == 3
52
- subject.scan('id="foo"').length.should == 3
53
- end
54
- end
55
-
56
- context "when config.asset_host is nil" do
57
- before { helper.config.asset_host.should be_nil }
58
-
59
- it "is three tags with hrefs derived from the asset name and signature" do
60
- should contain_string 'href="/assets/stylesheets/v/123/a.css"'
61
- should contain_string 'href="/assets/stylesheets/v/456/b.css"'
62
- should contain_string 'href="/assets/stylesheets/v/789/c.css"'
63
- end
64
- end
65
-
66
- context "when config.asset_host is set to a.example.com" do
67
- before { helper.config.stub(:asset_host).and_return "http://a.example.com" }
68
-
69
- it "is three tags with hrefs whose host is a.example.com" do
70
- should contain_string 'href="http://a.example.com/assets/stylesheets/v/123/a.css"'
71
- should contain_string 'href="http://a.example.com/assets/stylesheets/v/456/b.css"'
72
- should contain_string 'href="http://a.example.com/assets/stylesheets/v/789/c.css"'
73
- end
74
- end
75
-
76
- context "when config.asset_host is set to a%d.example.com" do
77
- before { helper.config.stub(:asset_host).and_return "http://a%d.example.com" }
78
-
79
- it "is three tags with hrefs whose host is a[0-3].example.com" do
80
- should =~ /href="http:\/\/a[0-3].example.com\/assets\/stylesheets\/v\/123\/a.css"/
81
- should =~ /href="http:\/\/a[0-3].example.com\/assets\/stylesheets\/v\/456\/b.css"/
82
- should =~ /href="http:\/\/a[0-3].example.com\/assets\/stylesheets\/v\/789\/c.css"/
83
- end
84
- end
85
- end
86
- end
87
- end
88
-
89
- describe "#javascript_asset_tag" do
90
- subject { helper.javascript_asset_tag *args }
91
-
92
- context "when called with no arguments" do
93
- let(:args) { [] }
94
-
95
- it "fails with an ArgumentError" do
96
- expect { subject }.to raise_error ArgumentError
97
- end
98
- end
99
-
100
- context "when called with a group_key" do
101
- let(:args) { [group_key] }
102
- let(:group_key) { :base }
103
-
104
- context "when the DynamicAssets::Manager says the given group key is associated with 3 scripts" do
105
-
106
- before do
107
- DynamicAssets::Manager.stub(:asset_references_for_group_key).with(:javascripts, group_key).
108
- and_return [
109
- DynamicAssets::JavascriptReference.new.tap { |r| r.stub(:name => "a", :signature => 123) },
110
- DynamicAssets::JavascriptReference.new.tap { |r| r.stub(:name => "b", :signature => 456) },
111
- DynamicAssets::JavascriptReference.new.tap { |r| r.stub(:name => "c", :signature => 789) }
112
- ]
113
- end
114
-
115
- it "is three script tags" do
116
- subject.scan('<script ').length.should == 3
117
- end
118
-
119
- it 'is three tags with type="text/javascript"' do
120
- subject.scan('type="text/javascript"').length.should == 3
121
- end
122
-
123
- context "when the arguments also include HTML attributes" do
124
- before { args << { :id => "foo" } }
125
-
126
- it "is three links, each of which has the given attributes" do
127
- subject.scan('id="foo"').length.should == 3
128
- end
129
- end
130
-
131
- context "when config.asset_host is nil" do
132
- before { helper.config.asset_host.should be_nil }
133
-
134
- it "is three tags with srcs derived from the asset name and signature" do
135
- should contain_string 'src="/assets/javascripts/v/123/a.js"'
136
- should contain_string 'src="/assets/javascripts/v/456/b.js"'
137
- should contain_string 'src="/assets/javascripts/v/789/c.js"'
138
- end
139
- end
140
-
141
- context "when config.asset_host is set to a.example.com" do
142
- before { helper.config.stub(:asset_host).and_return "http://a.example.com" }
143
-
144
- it "is three tags with srcs whose host is a.example.com" do
145
- should contain_string 'src="http://a.example.com/assets/javascripts/v/123/a.js"'
146
- should contain_string 'src="http://a.example.com/assets/javascripts/v/456/b.js"'
147
- should contain_string 'src="http://a.example.com/assets/javascripts/v/789/c.js"'
148
- end
149
- end
150
-
151
- context "when config.asset_host is set to a%d.example.com" do
152
- before { helper.config.stub(:asset_host).and_return "http://a%d.example.com" }
153
-
154
- it "is three tags with srcs whose host is a[0-3].example.com" do
155
- should =~ /src="http:\/\/a[0-3].example.com\/assets\/javascripts\/v\/123\/a.js"/
156
- should =~ /src="http:\/\/a[0-3].example.com\/assets\/javascripts\/v\/456\/b.js"/
157
- should =~ /src="http:\/\/a[0-3].example.com\/assets\/javascripts\/v\/789\/c.js"/
158
- end
159
- end
160
- end
161
- end
162
- end
163
-
164
- end
@@ -1,148 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DynamicAssets::Config do
4
- subject { Object.new.extend(DynamicAssets::Config).tap { |m| m.init yml_path } }
5
- let(:yml_path) { "/yml/path" }
6
-
7
-
8
- context "when the yml file does not exist" do
9
- before { File.stub(:exists?).with(yml_path).and_return false }
10
-
11
- it "#asset_references_for_group_key works but returns nil" do
12
- subject.asset_references_for_group_key(:stylesheets, :base).should be_nil
13
- end
14
-
15
- it "#asset_reference_for_name works but returns nil" do
16
- subject.asset_reference_for_name(:stylesheets, "sheet1").should be_nil
17
- end
18
- end
19
-
20
-
21
- context "when the yml file exists" do
22
- let(:yml) { {} }
23
- before do
24
- File.stub(:exists?).with(yml_path).and_return true
25
- YAML.stub(:load_file).with(yml_path).and_return yml
26
- end
27
-
28
- context "and contains no config vars" do
29
- its(:combine_asset_groups?) { should be_true }
30
- its(:minify?) { should be_true }
31
- its(:cache?) { should be_true }
32
-
33
- context "and contains one group of two stylesheets" do
34
- before { yml["stylesheets"] = [{ "base" => ["sheet1", "sheet2"] }] }
35
-
36
- it "instantiates one StylesheetReference" do
37
- DynamicAssets::StylesheetReference.should_receive(:new).once.and_return "a Style Ref"
38
- subject
39
- end
40
-
41
- it "#asset_references_for_group_key returns one stylesheet reference for the group" do
42
- subject.asset_references_for_group_key(:stylesheets, :base).length.should == 1
43
- end
44
-
45
- it "#asset_reference_for_name returns one stylesheet reference for the grouped sheet" do
46
- subject.asset_reference_for_name(:stylesheets, "base").should_not be_nil
47
- end
48
-
49
- it "#asset_reference_for_name returns no stylesheet reference for an individual sheet" do
50
- subject.asset_reference_for_name(:stylesheets, "sheet1").should be_nil
51
- end
52
-
53
- context "and contains one group of three javascripts" do
54
- before { yml["javascripts"] = [{ "base" => ["script1", "script2", "script3"] }] }
55
-
56
- it "instantiates one JavascriptReference" do
57
- DynamicAssets::JavascriptReference.should_receive(:new).once.and_return "a JavaScript Ref"
58
- subject
59
- end
60
-
61
- it "#asset_references_for_group_key returns one javascript reference for the group" do
62
- subject.asset_references_for_group_key(:javascripts, :base).length.should == 1
63
- end
64
-
65
- it "#asset_reference_for_name returns one javascript reference for the grouped script" do
66
- subject.asset_reference_for_name(:javascripts, "base").should_not be_nil
67
- end
68
-
69
- it "#asset_reference_for_name returns no javascript reference for an individual script" do
70
- subject.asset_reference_for_name(:javascripts, "script2").should be_nil
71
- end
72
- end
73
-
74
- end
75
-
76
- end
77
-
78
- context "when the yml file contains config vars that set minify to false" do
79
- let(:yml) { { "config" => { Rails.env => { "minify" => false } } } }
80
-
81
- its(:combine_asset_groups?) { should be_true }
82
- its(:minify?) { should be_false }
83
- its(:cache?) { should be_true }
84
- end
85
-
86
- context "when the yml file contains config vars that set cache to false" do
87
- let(:yml) { { "config" => { Rails.env => { "cache" => false } } } }
88
-
89
- its(:combine_asset_groups?) { should be_true }
90
- its(:minify?) { should be_true }
91
- its(:cache?) { should be_false }
92
- end
93
-
94
- context "when the yml file contains config vars that set combine_asset_groups to false" do
95
- let(:yml) { { "config" => { Rails.env => { "combine_asset_groups" => false } } } }
96
-
97
- its(:combine_asset_groups?) { should be_false }
98
- its(:minify?) { should be_true }
99
- its(:cache?) { should be_true }
100
-
101
- context "and contains one group of two stylesheets" do
102
- before { yml["stylesheets"] = [{ "base" => ["sheet1", "sheet2"] }] }
103
-
104
- it "instantiates two StylesheetReferences" do
105
- DynamicAssets::StylesheetReference.should_receive(:new).twice.and_return "a Style Ref"
106
- subject
107
- end
108
-
109
- it "#asset_references_for_group_key returns two stylesheet references for the group" do
110
- subject.asset_references_for_group_key(:stylesheets, :base).length.should == 2
111
- end
112
-
113
- it "#asset_reference_for_name returns no stylesheet reference for the grouped sheet" do
114
- subject.asset_reference_for_name(:stylesheets, "base").should be_nil
115
- end
116
-
117
- it "#asset_reference_for_name returns a stylesheet reference for an individual sheet" do
118
- subject.asset_reference_for_name(:stylesheets, "sheet1").should_not be_nil
119
- end
120
-
121
- context "and contains one group of three javascripts" do
122
- before { yml["javascripts"] = [{ "base" => ["script1", "script2", "script3"] }] }
123
-
124
- it "instantiates three JavascriptReferences" do
125
- DynamicAssets::JavascriptReference.should_receive(:new).exactly(3).times.
126
- and_return "a JavaScript Ref"
127
- subject
128
- end
129
-
130
- it "#asset_references_for_group_key returns three javascript references for the group" do
131
- subject.asset_references_for_group_key(:javascripts, :base).length.should == 3
132
- end
133
-
134
- it "#asset_reference_for_name returns no javascript reference for the grouped script" do
135
- subject.asset_reference_for_name(:javascripts, "base").should be_nil
136
- end
137
-
138
- it "#asset_reference_for_name returns a javascript reference for an individual script" do
139
- subject.asset_reference_for_name(:javascripts, "script2").should_not be_nil
140
- end
141
- end
142
-
143
- end
144
- end
145
-
146
- end
147
-
148
- end
@@ -1,9 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DynamicAssets::Manager do
4
-
5
- it "contains config methods, like a singleton" do
6
- expect { DynamicAssets::Manager.combine_asset_groups? }.not_to raise_error
7
- end
8
-
9
- end
@@ -1,139 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module DynamicAssets
4
- describe StylesheetReference do
5
-
6
- describe "#content" do
7
- subject { reference.content }
8
- let(:reference) { StylesheetReference.new :name => "thing" }
9
-
10
- context "when a css file with the given reference name exists" do
11
- before do
12
- reference.stub(:path_for_member_name).with("thing").and_return "/foo/thing.css"
13
- reference.stub(:raw_content_exists?).with("/foo/thing.css").and_return true
14
- reference.stub(:get_raw_content).with("/foo/thing.css").and_return raw_content
15
- end
16
-
17
- context "and the file is blank" do
18
- let(:raw_content) { "" }
19
- it { should be_blank }
20
- end
21
-
22
- context "and the file contains styles" do
23
- let(:raw_content) { "div.foo { color: #FFF }" }
24
-
25
- context "and the Manager is not configured to minify" do
26
- before { Manager.stub :minify? => false }
27
-
28
- it "is the raw content" do
29
- subject.should == raw_content
30
- end
31
-
32
- it "does not call its own #minify method" do
33
- reference.should_not_receive :minify
34
- subject
35
- end
36
- end
37
-
38
- context "and the Manager is configured to minify" do
39
- before { Manager.stub :minify? => true }
40
-
41
- it "is the result of calling its own #minify method" do
42
- reference.should_receive(:minify).and_return "tiny output"
43
- subject.should == "tiny output"
44
- end
45
-
46
- end
47
- end
48
-
49
- context "and the file contains URLs relative to the stylesheet" do
50
- let(:raw_content) { "body { background: url(background.gif); }" }
51
-
52
- it "makes the URLs relative to RELATIVE_URL_ROOT and the member name" do
53
- should contain_string "url(#{StylesheetReference::RELATIVE_URL_ROOT}/thing/background.gif)"
54
- end
55
- end
56
-
57
- context "and the file contains URLs within directories relative to the stylesheet" do
58
- let(:raw_content) { "body { background: url(a/b/background.gif); }" }
59
-
60
- it "makes the URLs relative to RELATIVE_URL_ROOT and the member name" do
61
- should contain_string "url(#{StylesheetReference::RELATIVE_URL_ROOT}/thing/a/b/background.gif)"
62
- end
63
- end
64
-
65
- context "and the file contains URLs with dots, relative to the stylesheet" do
66
- let(:raw_content) { "body { background: url(../a/b/background.gif); }" }
67
-
68
- it "makes the URLs relative to RELATIVE_URL_ROOT and the member name, ignoring leading dots" do
69
- should contain_string "url(#{StylesheetReference::RELATIVE_URL_ROOT}/thing/a/b/background.gif)"
70
- end
71
- end
72
-
73
- context "and the file contains full URLs with hosts" do
74
- let(:raw_content) { "body { background: url(http://www.example.com/background.gif); }" }
75
-
76
- it "leaves the URLs unchanged" do
77
- should contain_string "url(http://www.example.com/background.gif)"
78
- end
79
- end
80
- end
81
- end
82
-
83
- describe "#minify" do
84
- subject { reference.minify content }
85
- let(:reference) { StylesheetReference.new :name => "thing" }
86
-
87
- context "when given content that has whitespace and comments" do
88
- let(:content) do %Q!
89
- /*
90
- * Some sample styles
91
- */
92
- div.foo { color: #FFF; } /* foo style */
93
- div.bar, span.baz { font-size: 12px; }
94
- !
95
- end
96
-
97
- it "is the result of calling CSSMin.minify" do
98
- CSSMin.should_receive(:minify).with(content).and_return "tiny content"
99
- subject.should == "tiny content"
100
- end
101
-
102
- # We assume CSSMin is fully tested, but as a sanity check we
103
- # observe the StylesheetReference's behavior with a few examples:
104
-
105
- it "contains no double spaces" do
106
- should_not =~ / /
107
- end
108
-
109
- it "contains no newlines" do
110
- should_not =~ /\n/
111
- end
112
-
113
- it "removes space between selectors" do
114
- should contain_string "div.bar,span.baz"
115
- end
116
-
117
- it "removes space around opening brackets" do
118
- should contain_string "span.baz{font-size"
119
- end
120
-
121
- it "removes space around closing brackets" do
122
- should contain_string "}div.bar"
123
- end
124
-
125
- it "removes space around values" do
126
- should contain_string "font-size:12px;}"
127
- end
128
-
129
- it "removes comments that are on their own lines" do
130
- should_not contain_string "Some sample styles"
131
- end
132
-
133
- it "removes comments that are on the same line as a style" do
134
- should_not contain_string "Some sample styles"
135
- end
136
- end
137
- end
138
- end
139
- end
data/spec/spec_helper.rb DELETED
@@ -1,24 +0,0 @@
1
- ENV["RAILS_ENV"] ||= 'test'
2
-
3
- require 'dummy_rails_app/config/environment'
4
-
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
6
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
- $LOAD_PATH.unshift(File.dirname(__FILE__))
8
-
9
- require 'rspec/rails'
10
- require 'capybara/rspec'
11
-
12
- require 'dynamic_assets'
13
-
14
- # PENDING: not sure why these aren't auto-required
15
- require 'app/helpers/dynamic_assets_helpers'
16
- require 'config/routes'
17
-
18
- # Requires supporting files with custom matchers and macros, etc,
19
- # in ./support/ and its subdirectories.
20
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
21
-
22
- RSpec.configure do |config|
23
-
24
- end
@@ -1,61 +0,0 @@
1
-
2
- #
3
- # Makes sure a string contains a given substring. The difference between these two:
4
- #
5
- # my_string.should =~ /something/ # Using a regex
6
- # my_string.should contain_string "something" # Using this matcher
7
- #
8
- # is that this matcher will escape the string for you, so you can search for .*?%^ASQ
9
- # and not get weird results.
10
- #
11
- RSpec::Matchers.define :contain_string do |expected|
12
-
13
- match do |actual|
14
- raise "Expected value has an unexpected type. It's #{expected.class} but should be String." unless
15
- expected.is_a?(String)
16
- raise "Actual value has an unexpected type. It's #{actual.class} but should be String." unless
17
- actual.is_a?(String)
18
-
19
- actual =~ /#{Regexp.escape(expected)}/
20
- end
21
-
22
- failure_message_for_should do |actual|
23
- "expected that \"#{actual}\" would contain \"#{expected}\""
24
- end
25
-
26
- failure_message_for_should_not do |actual|
27
- "expected that \"#{actual}\" would not contain \"#{expected}\""
28
- end
29
-
30
- description do
31
- "contain \"#{expected}\""
32
- end
33
-
34
- end
35
-
36
-
37
- #
38
- # Example: my_string.should start_with "ABCDE"
39
- #
40
- RSpec::Matchers.define :start_with do |expected|
41
-
42
- match do |actual|
43
- raise "Actual value has an unexpected type. It's #{actual.class} but should be String." unless
44
- actual.is_a?(String)
45
-
46
- actual =~ /^#{Regexp.escape("#{expected}")}/
47
- end
48
-
49
- failure_message_for_should do |actual|
50
- "expected that \"#{actual}\" would start with \"#{expected}\""
51
- end
52
-
53
- failure_message_for_should_not do |actual|
54
- "expected that \"#{actual}\" would not start with \"#{expected}\""
55
- end
56
-
57
- description do
58
- "start with \"#{expected}\""
59
- end
60
-
61
- end