Permalink
Browse files

Version 0.5 (exported from SVN)

  • Loading branch information...
1 parent 371c37b commit 22479b94bb194609e44b160ddf57af7ee24efa8b @chrisparrish chrisparrish committed Oct 18, 2008
Showing with 687 additions and 455 deletions.
  1. +50 −11 CHANGELOG → HISTORY
  2. +17 −14 README
  3. +9 −6 app/controllers/admin/text_asset_controller.rb
  4. +0 −54 app/controllers/text_asset_site_controller.rb
  5. +1 −0 app/models/javascript_filter.rb
  6. +1 −0 app/models/stylesheet_filter.rb
  7. +7 −3 app/models/text_asset.rb
  8. +2 −2 app/models/text_asset_context.rb
  9. +20 −0 app/models/text_asset_filter.rb
  10. +1 −1 app/models/text_asset_observer.rb
  11. +3 −1 app/models/text_asset_response_cache.rb
  12. +38 −21 app/views/admin/text_asset/edit.html.haml
  13. +0 −50 custom_settings.rb
  14. +10 −0 db/migrate/001_create_text_assets.rb
  15. +67 −0 db/migrate/003_rename_config_keys_and_add_filter_id.rb
  16. +12 −12 lib/{extended_page_tags.rb → page_tag_mixins.rb}
  17. +70 −0 lib/site_controller_mixins.rb
  18. +18 −24 lib/styles_n_scripts/config.rb
  19. +42 −6 lib/tasks/styles_n_scripts_extension_tasks.rake
  20. +24 −18 spec/controllers/admin/text_asset_controller_spec.rb
  21. +41 −8 spec/controllers/admin/text_asset_controller_upload_spec.rb
  22. +67 −105 spec/controllers/{text_asset_site_controller_spec.rb → site_controller_extensions_spec.rb}
  23. +38 −60 spec/lib/config_spec.rb
  24. +1 −1 spec/models/stylesheet_and_javascript_dependency_spec.rb
  25. +5 −5 spec/models/stylesheet_and_javascript_spec.rb
  26. +3 −9 spec/models/stylesheet_and_javascript_tags_spec.rb
  27. +71 −0 spec/models/stylesheet_filter_and_javascript_filter_spec.rb
  28. +24 −12 spec/models/text_asset_response_cache_spec.rb
  29. +1 −1 spec/models/user_action_observer_spec.rb
  30. +2 −2 spec/scenarios/javascripts_scenario.rb
  31. +2 −2 spec/scenarios/stylesheets_scenario.rb
  32. +40 −27 styles_n_scripts_extension.rb
View
61 CHANGELOG → HISTORY
@@ -1,6 +1,43 @@
-v0.4.1 Bug fix. Last-Modified header was not reporting the date using a valid
- HTTP format. This is now spec'ed and fixed. (Thanks to Jay Levitt for
- identifying this issue).
+v0.5 Lots of improvements here. Some new features some just refactoring.
+
+ Added Shards to the edit view so now this extension can be extended
+ (watch for SnS extension extensions to come soon).
+
+ Added support for filters. The only real one planned is Sass for the
+ CSS files but, the way I did it, it's pretty extensible (based on how
+ Radiant handles page content filters). Did I mention that there were
+ some SnS extension extensions coming soon...
+
+ Changed the way stylesheet and javascript urls were routed and evaluated
+ (now its a before_filter hooked into SiteController's show_page method).
+ This has the added bonus of allowing the stylesheet and javascript
+ directories to be changed during runtime (see next point).
+
+ Configurations are now handled differently. Rather than setting them
+ once at load-time, they are changed dynamically via Rake tasks. The
+ only exception is the caching directory -- you really don't want to do
+ that one dynamcially -- so it's been pulled out of the config and made a
+ constant.
+
+ Caching is more better. No more caching the whole url path -- just the
+ file, thank you. This makes it possible to change the directory name
+ on the fly.
+
+ Bug fixes:
+ * Added a 'url' definition (just "") to the extension to fix an issue
+ with a change in Radiant 0.6.7 behavior (I really shoulda done that
+ all along).
+
+ * Corrected an issue where this extension is included as part of a new
+ Radiant install. The RAKE db:bootstrap task would initialize the
+ extension which would then try to write the default settings to a
+ not-yet-existing config table.
+
+
+v0.4.1 Bug fixes:
+ * Last-Modified header was not reporting the date using a valid
+ HTTP format. This is now spec'ed and fixed. (Thanks to Jay Levitt
+ for identifying this issue).
v0.4 File Uploading! You can now upload Javascripts or Stylesheets from the
@@ -52,17 +89,19 @@ v0.3 Big new features plus code refactoring and a bug fix. First, the new
adding the form helper to the remove.html.erb template.
-v0.2.2 Bugfix - I added the UserActionObserver behavior and accompanying specs
- to the Stylesheet and Javascript models
+v0.2.2 Bug fixes:
+ * I added the UserActionObserver behavior and accompanying specs
+ to the Stylesheet and Javascript models
-v0.2.1 Bugfix - I corrected the ActionController::InvalidAuthenticityToken that
- comes from my not using the form helpers in my templates. I also weakly
- spec-ed it.
+v0.2.1 Bug fixes:
+ * I corrected the ActionController::InvalidAuthenticityToken that
+ comes from my not using the form helpers in my templates. I also
+ weakly spec-ed it.
- NOTE: This was only a partial fix. I corrected the edit.html.erb
- template but not the remove.html.erb one. See v0.3 for the completely
- fixed version.
+ NOTE: This was only a partial fix. I corrected the edit.html.erb
+ template but not the remove.html.erb one. See v0.3 for the
+ completely fixed version.
v0.2 Initial public release. Complete rebuild of the extension to work with
View
31 README
@@ -6,8 +6,7 @@ stored in pages.
-CONTENTS
-========
+== CONTENTS
In this README you'll find:
1. Usage
2. Why Change Things?
@@ -16,8 +15,7 @@ In this README you'll find:
-USAGE
-=====
+== USAGE
Using this extension is rather painless. If you can use the rest of Radiant,
using these additions should feel obvious. There are a couple of things to take
note of, however.
@@ -39,14 +37,19 @@ note of, however.
time -- viola, now Radiant offers asset packaging just like Rails! (And the
caching mechanism is smart enough to keep track of your file's dependencies)
+ * There are 4 settings that can be changed via Rake "config" task (see
+ Rake -T for usage):
+ - stylesheet_directory
+ - javascript_directory
+ - stylesheet_mime_type
+ - javascript_mime_type
That's it. Everything else is either too obvious to bother with here or
automagical and/or too top secret to disclose ;-).
-WHY CHANGE THINGS?
-==================
+== WHY CHANGE THINGS?
As John sees it, the pages tab is for storing your main content. (Think of the
tree view as the list of available destinations for your users. Sure, they need
stylesheets and javascripts, but those are supporting files -- much like images
@@ -79,8 +82,7 @@ There are a number of interesting benefits gained by this approach:
-INSTALLATION
-============
+== INSTALLATION
1. Copy this extension into your existing Radiant project (place it at:
[your project location]/vendor/extensions/styles_n_scripts
@@ -96,17 +98,18 @@ INSTALLATION
4. (Optional) Configure your stylesheet and javascript directories. By default
The Styles 'n Scripts serves your stylesheets and javascripts out of the
/css and /js folders respectively. You can change these locations via the
- custom_settings.rb file if you wish (just make sure you aren't competing
- with Radiant's /public files or your own pages.
+ "config" rake task:
+ rake radiant:extensions:styles_n_scripts:config
5. (Optional) Configure your stylesheet and javascript content (MIME) types.
- Again there are defaults but you can change them via the custom_settings.rb
- file.
+ Again, you use the Rake "config" task.
+6. (Optional) Set a different location for the cache directory. This is set in
+ The styles_n_scripts_extension.rb file using TEXT_ASSET_CACHE_DIR
-TO-DO
-=====
+
+== TO-DO
Figure out what the core team needs to get this puppy baked into Radiant!
Improve caching by just clearing JS files on JS file update (and CSS on
View
15 app/controllers/admin/text_asset_controller.rb
@@ -1,6 +1,6 @@
class Admin::TextAssetController < Admin::AbstractModelController
- only_allow_access_to :index, :new, :edit, :remove,
+ only_allow_access_to :index, :new, :edit, :remove, :upload,
:when => [:developer, :admin],
:denied_url => { :controller => 'page', :action => 'index' },
:denied_message => 'You must have developer or administrator privileges to perform this action.'
@@ -61,9 +61,9 @@ def upload
responds_to_parent do
render :update do |page|
# populate errors in the errors popup and call method to show
- page.replace_html "errors_for_#{model_name}",
- '<ul class="uploadErrors">' +
- @text_asset.errors.collect{|k,v|
+ page.replace_html "errors_for_#{model_name}",
+ '<ul class="uploadErrors">' +
+ @text_asset.errors.collect{|k,v|
%{<li class="warning">#{k.humanize.titlecase}: #{v}</li>}
}.to_s +
'</ul>'
@@ -72,9 +72,12 @@ def upload
end
else # success!
+ # AbstractController methods aren't available within parent/render
+ # blocks so call #model_index_url now to use from inside
+ index_url = model_index_url
responds_to_parent do
- render :update do |page|
- page.redirect_to(:action => 'index')
+ render :update do |page|
+ page.redirect_to index_url
end
end
View
54 app/controllers/text_asset_site_controller.rb
@@ -1,54 +0,0 @@
-class TextAssetSiteController < SiteController
-# This controller adds functionality to site controller to render text_assets
-# (similarly to how it renders pages). This interacts with my
-# TextAssetResponseCache model (which behaves very much like ResponseCache)
-# to cache file items in their own separate cache.
-
- def text_asset_cache
- @text_asset_cache ||= TextAssetResponseCache.instance
- end
-
-
- def show_text_asset
- response.headers.delete('Cache-Control')
-
- filename = params[:filename].join('/')
- url = "#{params[:directory]}/#{filename}"
-
- if (request.get? || request.head?) and live? and (text_asset_cache.response_cached?(url))
- @text_asset_cache.update_response(url, response, request)
- @performed_render = true
- else
- show_uncached_text_asset(filename, params[:asset_type], url)
- end
- end
-
-
- private
-
- def show_uncached_text_asset(filename, asset_class, url)
- case asset_class
- when 'stylesheet'
- @text_asset = Stylesheet.find_by_filename(filename)
- mime_type = StylesNScripts::Config[:stylesheet_mime_type]
- when 'javascript'
- @text_asset = Javascript.find_by_filename(filename)
- mime_type = StylesNScripts::Config[:javascript_mime_type]
- end
- unless @text_asset.nil?
- response.headers['Content-Type'] = mime_type
- # set the last modified date based on updated_at time for the asset
- # we can do this as long as there is no dynamic content in the assets
- response.headers['Last-Modified'] = @text_asset.effectively_updated_at.httpdate
- response.body = @text_asset.render
-
- # for text_assets, we cache no matter what (no 'status' setting for them)
- text_asset_cache.cache_response(url, response) if request.get?
- @performed_render = true
- else
- params[:url] = url
- show_page
- end
- end
-
-end
View
1 app/models/javascript_filter.rb
@@ -0,0 +1 @@
+class JavascriptFilter < TextAssetFilter; end
View
1 app/models/stylesheet_filter.rb
@@ -0,0 +1 @@
+class StylesheetFilter < TextAssetFilter; end
View
10 app/models/text_asset.rb
@@ -14,6 +14,8 @@ class TextAsset < ActiveRecord::Base
# the following regexp uses \A and \Z rather than ^ and $ to enforce no "\n" characters
validates_format_of :filename, :with => %r{\A[-_.A-Za-z0-9]*\Z}, :message => 'invalid format'
+ object_id_attr :filter, TextAssetFilter
+
include Radiant::Taggable
class TagError < StandardError; end
@@ -38,6 +40,8 @@ def effectively_updated_at
def render
text = self.content
text = parse(text)
+ text = self.filter.filter(text)
+ text
end
@@ -55,7 +59,7 @@ def self.create_from_file(file)
@text_asset = self.new
if file.blank?
@text_asset.errors.add(:uploaded_file, 'no file submitted for upload')
-
+
elsif !file.kind_of?(ActionController::UploadedFile)
@text_asset.errors.add(:uploaded_file, 'unusable format')
@@ -74,8 +78,8 @@ def self.create_from_file(file)
private
- # Adds a tag named after the inheriting class name (so <r:javascript> or
- # <r:stylesheet>. This method is kind of funky since we wanted to define
+ # Adds a tag named after the inheriting class name (i.e. <r:javascript> or
+ # <r:stylesheet>). This method is kind of funky since we wanted to define
# the tag in only one place yet we don't have the inheriting class' name
# until after initialization.
def create_tags
View
4 app/models/text_asset_context.rb
@@ -1,5 +1,5 @@
class TextAssetContext < Radius::Context
-
+
def initialize(text_asset)
@show_errors = true
super()
@@ -36,5 +36,5 @@ def tag_missing(name, attributes = {}, &block)
def raise_errors?
RAILS_ENV != 'production' && @show_errors
end
-
+
end
View
20 app/models/text_asset_filter.rb
@@ -0,0 +1,20 @@
+class TextAssetFilter
+ include Simpleton
+ include Annotatable
+
+ annotate :filter_name
+
+ def filter(text)
+ text
+ end
+
+ class << self
+ def inherited(subclass)
+ subclass.filter_name = subclass.name.to_name('Filter')
+ end
+
+ def filter(text)
+ instance.filter(text)
+ end
+ end
+end
View
2 app/models/text_asset_observer.rb
@@ -1,6 +1,6 @@
class TextAssetObserver < ActiveRecord::Observer
observe Stylesheet, Javascript
-
+
def after_save(model)
update_dependencies(model, model.updated_at)
update_other_dependants(model, model.updated_at)
View
4 app/models/text_asset_response_cache.rb
@@ -1,7 +1,9 @@
class TextAssetResponseCache < ResponseCache
def initialize(options={})
- @@defaults[:directory] = "#{RAILS_ROOT}/#{StylesNScripts::Config[:response_cache_directory]}"
+ # Use TEXT_ASSET_CACHE_DIR but strip leading and trailing slashes (if any)
+ @@defaults[:directory] = RAILS_ROOT + '/' +
+ TEXT_ASSET_CACHE_DIR.gsub(/^\/+/, '').gsub(/\/+$/, '')
@@defaults[:expire_time] = 1.year
super(options)
end
View
59 app/views/admin/text_asset/edit.html.haml
@@ -1,22 +1,39 @@
-- if @text_asset.new_record?
- %h1== New #{proper_model_name}
-- else
- %h1== Edit #{proper_model_name}
-- form_tag do
- = hidden_field model_name, 'lock_version'
- .form-area
- %p.title
- %label{:for=>"#{model_name}_filename"} Filename
- = text_field model_name, "filename", :class => 'textbox', :maxlength => 100
- %p.content
- %label{:for=>"#{model_name}_content"} Body
- ~ text_area model_name, "content", :class => "textarea", :style => "width: 100%"
- = updated_stamp @text_asset
+- render_region :main do |main|
+ - main.edit_header do
+ - if @text_asset.new_record?
+ %h1== New #{proper_model_name}
+ - else
+ %h1== Edit #{proper_model_name}
+ - main.edit_form do
+ - form_tag do
+ = hidden_field model_name, 'lock_version'
+ = render_region :form_top
+ .form-area
+ - render_region :form do |form|
+ - form.edit_title do
+ %p.title
+ %label{:for=>"#{model_name}_filename"} Filename
+ = text_field model_name, "filename", :class => 'textbox', :maxlength => 100
+ - form.edit_content do
+ %p.content
+ %label{:for=>"#{model_name}_content"} Body
+ ~ text_area model_name, "content", :class => "textarea", :style => "width: 100%"
+ .row
+ - render_region :content_bottom do |content_bottom|
+ - content_bottom.edit_filter do
+ - unless (proper_model_name + "Filter").constantize.descendants.empty?
+ %p
+ %label{:for=>"#{model_name}_filter"} Filter
+ = select_tag "#{model_name}[filter_id]", options_for_select([['<none>', '']] + (proper_model_name + "Filter").constantize.descendants.map { |f| f.filter_name }.sort, @text_asset.filter_id), :id => "#{model_name}_filter"
+ - form.edit_timestamp do
+ = updated_stamp @text_asset
+
+ = javascript_tag %{$("#{model_name}_filename").activate()}
- = javascript_tag "$("#{model_name}_filename").activate()"
-
- %p.buttons
- = save_model_button(@text_asset)
- = save_model_and_continue_editing_button(@text_asset)
- or
- = link_to 'Cancel', send("#{model_name}_index_url")
+ - render_region :form_bottom do |form_bottom|
+ - form_bottom.edit_buttons do
+ %p.buttons
+ = save_model_button(@text_asset)
+ = save_model_and_continue_editing_button(@text_asset)
+ or
+ = link_to 'Cancel', send("#{model_name}_index_url")
View
50 custom_settings.rb
@@ -1,50 +0,0 @@
-# Initializes settings for the extension. By uncommenting lines below, you may
-# customize these settings. This file gets executed once at load time so these
-# values are designed to stick until you reboot Radiant.
-#
-# Ideally, along with some updating to Radiant, managing these settings would
-# move to their own location in the Radiant Admin UI (probably with admin-only
-# permissions).
-
-module CustomSettings
- # always start by initializing the defauls so we know what to expect...
- StylesNScripts::Config.restore_defaults
-
- # Uncomment the items below to customize the locations from which stylesheets
- # and javascripts are served. The default values are:
- # * /css
- # * /js
- #
- # These default values are not the Rails-standard 'javascript' and
- # 'stylesheets' directories because Radiant is already using those.
- #
- # Currently, you may use complex paths like:
- # 'assets/text/css'
-
-# StylesNScripts::Config[:stylesheet_directory] = 'my/stylesheets/go/here'
-# StylesNScripts::Config[:javascript_directory] = 'all_my_scripts'
-
-
-
- # Uncomment the items below to customize the MIME types for stylesheets and
- # and javascripts are served. The default values are:
- # * text/css
- # * text/javascript
-
-# StylesNScripts::Config[:stylesheet_mime_type] = "text/foo"
-# StylesNScripts::Config[:javascript_mime_type] = "application/bar"
-
-
- # Uncomment the item below to customize where stylesheets and javascripts are
- # cached. (This extension caches those files in a different location than
- # where Radiant's ResponseCache caches pages.
- #
- # Our cache (the TextAssetResponseCache) will periodically blow away this
- # entire directory so make sure you don't pick some conflicting name. For
- # instance, choosing 'public' as your cache folder would be *bad*
- #
- # The default is:
- # * text_asset_cache
-
-# StylesNScripts::Config[:response_cache_directory] = "my_cache_directory"
-end
View
10 db/migrate/001_create_text_assets.rb
@@ -13,6 +13,16 @@ def self.up
def self.down
drop_table :text_assets
+ [ 'stylesheet_directory',
+ 'javascript_directory',
+ 'stylesheet_mime_type',
+ 'javascript_mime_type',
+ 'response_cache_directory'
+ ].each do |config_name|
+ id = Radiant::Config.find_by_key(config_name).id
+ Radiant::Config.delete(id) unless id.nil?
+ say "Removed config for #{config_name}"
+ end
end
end
View
67 db/migrate/003_rename_config_keys_and_add_filter_id.rb
@@ -0,0 +1,67 @@
+class RenameConfigKeysAndAddFilterId < ActiveRecord::Migration
+
+ def self.up
+ # add filter_id attribute
+ add_column :text_assets, :filter_id, :string, :limit => 25
+
+ # move all the old keys config to the new names (cleaner namespace)
+ [ {:old => 'stylesheet_directory', :new => 'SnS.stylesheet_directory'},
+ {:old => 'javascript_directory', :new => 'SnS.javascript_directory'},
+ {:old => 'stylesheet_mime_type', :new => 'SnS.stylesheet_mime_type'},
+ {:old => 'javascript_mime_type', :new => 'SnS.javascript_mime_type'}
+ ].each do |keys|
+ self.rename_key(keys[:old],keys[:new])
+ end
+
+ # remove the cache directory setting and, if necessary notify user of issues
+ if old_cache_config = Radiant::Config.find_by_key('response_cache_directory')
+ if old_cache_config.value != TEXT_ASSET_CACHE_DIR
+ puts "\n * * * * NOTICE * * * *"
+ puts " The Styles 'n Scripts extension has changed the way its cache directory"
+ puts " is set and the previous setting cannot be migrated. It can now be set"
+ puts " using TEXT_ASSET_CACHE_DIR (found in: styles_n_scripts_extension.rb)."
+ puts %{\n Your previous setting was: "#{old_cache_config.value}"}
+ puts %{ The new value is currently: "#{TEXT_ASSET_CACHE_DIR}"}
+ puts "\n If you want to use your old value again, go change TEXT_ASSET_CACHE_DIR"
+ puts "\n * * * * * * * * * * * *\n\n"
+ Radiant::Config.delete(old_cache_config.id)
+ say 'Removed old setting for "response_cache_directory"'
+ end
+ end
+
+ end
+
+
+ def self.down
+ remove_column :text_assets, :filter_id
+
+ [ {:old => 'SnS.stylesheet_directory', :new => 'stylesheet_directory'},
+ {:old => 'SnS.javascript_directory', :new => 'javascript_directory'},
+ {:old => 'SnS.stylesheet_mime_type', :new => 'stylesheet_mime_type'},
+ {:old => 'SnS.javascript_mime_type', :new => 'javascript_mime_type'}
+ ].each do |keys|
+ rename_key(keys[:old], keys[:new])
+ end
+
+ Radiant::Config['response_cache_directory'] = TEXT_ASSET_CACHE_DIR.gsub(/^\/+/, '').gsub(/\/+$/, '')
+ say 'Copied value from TEXT_ASSET_CACHE_DIR into "response_cache_directory"'
+
+ end
+
+
+ private
+
+ def self.rename_key(old_key, new_key)
+ if config = Radiant::Config.find_by_key(old_key)
+ if new_config = Radiant::Config.find_by_key(new_key)
+ # new and old keys exist - move the value (weird fringe case)
+ new_config.update_attribute(:value, config.value)
+ Radiant::Config.delete(config.id)
+ else
+ config.update_attribute(:key, new_key)
+ end
+ say %{Moved value from "#{old_key}" to "#{new_key}"}
+ end
+ end
+
+end
View
24 lib/extended_page_tags.rb → lib/page_tag_mixins.rb
@@ -1,4 +1,4 @@
-module ExtendedPageTags
+module PageTagMixins
include Radiant::Taggable
class TagError < StandardError; end
@@ -16,24 +16,24 @@ class TagError < StandardError; end
].each do |current_tag|
desc %{
- Renders the content from or a reference to the #{current_tag[:name]}
+ Renders the content from or a reference to the #{current_tag[:name]}
specified in the @name@ attribute. Additionally, the @as@ attribute can be
used to make the tag render as one of the following:
-
+
* @content@ - renders the #{current_tag[:name]}'s content (this is the
default when the @as@ attribute is omitted).
-
- * @inline@ - wraps the #{current_tag[:name]}'s content in an (X)HTML
+
+ * @inline@ - wraps the #{current_tag[:name]}'s content in an (X)HTML
@<#{current_tag[:inline_tag]}>@ element.
-
+
* @url@ - the url to the #{current_tag[:name]} (relative to the web root).
-
+
* @link@ - embeds the url in an (X)HTML @<#{current_tag[:link_tag]}>@
element (creating a link to the external #{current_tag[:name]}).
-
-
+
+
*Additional Options:*
- When rendering @as@="@inline@" or @as@="@link@", the (X)HTML @type@ attribute
+ When rendering @as@="@inline@" or @as@="@link@", the (X)HTML @type@ attribute
is automatically be set to the default #{current_tag[:name]} content-type.
You can overrride this attribute or add additional ones by passing extra
attributes to the @<r:#{current_tag[:name]}>@ tag. For example,
@@ -44,8 +44,8 @@ class TagError < StandardError; end
Your #{current_tag[:name]}'s content here...
//]]>
</#{current_tag[:inline_tag]}></code></pre>
-
- *Usage:*
+
+ *Usage:*
<pre><code><r:#{current_tag[:name]} name="#{current_tag[:sample_file]}" [as="content | inline | url | link"] [other attribues...] /></code></pre>
}
tag(current_tag[:name]) do |tag|
View
70 lib/site_controller_mixins.rb
@@ -0,0 +1,70 @@
+module SiteControllerMixins
+
+ def self.included(base)
+ base.class_eval do
+ prepend_before_filter :ensure_no_login_required
+ before_filter :parse_url_for_text_assets, :only => :show_page
+ end
+ end
+
+
+ def text_asset_cache
+ @text_asset_cache ||= TextAssetResponseCache.instance
+ end
+
+
+ private
+
+ # There's some really weird behavior going on here. For some reason -- only
+ # when running specs -- the LoginSystem seems to be reset and so, forgets
+ # that login is not required for SiteController. This method was added to
+ # refresh its memory (I really wish I didn't have to do this step).
+ def ensure_no_login_required
+ SiteController.no_login_required unless no_login_required?
+ end
+
+
+ def parse_url_for_text_assets
+ url = params[:url]
+ if url.kind_of?(Array)
+ url = url.join('/')
+ else
+ url = url.to_s
+ end
+ if url =~ %r{^\/?(#{StylesNScripts::Config[:stylesheet_directory]})\/(.*)$}
+ show_text_asset($2, 'stylesheet')
+
+ elsif url =~ %r{^\/?(#{StylesNScripts::Config[:javascript_directory]})\/(.*)$}
+ show_text_asset($2, 'javascript')
+ end
+ end
+
+
+ def show_text_asset(filename, asset_type)
+ response.headers.delete('Cache-Control')
+ cache_url = "#{asset_type}_cache/#{filename}"
+
+ if (request.get? || request.head?) and live? and (text_asset_cache.response_cached?(cache_url))
+ text_asset_cache.update_response(cache_url, response, request)
+ @performed_render = true
+ else
+ show_uncached_text_asset(filename, asset_type, cache_url)
+ end
+ end
+
+
+ def show_uncached_text_asset(filename, asset_type, cache_url)
+ @text_asset = asset_type.camelcase.constantize.find_by_filename(filename)
+ mime_type = StylesNScripts::Config["#{asset_type}_mime_type"]
+
+ unless @text_asset.nil?
+ response.headers['Content-Type'] = mime_type
+ response.headers['Last-Modified'] = @text_asset.effectively_updated_at.httpdate
+ response.body = @text_asset.render
+ # for text_assets, we cache no matter what (no 'status' setting for them)
+ text_asset_cache.cache_response(cache_url, response) if request.get?
+ @performed_render = true
+ end
+ end
+
+end
View
42 lib/styles_n_scripts/config.rb
@@ -1,14 +1,12 @@
module StylesNScripts
-
class Config
# stores the default values for the settings
# note: key names *must* be declared as strings -- *not* as symbols
@defaults = { 'stylesheet_directory' => 'css',
'javascript_directory' => 'js',
'stylesheet_mime_type' => 'text/css',
- 'javascript_mime_type' => 'text/javascript',
- 'response_cache_directory' => 'text_asset_cache'
+ 'javascript_mime_type' => 'text/javascript'
}
@@live_config = {}
@@ -26,9 +24,10 @@ def [](key)
key = key.to_s
validate_key(key)
if @@live_config[key].blank?
- @@live_config[key] = Radiant::Config[key]
+ return @defaults[key] unless Radiant::Config.table_exists?
+ @@live_config[key] = Radiant::Config["SnS.#{key}"]
if @@live_config[key].blank?
- @@live_config[key] = Radiant::Config[key] = @defaults[key]
+ @@live_config[key] = Radiant::Config["SnS.#{key}"] = @defaults[key]
end
end
@@live_config[key]
@@ -38,58 +37,53 @@ def [](key)
# Setter for Config key/value pairs. Keys must be limited to valid
# settings for the extension.
def []=(key, value)
+ validate_key(key)
+ return unless Radiant::Config.table_exists?
# key must be in the form of a string for Radiant:Config
key = key.to_s
case key
- when 'response_cache_directory'
- value = validate_cache_directory(value, key)
when /_directory$/
value = validate_directory(value, key)
+ @@live_config[key] = Radiant::Config["SnS.#{key}"] = value
when /_mime_type$/
validate_mime_type(value, key)
+ @@live_config[key] = Radiant::Config["SnS.#{key}"] = value
+ TextAssetResponseCache.instance.clear
end
+ end
- @@live_config[key] = Radiant::Config[key] = value
+
+ def to_hash
+ Hash[*@defaults.keys.map { |key| [key, self[key]] }.flatten]
end
def restore_defaults
- @defaults.each do |key, value|
- Radiant::Config[key] = value
- end
- @@live_config = {}
+ @defaults.each { |key, value| self[key] = value }
end
private
# determines whether 'key' matches one of the keys in @defaults (
def validate_key(key)
- raise("invalid setting name: #{key.inspect}") unless @defaults.has_key?(key)
- end
-
-
- def validate_cache_directory(directory, directory_label)
- raise("invalid #{directory_label} value: #{directory.inspect}") unless directory =~ %r{\A/?[-_A-Za-z0-9]*/?\Z}
- # return value with leading/trailing slashes removed
- directory.gsub(/^\//, '').gsub(/\/$/, '')
+ raise(%{Invalid setting name: "#{key}"}) unless @defaults.has_key?(key.to_s)
end
def validate_directory(directory, directory_label)
- raise("invalid #{directory_label} value: #{directory.inspect}") unless directory =~ %r{\A/?[-_A-Za-z0-9]+(/[-_A-Za-z0-9]+)*/?\Z}
+ raise(%{Invalid #{directory_label} value: "#{directory}"}) unless directory =~ %r{\A/?[-_A-Za-z0-9]+(/[-_A-Za-z0-9]+)*/?\Z}
# return value with leading/trailing slashes removed
- directory.gsub(/^\//, '').gsub(/\/$/, '')
+ directory.gsub(/^\/+/, '').gsub(/\/+$/, '')
end
def validate_mime_type(mime_type, mime_type_label)
- raise("invalid #{mime_type_label} value: #{mime_type.inspect}") unless mime_type =~ %r{\A[-.A-Za-z0-9]+(/[-.A-Za-z0-9]+)*\Z}
+ raise(%{Invalid #{mime_type_label} value: "#{mime_type}"}) unless mime_type =~ %r{\A[-.A-Za-z0-9]+(/[-.A-Za-z0-9]+)*\Z}
end
end
end
-
end
View
48 lib/tasks/styles_n_scripts_extension_tasks.rake
@@ -1,8 +1,8 @@
namespace :radiant do
namespace :extensions do
namespace :styles_n_scripts do
-
- desc "Runs the migration of the Styles N Scripts extension"
+
+ desc "Runs the migration of the SnS extension"
task :migrate => :environment do
require 'radiant/extension_migrator'
if ENV["VERSION"]
@@ -11,8 +11,9 @@ namespace :radiant do
StylesNScriptsExtension.migrator.migrate
end
end
-
- desc "Copies public assets of the Styles N Scripts to the instance public/ directory."
+
+
+ desc "Copies public assets of SnS to the instance public/ directory."
task :update => :environment do
is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
Dir[StylesNScriptsExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
@@ -22,7 +23,42 @@ namespace :radiant do
mkdir_p RAILS_ROOT + directory
cp file, RAILS_ROOT + path
end
- end
+ end
+
+
+ desc "Configure SnS options"
+ task :config => :environment do
+ new_settings = {}
+
+ if ARGV.length == 2 && ARGV[1] = 'restore_defaults'
+ print %{\nRestoring StylesNScripts::Config Defaults -> }
+ StylesNScripts::Config.restore_defaults
+ puts "Success"
+
+
+ else
+ # iterate through each argument (except ARGV[0]) and verify well-formed
+ ARGV[1..ARGV.length-1].each do |argument|
+ arg_elements = argument.split("=")
+ raise(%{Invalid parameter: "#{argument}"}) unless arg_elements.length == 2
+ new_settings[arg_elements[0]] = arg_elements[1]
+ end
+
+ new_settings.each do |k,v|
+ print %{--setting StylesNScripts::Config[#{k}] = "#{v}" -> }
+ StylesNScripts::Config[k] = v
+ puts "Success"
+ end
+ end
+
+ puts "\n Current Styles 'n Scripts Configuration:\n\n"
+ # convert the hash into an array to sort by key name (pair[0])
+ StylesNScripts::Config.to_hash.to_a.sort_by { |pair| pair[0] }.each do |pair|
+ puts %{ #{pair[0].to_s.ljust(24)} "#{pair[1].to_s}"}
+ end
+ puts %{\n TEXT_ASSET_CACHE_DIR "#{TEXT_ASSET_CACHE_DIR}"}
+ end
+
end
end
-end
+end
View
42 spec/controllers/admin/text_asset_controller_spec.rb
@@ -21,7 +21,7 @@
].each do |current_asset|
- describe "For #{current_asset[:name].pluralize},", Admin::TextAssetController do
+ describe "For #{current_asset[:name].pluralize}, the", Admin::TextAssetController do
integrate_views
@@ -35,7 +35,7 @@
scenario :users, :javascripts if current_asset[:name] == 'javascript'
test_helper :caching
-
+
before :each do
login_as :developer
@cache = @controller.cache = FakeResponseCache.new
@@ -48,39 +48,45 @@
+ # The :upload action is an addtion to the AbstractModelController
[:index, :new, :edit, :remove, :upload].each do |action|
- # moved this test into the actions as the controller inits this before each action
- it "should have a model_class of #{current_asset[:name]}" do
- controller.class.model_class.should == current_asset[:class]
- end
+ describe "#{action} action" do
+ # Different than the AbstractModelController, ours initializes the model
+ # class just prior to each action. So it gets spec'ed for each action.
+ it "should handle #{current_asset[:name].titlecase.pluralize}" do
+ controller.class.model_class.should == current_asset[:class]
+ end
- describe "#{action} action" do
- it "should require login to access the #{action} action" do
+ it "should require login to access" do
logout
- lambda { get action }.should require_login
+ lambda { get action, :asset_type => current_asset[:name] }.
+ should require_login
end
it "should allow access to developers" do
- lambda { get action, :id => text_asset_id('main'),
- :asset_type => current_asset[:name] }.should
- restrict_access(:allow => [users(:developer)])
+ lambda { get action,
+ :id => text_asset_id('main'),
+ :asset_type => current_asset[:name] }.
+ should restrict_access(:allow => [users(:developer)])
end
it "should allow access to admins" do
- lambda { get action, :id => text_asset_id('main'),
- :asset_type => current_asset[:name]}.should
- restrict_access(:allow => [users(:admin)])
+ lambda { get action,
+ :id => text_asset_id('main'),
+ :asset_type => current_asset[:name]}.
+ should restrict_access(:allow => [users(:admin)])
end
it "should deny non-developers and non-admins" do
- lambda { get action,:asset_type => current_asset[:name] }.should
- restrict_access(:deny => [users(:non_admin), users(:existing)])
+ lambda { get action, :asset_type => current_asset[:name] }.
+ should restrict_access(:deny => [users(:non_admin),
+ users(:existing)])
end
end
@@ -238,7 +244,7 @@
describe "via GET" do
before :each do
- get :edit,
+ get :edit,
:id => text_asset_id('main'),
:asset_type => current_asset[:name]
end
View
49 spec/controllers/admin/text_asset_controller_upload_spec.rb
@@ -30,7 +30,40 @@
describe "upload action" do
-
+#
+# it "should require login to access" do
+# logout
+# lambda { get :upload, :asset_type => current_asset[:name] }.
+# should require_login
+# end
+#
+#
+# it "should allow access to developers" do
+# lambda { get :upload,
+# :id => text_asset_id('main'),
+# :asset_type => current_asset[:name] }.
+# should restrict_access(:allow => [users(:developer)])
+# end
+#
+#
+# it "should allow access to admins" do
+# lambda { get :upload,
+# :id => text_asset_id('main'),
+# :asset_type => current_asset[:name]}.
+# should restrict_access(:allow => [users(:admin)])
+# end
+#
+#
+# it "should deny non-developers and non-admins" do
+# lambda { get :upload,
+# :asset_type => current_asset[:name] }.
+# should restrict_access(:deny => [users(:non_admin),
+# users(:existing)])
+# end
+
+
+
+
describe "via GET" do
before :each do
@@ -55,13 +88,13 @@
describe "via POST" do
describe "with no file in the params" do
-
+
before :each do
post :upload,
:asset_type => current_asset[:name]
end
-
+
it "should return a 'Bad Request (400) Error'" do
response.response_code.should == 400
end
@@ -209,17 +242,17 @@
it "should redirect the parent window to the index page" do
url = send("#{current_asset[:name]}_index_url", :only_path => true)
- js_regexp = %r{window.location.href = ['"]#{url}['"][;]}
+ js_regexp = %r{window.location.href = ['"][^'"]*#{url}['"][;]}
assert_select_parent { |script|
script.should match(js_regexp)
}
end
- it "should create a new current_asset[:name] based on the uploaded file" do
+ it "should create a new #{current_asset[:name]} based on the uploaded file" do
# filename should be hypenated (where appropriate)
- current_asset[:class].find_by_filename('hello-world.txt').content.should
- be("Hello World! (text file)")
+ current_asset[:class].find_by_filename('hello-world.txt').content.
+ should eql("Hello World! (text file)")
end
end
@@ -240,7 +273,7 @@ def mock_uploader(file, type = 'text/plain', file_class = ActionController::Uplo
uploader = file_class.new
uploader.original_path = filename
uploader.content_type = type
-
+
def uploader.read
File.read(original_path)
end
View
172 ...ollers/text_asset_site_controller_spec.rb → ...ollers/site_controller_extensions_spec.rb
@@ -1,28 +1,24 @@
-# This specifies the behavior of the TextAssetSiteController which behaves much
-# like Radiant's SiteController except that it serves up the stylesheets and
-# javascripts to the public. Many of these specs confirm SiteController's
-# behavior from which TextAssetSiteController inherits.
-#
+# This specifies the extended behavior of the SiteController which includes
+# serving up the stylesheets and javascripts to the public.
require File.dirname(__FILE__) + '/../spec_helper'
-describe TextAssetSiteController do
+describe SiteController, "(Extended)" do
integrate_views
# Pages scenario is used for two reasons. First, we test for conditions where
- # pages have been created that conflict with css_ or javascript_directory
- # values. Secondly, at least one page must exist when SiteController goes
- # to find an uncached page or else it redirects the user to the login screen.
+ # pages urls may conflict with stylesheet_ or javascript_directory urls.
+ # Secondly, at least one page must exist when SiteController goes to find an
+ # uncached page or else it redirects the user to the login screen.
scenario :javascripts, :stylesheets, :pages
before(:each) do
# make sure the css_ and js_directories are the default ones
StylesNScripts::Config.restore_defaults
- ActionController::Routing::Routes.reload!
# don't bork results with stale cache items
- controller.text_asset_cache.clear
+# controller.text_asset_cache.clear
end
@@ -40,103 +36,88 @@
[ { :class => Stylesheet,
:name => 'stylesheet',
- :default_directory => 'css' },
+ :default_directory => "css" },
{ :class => Javascript,
:name => 'javascript',
- :default_directory => 'js' }
+ :default_directory => "js" }
].each do |current_asset|
- describe "#{current_asset[:name]} request rendering" do
-
- it "should route urls based on a customized setting for: #{current_asset[:name]}_directory" do
- # change the css_ or js_direectory and recreate routes to use them
- StylesNScripts::Config[current_asset[:name] + '_directory'] = 'foo'
- ActionController::Routing::Routes.reload!
-
- params_from(:get, "/foo/main").should ==
- { :controller => 'text_asset_site',
- :action => 'show_text_asset',
- :filename => ['main'],
- :directory => 'foo',
- :asset_type => current_asset[:name] }
+
+ describe ",when routing #{current_asset[:name].pluralize}," do
+
+ it "should send default #{current_asset[:name]}_directory urls (setting isn't customized) to #show_page action" do
+ params_from(:get, "/#{current_asset[:default_directory]}/main").should ==
+ { :controller => "site",
+ :action => "show_page",
+ :url => current_asset[:default_directory].split("/") << "main" }
end
- it "should route urls based on a multi-level, customized setting for: #{current_asset[:name]}_directory" do
- # change the css_ or js_direectory and recreate routes to use them
- StylesNScripts::Config[current_asset[:name] + '_directory'] = 'foo/bar/baz'
- ActionController::Routing::Routes.reload!
+ it "should send customized #{current_asset[:name]}_directory urls to #show_page action" do
+ StylesNScripts::Config["#{current_asset[:name]}_directory"] = "foo"
+ params_from(:get, "/foo/main").should ==
+ { :controller => "site",
+ :action => "show_page",
+ :url => ["foo", "main"] }
+ end
+
+ it "should send multi-level, customized #{current_asset[:name]}_directory urls to #show_page action" do
+ StylesNScripts::Config[current_asset[:name] + '_directory'] = 'foo/bar/baz'
params_from(:get, "/foo/bar/baz/main").should ==
- { :controller => 'text_asset_site',
- :action => 'show_text_asset',
- :filename => ['main'],
- :directory => 'foo/bar/baz',
- :asset_type => current_asset[:name] }
+ { :controller => "site",
+ :action => "show_page",
+ :url => ["foo", "bar", "baz", "main"] }
end
+ end
+
- it "should route urls based on the default if #{current_asset[:name]}_directory isn't customized" do
- params_from(:get, "/#{current_asset[:default_directory]}/main").should ==
- { :controller => 'text_asset_site',
- :action => 'show_text_asset',
- :filename => ['main'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name] }
- end
- it "should find and render an existing asset" do
- get :show_text_asset,
- :filename => ['main'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ describe "valid GET requests" do
+
+ it "should render the content for existing #{current_asset[:name].pluralize}" do
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << "main"
# For SOME reason, the response.header does not include a 'status' key so it is
# not possible to check for success.
# response.should be_success
response.body.should == "Main #{current_asset[:name]} content"
end
- it "should find and render an existing asset on the default dev site" do
+ it "should find and render an existing #{current_asset[:name]} on the default dev site" do
request.host = "dev.site.com"
- get :show_text_asset,
- :filename => ['main'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << "main"
# For SOME reason, the response.header does not include a 'status' key so it is
# not possible to check for success.
# response.should be_success
response.body.should == "Main #{current_asset[:name]} content"
end
- it "should render a 404 page for a non-existing asset" do
- get :show_text_asset,
- :filename => ['non-existent.file'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ it "should render a 404 page for a non-existing #{current_asset[:name]}" do
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << "non-existent.file"
response.should render_template('site/not_found')
response.headers["Status"].should == "404 Not Found"
end
it "should render a 404 page for #{current_asset[:name]}_directory (/#{current_asset[:default_directory]}/)" do
- get :show_text_asset,
- :filename => [],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/")
response.should render_template('site/not_found')
response.headers["Status"].should == "404 Not Found"
end
it "should render a 404 page if url includes a deeper path than :#{current_asset[:name]}_directory" do
- get :show_text_asset,
- :filename => ['bogus', 'extra', 'path', 'segments', 'main'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'bogus' << 'extra' << 'path' << 'main'
response.headers["Status"].should == "404 Not Found"
response.should render_template('site/not_found')
end
@@ -147,14 +128,12 @@
# This is sort of dumb. Really, users should not go anywhere near creating
# a page with the same name as the css directory. If they do, here's what
# should happen.
- describe "where page urls conflict with text asset urls" do
+ describe "with URLs that overlap Page namespaces" do
it "should render a page that is competing with :#{current_asset[:name]}_directory (the directory)" do
create_page current_asset[:default_directory]
- get :show_text_asset,
- :filename => [],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/")
response.should be_success
response.body.should == "#{current_asset[:default_directory]} body."
end
@@ -164,11 +143,8 @@
create_page current_asset[:default_directory] do
create_page 'page-inside'
end
- get :show_text_asset,
- :filename => ['page-inside'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
-
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'page-inside'
response.should be_success
response.body.should == 'page-inside body.'
end
@@ -180,10 +156,8 @@
create_page 'another-page'
end
end
- get :show_text_asset,
- :filename => ['page-inside', 'another-page'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'page-inside' << 'another-page'
response.should be_success
response.body.should == 'another-page body.'
end
@@ -194,10 +168,8 @@
create_page 'abc.123'
end
send("create_#{current_asset[:name]}", 'abc.123')
- get :show_text_asset,
- :filename => ['abc.123'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'abc.123'
# For SOME reason, the response.header does not include a 'status' key so it is
# not possible to check for success.
# response.should be_success
@@ -219,28 +191,22 @@
it "should be a string" do
- get :show_text_asset,
- :filename => ['dependant'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'dependant'
response.headers['Last-Modified'].should be_kind_of(String)
end
it "should use a valid HTTP header date format" do
- get :show_text_asset,
- :filename => ['dependant'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'dependant'
response.headers['Last-Modified'].should == "Mon, 01 Jan 1990 00:00:00 GMT"
end
it "should reflect the #{current_asset[:name]}'s updated_at date/time if the file has no dependencies" do
- get :show_text_asset,
- :filename => ['dependant'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'dependant'
response.headers['Last-Modified'].should == Time.gm(1990).httpdate
end
@@ -250,10 +216,8 @@
save_asset_at(@dependency, 1991)
save_asset_at(@dependant, 1992)
- get :show_text_asset,
- :filename => ['dependant'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'dependant'
response.headers['Last-Modified'].should == Time.gm(1992).httpdate
end
@@ -263,13 +227,11 @@
save_asset_at(@dependant, 1993)
save_asset_at(@dependency, 1994)
- get :show_text_asset,
- :filename => ['dependant'],
- :directory => current_asset[:default_directory],
- :asset_type => current_asset[:name]
+ get :show_page,
+ :url => current_asset[:default_directory].split("/") << 'dependant'
response.headers['Last-Modified'].should == Time.gm(1994).httpdate
end
-
+
end
end
View
98 spec/lib/config_spec.rb
@@ -4,24 +4,24 @@
require File.dirname(__FILE__) + '/../spec_helper'
describe StylesNScripts::Config do
-
+
it 'should not accept declarations for invalid setting names' do
- lambda{StylesNScripts::Config['bogus setting name'] = "whatever"}.should
- raise_error(RuntimeError,
- 'text asset response cache location is not settable'
- )
+ lambda{StylesNScripts::Config['bogus name'] = "whatever"}.
+ should raise_error(RuntimeError, 'Invalid setting name: "bogus name"')
end
it 'should return known settings without having to query the database again' do
+ # How to test this??
end
- [ :stylesheet_directory,
- :javascript_directory,
+
+
+ [ :stylesheet_directory,
+ :javascript_directory,
:stylesheet_mime_type,
- :javascript_mime_type,
- :response_cache_directory
+ :javascript_mime_type
].each do |setting_name|
describe setting_name do
@@ -39,13 +39,15 @@
end
end
-
+
+
+
{ :stylesheet_directory => 'css',
:javascript_directory => 'js',
:stylesheet_mime_type => 'text/css',
- :javascript_mime_type => 'text/javascript',
- :response_cache_directory => 'text_asset_cache'
+ :javascript_mime_type => 'text/javascript'
}.each do |setting_name, default_value|
+
describe setting_name do
it "should set default value when starting with an empty db" do
@@ -62,17 +64,20 @@
end
end
+
end
-
-
+
+
+
+
# the following specs apply both to css and js directories...
[:stylesheet_directory, :javascript_directory].each do |setting_name|
- describe setting_name do
+ describe setting_name do
# declare an array of values with alphanumeric-characters, iterate through each and test
['abc', 'ABC', 'aBc', 'AbC123'].each do |alpha_num_value|
- it "should allow alphanumeric chars (like: #{alpha_num_value.inspect})" do
+ it "should allow alphanumeric chars (i.e. #{alpha_num_value.inspect})" do
StylesNScripts::Config[setting_name] = alpha_num_value
StylesNScripts::Config[setting_name].should eql(alpha_num_value)
end
@@ -81,7 +86,7 @@
# declare an array of values with slashes, iterate through each and test
['aBc-123', 'AbC_123', 'a-B_c-1_23'].each do |hyphen_underscore_value|
- it "should allow hyphens, and/or underscores (like: #{hyphen_underscore_value.inspect})" do
+ it "should allow hyphens, and/or underscores (i.e. #{hyphen_underscore_value.inspect})" do
StylesNScripts::Config[setting_name] = hyphen_underscore_value
StylesNScripts::Config[setting_name].should eql(hyphen_underscore_value)
end
@@ -90,7 +95,7 @@
# declare an array of values with slashes, iterate through each and test
['aBc/123', 'Ab-C/1_23', 'a/B_c-1/2_3'].each do |slash_value|
- it "should allow slashes (like: #{slash_value.inspect})" do
+ it "should allow slashes (i.e. #{slash_value.inspect})" do
StylesNScripts::Config[setting_name] = slash_value
StylesNScripts::Config[setting_name].should eql(slash_value)
end
@@ -111,36 +116,40 @@
# declare an array of invalid characters, inject them into valid values and test
%w[! @ # $ % ^ & * ( ) { } < > + = ? , : ; ' " \\ \t \ \n \r \[ \]].each do |invalid_char|
- it "should reject invalid characters (like:#{invalid_char.inspect})" do
+ it "should reject invalid characters (i.e. #{invalid_char.inspect})" do
lambda{StylesNScripts::Config[setting_name] = "aBc#{invalid_char}123"}.should raise_error(
RuntimeError,
- "invalid #{setting_name} value: #{('aBc' + invalid_char + '123').inspect}"
+ %{Invalid #{setting_name} value: "#{('aBc' + invalid_char + '123')}"}
)
end
end
# declare an array of values with bad/multiple slashes, iterate through each and test
['//abc/123', 'abc/123//', '/abc///123/','abc\123'].each do |slash_value|
- it "should reject invalid use of slashes (like: #{slash_value.inspect})" do
+ it "should reject invalid use of slashes (i.e. #{slash_value.inspect})" do
lambda{StylesNScripts::Config[setting_name] = slash_value}.should raise_error(
RuntimeError,
- "invalid #{setting_name} value: #{(slash_value).inspect}"
+ %{Invalid #{setting_name} value: "#{(slash_value)}"}
)
end
end
end
+
end
+
+
# the following specs apply both to css and js mime_types...
[:stylesheet_mime_type, :javascript_mime_type].each do |setting_name|
+
describe setting_name do
-
- ['text/javascript', 'text/javascript1.0', 'text/x-javascript', 'text/css',
+
+ ['text/javascript', 'text/javascript1.0', 'text/x-javascript', 'text/css',
'application/x-ecmascript'].each do |mime_example|
- it "should allow valid mime-types (like: #{mime_example.inspect})" do
+ it "should allow valid mime-types (i.e. #{mime_example.inspect})" do
StylesNScripts::Config[setting_name] = mime_example
StylesNScripts::Config[setting_name].should eql(mime_example)
end
@@ -149,10 +158,10 @@
# declare an array of invalid characters, inject them into valid values and test
%w[! @ # $ % ^ & * ( ) { } < > + = ? , : ; ' " _ \\ \t \ \n \r \[ \]].each do |invalid_char|
- it "should reject invalid characters (like: #{invalid_char.inspect})" do
+ it "should reject invalid characters (i.e. #{invalid_char.inspect})" do
lambda{StylesNScripts::Config[setting_name] = "text/x-#{invalid_char}javascript"}.should raise_error(
RuntimeError,
- "invalid #{setting_name} value: #{('text/x-' + invalid_char + 'javascript').inspect}"
+ %{Invalid #{setting_name} value: "#{('text/x-' + invalid_char + 'javascript')}"}
)
end
end
@@ -161,43 +170,12 @@
it 'should reject mime-types ending in a "/"' do
lambda{StylesNScripts::Config[setting_name] = "text/"}.should raise_error(
RuntimeError,
- "invalid #{setting_name} value: #{('text/').inspect}"
+ "Invalid #{setting_name} value: #{('text/').inspect}"
)
end
end
- end
-
-
- describe 'response_cache_directory' do
-
- # declare an array of values with alphanumeric-characters, iterate through each and test
- ['abc', 'ABC', 'aBc', 'AbC123'].each do |alpha_num_value|
- it "should allow a #{:response_cache_directory} with alphanumeric chars (#{alpha_num_value.inspect})" do
- StylesNScripts::Config[:response_cache_directory] = alpha_num_value
- StylesNScripts::Config[:response_cache_directory].should eql(alpha_num_value)
- end
- end
-
-
- # declare an array of values with slashes, iterate through each and test
- ['aBc-123', 'AbC_123', 'a-B_c-1_23'].each do |hyphen_underscore_value|
- it "should allow hyphens, and/or underscores (like: #{hyphen_underscore_value.inspect})" do
- StylesNScripts::Config[:response_cache_directory] = hyphen_underscore_value
- StylesNScripts::Config[:response_cache_directory].should eql(hyphen_underscore_value)
- end
- end
-
-
- # declare an array of invalid characters, inject them into valid values and test
- %w[! @ # $ % ^ & * ( ) { } < > + = ? , : ; ' " \\ \t \ \n \r \[ \] /].each do |invalid_char|
- it "should reject invalid characters (like:#{invalid_char.inspect})" do
- lambda{StylesNScripts::Config[:response_cache_directory] = "aBc#{invalid_char}123"}.should raise_error(
- RuntimeError,
- "invalid response_cache_directory value: #{('aBc' + invalid_char + '123').inspect}"
- )
- end
- end
end
+
end
View
2 spec/models/stylesheet_and_javascript_dependency_spec.rb
@@ -143,7 +143,7 @@
@dependant = current_tag[:class].find_by_filename('dependant')
@dependant.dependencies.effectively_updated_at.should == Time.gm(1998)
end
-
+
end
end
View
10 spec/models/stylesheet_and_javascript_spec.rb
@@ -1,5 +1,5 @@
# This specs the behavior of the Stylesheet and Javascript models where they
-# share much the same behavior (this is better than spec-ing the TextAsset model
+# share much the same behavior (this is better than spec-ing the TextAsset model
# since, frankly, we care about the Stylesheet and Javascript models, not their
# parent).
#
@@ -10,7 +10,7 @@
[ Stylesheet, Javascript].each do |current_asset|
describe current_asset do
-
+
before(:each) do
@record = current_asset.new
end
@@ -19,7 +19,7 @@
it 'should limit the filename to 100 characters' do
@record.filename = 'a' * 100
@record.should be_valid
-
+
@record.filename = 'a' * 101
@record.should_not be_valid
end
@@ -81,14 +81,14 @@
# declare an array of invalid characters then iterate through each and test
(
- %w[! @ # $ % ^ & * ( ) { } \ / < > + = ? , : ; ' "] +
+ %w[! @ # $ % ^ & * ( ) { } \ / < > + = ? , : ; ' "] +
[' ', "\t", "\n", "\r", '[', ']']
).each do |invalid_char|
it "should not allow filenames with invalid characters (#{invalid_char.inspect})" do
@record.filename = "abc#{invalid_char}123"
@record.should_not be_valid
@record.should have(1).error_on(:filename)
- @record.errors.on(:filename).should == 'invalid format'
+ @record.errors.on(:filename).should == 'invalid format'
end
end
View
12 spec/models/stylesheet_and_javascript_tags_spec.rb
@@ -56,15 +56,13 @@
it "should render the url of the #{current_tag[:name]} when the 'as' attribute is set to 'url'" do
StylesNScripts::Config["#{current_tag[:name]}_directory"] = 'foo/bar/baz'
- ActionController::Routing::Routes.reload!
@page.should render(%{<r:#{current_tag[:name]} name="main" as="url" />}).as(
"/foo/bar/baz/main")
StylesNScripts::Config.restore_defaults
- ActionController::Routing::Routes.reload!
@page.should render(%{<r:#{current_tag[:name]} name="main" as="url" />}).as(
"#{current_tag[:default_directory]}/main")
- end
+ end
@@ -74,7 +72,6 @@
it "should render a <#{current_tag[:inline_element]}> element containing the content of the #{current_tag[:name]} with the type attribute matching the #{current_tag[:name]}_mime_type setting" do
# try with a custom mime_type value
StylesNScripts::Config["#{current_tag[:name]}_mime_type"] = 'bologna'
- ActionController::Routing::Routes.reload!
@page.should render(%{<r:#{current_tag[:name]} name="main" as="inline" />}).as(
%{<#{current_tag[:inline_element]} type="bologna">\n} <<
%{//<![CDATA[\n} <<
@@ -85,7 +82,6 @@
# try with the default mime_type value
StylesNScripts::Config.restore_defaults
- ActionController::Routing::Routes.reload!
@page.should render(%{<r:#{current_tag[:name]} name="main" as="inline" />}).as(
%{<#{current_tag[:inline_element]} type="#{current_tag[:default_mime_type]}">\n} <<
%{//<![CDATA[\n} <<
@@ -116,7 +112,7 @@
%{//]]>\n} <<
%{</#{current_tag[:inline_element]}>}
)
- end
+ end
end
@@ -129,7 +125,6 @@
# try with a custom mime_type value
StylesNScripts::Config["#{current_tag[:name]}_mime_type"] = 'bologna'
StylesNScripts::Config["#{current_tag[:name]}_directory"] = 'foo/bar/baz'
- ActionController::Routing::Routes.reload!
if current_tag[:name] == 'stylesheet'
@page.should render(%{<r:stylesheet name="main" as="link" />}).as(
%{<link rel="stylesheet" href="/foo/bar/baz/main" type="bologna" />}
@@ -142,7 +137,6 @@
# try with the default mime_type and directory values
StylesNScripts::Config.restore_defaults
- ActionController::Routing::Routes.reload!
if current_tag[:name] == 'stylesheet'
@page.should render(%{<r:stylesheet name="main" as="link" />}).as(
%{<link rel="stylesheet" href="#{current_tag[:default_directory]}/main" type="#{current_tag[:default_mime_type]}" />}
@@ -179,7 +173,7 @@
%{<script src="#{current_tag[:default_directory]}/main" type="#{current_tag[:default_mime_type]}" another="mayer" attrib="WEINER"></script>}
)
end
- end
+ end
end
View
71 spec/models/stylesheet_filter_and_javascript_filter_spec.rb
@@ -0,0 +1,71 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+class SimpleStylesheetFilter < StylesheetFilter
+end
+
+
+class ReverseStylesheetFilter < StylesheetFilter
+ filter_name "Custom Filter Name"
+ def filter(text)
+ text.reverse
+ end
+end
+
+
+class SimpleJavascriptFilter < JavascriptFilter
+end
+
+
+class ReverseJavascriptFilter < JavascriptFilter
+ filter_name "Custom Filter Name"
+ def filter(text)
+ text.reverse
+ end
+end
+
+
+[ { :class => StylesheetFilter,
+ :simple => SimpleStylesheetFilter,
+ :reverse => ReverseStylesheetFilter },
+
+ { :class => JavascriptFilter,
+ :simple => SimpleJavascriptFilter,
+ :reverse => ReverseJavascriptFilter }
+
+].each do |current_filter|
+
+ describe current_filter[:class].to_s.pluralize do
+
+ it "should list all the current subclasssed filters as descendants" do
+ # we can't really control all of the filters for our test (the tester may
+ # have other filters installed via extension) but we can test that those
+ # we've built are found.
+ current_filter[:class].descendants.should include(
+ current_filter[:simple])
+ current_filter[:class].descendants.should include(
+ current_filter[:reverse])
+ end
+
+
+ it "should use a subclass's name as the default filter_name (SuperDuperFilter -> 'Super Duper'" do
+ current_filter[:simple].filter_name.should == current_filter[:simple].
+ to_s.titleize.gsub(/\s*Filter$/, '')
+ end
+
+
+ it "should allow developers to override the default with their own filter_name" do
+ current_filter[:reverse].filter_name.should == 'Custom Filter Name'
+ end
+
+
+ it "should pass back the input text unfiltered if a filter is not explicitly defined" do
+ current_filter[:simple].filter('my test text').should == 'my test text'
+ end
+
+
+ it "should pass return filtered text via a #filter method" do
+ current_filter[:reverse].filter('my test text').should == 'txet tset ym'
+ end
+ end
+
+end
View
36 spec/models/text_asset_response_cache_spec.rb
@@ -51,18 +51,21 @@ def initialize(body = '', headers = {})
end
- it 'should have its default directory equal to StylesNScripts::Config setting' do
- # change the config setting, re-init the TextAssetResponseCache (a Singleton)
- # and make sure it slurped up the new setting.
- StylesNScripts::Config[:response_cache_directory] = 'foo'
+ it 'should set cache location to: RAILS_ROOT/TEXT_ASSET_CACHE_DIR' do
+ with_warnings_suppressed{TEXT_ASSET_CACHE_DIR = 'another_cache_location'}
Singleton.send :__init__, TextAssetResponseCache
- TextAssetResponseCache.instance.defaults[:directory].should == "#{RAILS_ROOT}/foo"
+ TextAssetResponseCache.instance.defaults[:directory].
+ should == "#{RAILS_ROOT}/another_cache_location"
+ end
- # reset the config settings, re-init the TextAssetResponseCache (a Singleton)
- # and make sure it aquired the default setting this time.
- StylesNScripts::Config.restore_defaults
- Singleton.send :__init__, TextAssetResponseCache
- TextAssetResponseCache.instance.defaults[:directory].should == "#{RAILS_ROOT}/text_asset_cache"
+
+ ['/cache_dir', 'cache_dir/', '/cache_dir/', '//cache_dir//'].each do |dir|
+ it "should tolerate TEXT_ASSET_CACHE_DIR values with leading or trailing slashes (i.e. #{dir.inspect})" do
+ with_warnings_suppressed{TEXT_ASSET_CACHE_DIR = dir}
+ Singleton.send :__init__, TextAssetResponseCache
+ TextAssetResponseCache.instance.defaults[:directory].
+ should == "#{RAILS_ROOT}/cache_dir"
+ end
end
@@ -81,9 +84,9 @@ def initialize(body = '', headers = {})
@cache.cache_response(url, response)
name = "#{@dir}/test/me.yml"
File.exists?(name).should == true
- file(name).should == "--- \nexpires: 2007-02-08 17:37:09 Z\nheaders: \n Last-Modified: Tue, 27 Feb 2007 06:13:43 GMT\n"
+ file(name).should == "--- \nexpires: 2007-02-08 17:37:09 Z\nheaders: \n Last-Modified: Tue, 27 Feb 2007 06:13:43 GMT\n"
data_name = "#{@dir}/test/me.data"
- file(data_name).should == "content"
+ file(data_name).should == "content"
end
end
@@ -105,4 +108,13 @@ def file(filename)
def response(*args)
TestResponse.new(*args)
+ end
+
+
+ def with_warnings_suppressed
+ old_verbosity = $-v
+ $-v = nil
+ yield
+ ensure
+ $-v = old_verbosity
end
View
2 spec/models/user_action_observer_spec.rb
@@ -16,7 +16,7 @@
Stylesheet.create!(stylesheet_params).created_by.should == @user
end
-
+
it 'should observe javascript creation' do
Javascript.create!(javascript_params).created_by.should == @user
end
View
4 spec/scenarios/javascripts_scenario.rb
@@ -8,7 +8,7 @@ def load
helpers do
def create_javascript(filename, attributes={})
- create_model :javascript,
+ create_model :javascript,
filename.symbolize,
javascript_params(
attributes.reverse_merge(:filename => filename) )
@@ -17,7 +17,7 @@ def create_javascript(filename, attributes={})
def javascript_params(attributes={})
filename = attributes[:filename] || unique_javascript_filename
- {
+ {
:filename => filename,
:content => "javascript content for #{filename}"
}.merge(attributes)
View
4 spec/scenarios/stylesheets_scenario.rb
@@ -8,7 +8,7 @@ def load
helpers do
def create_stylesheet(filename, attributes={})
- create_model :stylesheet,
+ create_model :stylesheet,
filename.symbolize,
stylesheet_params(
attributes.reverse_merge(:filename => filename) )
@@ -17,7 +17,7 @@ def create_stylesheet(filename, attributes={})
def stylesheet_params(attributes={})
filename = attributes[:filename] || unique_stylesheet_filename
- {
+ {
:filename => filename,
:content => "stylesheet content for #{filename}"
}.merge(attributes)
View
67 styles_n_scripts_extension.rb
@@ -1,14 +1,19 @@
+# TEXT_ASSET_CACHE_DIR stores directory where text assets will be cached
+# (relative to RAILS_ROOT). The default value is: "text_asset_cache"
+#
+# NOTE: If you change this, don't forget to remove any previous cache folder
+TEXT_ASSET_CACHE_DIR = "text_asset_cache"
+
# Uncomment this if you reference any of your controllers in activate
require_dependency 'application'
-
-# allows admins to customize settings for the extension:
-include CustomSettings
+require 'ostruct'
class StylesNScriptsExtension < Radiant::Extension
- version "0.4.1"
+ version "0.5"
extension_name "Styles 'n Scripts"
description "Adds CSS and JS file management to Radiant"
+ url ""
define_routes do |map|
@@ -20,7 +25,7 @@ class StylesNScriptsExtension < Radiant::Extension
controller.stylesheet_remove 'admin/css/remove/:id', :action => 'remove'
controller.stylesheet_upload 'admin/css/upload', :action => 'upload'
end
-
+
# Admin javascript Routes
map.with_options(:controller => 'admin/text_asset', :asset_type => 'javascript') do |controller|
controller.javascript_index 'admin/js', :action => 'index'
@@ -29,45 +34,53 @@ class StylesNScriptsExtension < Radiant::Extension
controller.javascript_remove 'admin/js/remove/:id', :action => 'remove'
controller.javascript_upload 'admin/js/upload', :action => 'upload'
end
-
- # Public side routes (for JS and CSS directories)
- map.connect "#{StylesNScripts::Config[:stylesheet_directory]}/*filename",
- :controller => 'text_asset_site', :action => 'show_text_asset',
- :directory => StylesNScripts::Config[:stylesheet_directory],
- :asset_type => 'stylesheet'
-
- map.connect "#{StylesNScripts::Config[:javascript_directory]}/*filename",
- :controller => 'text_asset_site', :action => 'show_text_asset',
- :directory => StylesNScripts::Config[:javascript_directory],
- :asset_type => 'javascript'
end
def activate
admin.tabs.add "CSS", "/admin/css", :after => "Layouts", :visibility => [:admin, :developer]
admin.tabs.add "JS", "/admin/js", :after => "CSS", :visibility => [:admin, :developer]
- Page.send :include, ExtendedPageTags
-
- # join already observed models with extension models
- observables = UserActionObserver.instance.observed_classes | [Stylesheet, Javascript]
+ # Include my mixins (extending PageTags and SiteController)
+ Page.send :include, PageTagMixins
+ SiteController.send :include, SiteControllerMixins
- # update list of observables
- UserActionObserver.send :observe, observables
+ Radiant::AdminUI.class_eval do
+ attr_accessor :text_asset
+ end
+ admin.text_asset = load_default_text_asset_regions
- # connect UserActionObserver with my models
- UserActionObserver.instance.send :add_observer!, Stylesheet
- UserActionObserver.instance.send :add_observer!, Javascript
+
+ # Add Javascript and Stylesheet to UserActionObserver (used for updated_at and updated_by)
+ observables = UserActionObserver.instance.observed_classes | [Stylesheet, Javascript]
+ UserActionObserver.send :observe, observables
+ UserActionObserver.instance.send :add_observer!, Stylesheet
+ UserActionObserver.instance.send :add_observer!, Javascript
- # activate TextAssetObserver (can't be set via config.active_record.observer)
+ # Activate TextAssetObserver (can't be set via config.active_record.observer)
TextAssetObserver.instance.send :add_observer!, Stylesheet
TextAssetObserver.insta