Permalink
Browse files

Initial Commit

  • Loading branch information...
chrisparrish committed Oct 18, 2008
0 parents commit 273848ff347dfe0578e5a690b6ff60ad6bccc0c2
Showing with 7,275 additions and 0 deletions.
  1. +21 −0 LICENSE
  2. +52 −0 README
  3. +120 −0 Rakefile
  4. +8 −0 app/controllers/admin/css_controller.rb
  5. +8 −0 app/controllers/admin/js_controller.rb
  6. +23 −0 app/controllers/admin/text_asset_controller.rb
  7. +49 −0 app/controllers/text_asset_site_controller.rb
  8. +2 −0 app/helpers/admin/css_helper.rb
  9. +2 −0 app/helpers/admin/js_helper.rb
  10. +2 −0 app/helpers/text_asset_site_helper.rb
  11. +11 −0 app/models/css_asset.rb
  12. +11 −0 app/models/js_asset.rb
  13. +14 −0 app/models/text_asset.rb
  14. +12 −0 app/models/text_asset_response_cache.rb
  15. +36 −0 app/views/admin/css/edit.html.erb
  16. +36 −0 app/views/admin/css/index.html.erb
  17. +17 −0 app/views/admin/css/remove.html.erb
  18. +37 −0 app/views/admin/js/edit.html.erb
  19. +36 −0 app/views/admin/js/index.html.erb
  20. +17 −0 app/views/admin/js/remove.html.erb
  21. +2 −0 config/environment.rb
  22. +34 −0 custom_settings.rb
  23. +16 −0 db/migrate/001_create_text_assets.rb
  24. +119 −0 lib/cssmin.rb
  25. +233 −0 lib/jsmin.rb
  26. +81 −0 lib/styles_n_scripts/config.rb
  27. +28 −0 lib/tasks/styles_n_scripts_extension_tasks.rake
  28. BIN public/images/admin/javascript.png
  29. BIN public/images/admin/new-javascript.png
  30. BIN public/images/admin/new-stylesheet.png
  31. BIN public/images/admin/stylesheet.png
  32. +307 −0 spec/controllers/admin/css_and_js_controllers_common_spec.rb
  33. +3 −0 spec/controllers/admin/css_controller_spec.rb
  34. +3 −0 spec/controllers/admin/js_controller_spec.rb
  35. +190 −0 spec/controllers/text_asset_site_controller_spec.rb
  36. +11 −0 spec/helpers/admin/css_helper_spec.rb
  37. +11 −0 spec/helpers/admin/js_helper_spec.rb
  38. +11 −0 spec/helpers/text_asset_site_helper_spec.rb
  39. +180 −0 spec/lib/config_spec.rb
  40. +95 −0 spec/models/css_asset_and_js_asset_common_spec.rb
  41. +34 −0 spec/models/css_asset_minify_spec.rb
  42. +25 −0 spec/models/js_asset_minify_spec.rb
  43. +98 −0 spec/models/text_asset_response_cache_spec.rb
  44. +4,184 −0 spec/samples/prototype.js
  45. +264 −0 spec/samples/prototype.minified.js
  46. +624 −0 spec/samples/radiant.css
  47. +1 −0 spec/samples/radiant.minified.css
  48. +12 −0 spec/samples/test.css
  49. +1 −0 spec/samples/test.minified.css
  50. +47 −0 spec/scenarios/javascripts_scenario.rb
  51. +47 −0 spec/scenarios/stylesheets_scenario.rb
  52. +6 −0 spec/spec.opts
  53. +37 −0 spec/spec_helper.rb
  54. +57 −0 styles_n_scripts_extension.rb
21 LICENSE
@@ -0,0 +1,21 @@
+== MIT License
+
+Copyright (c) 2008, Swank Innovations, LLC.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
52 README
@@ -0,0 +1,52 @@
+= Styles 'n Scripts Extension
+
+The files extension is a proof-of-concept suggested by John Long
+as a means of separating javascripts & stylesheets from other
+site content stored in pages.
+
+
+WHY CHANGE THINGS?
+==================
+As John sees it, the pages tab is really for the main content.
+(Think of it as the list of available destinations for your users)
+Sure, they need stylesheets and such but those are supporting files
+that they need to view your pages.
+
+There are a number of interesting benefits gained by this approach:
+
+ * These files really deserve designer-level permissions no user-
+ level. Well, they just got their own tabs.
+
+ * These files should be cached differently. Rather than the 5
+ minute page cache, we now have essentially infinite caching.
+
+ * This frees up pages to offer fields like <meta keywords...> that
+ make absolutely no sense for, say, a stylesheet page.
+
+ * This opens the door for minification and obfuscation of scripts
+ and stylesheets (I've incorporated JSMin and a stylesheet minifier
+ in the initial release. Future versions may include Dean Edwards Packer or
+ other more advanced compressors).
+
+ * This lays a conceptual foundation for other, non-text assets like images,
+ flash, and the like.
+
+
+
+
+TO-DO
+=====
+Create a way for users to upload stylesheets and javascripts (maybe even many in
+a zipped file) and unpack them into the db.
+
+Improve caching (I'd like to see a scenario where files are cached on save (or
+even just Rails page caching. Maintaining a server-ready cache that bypasses
+ruby wouldn't be all that hard for these items.).
+
+Create <r:stylesheet> (or maybe <r:text_asset>) tag to let users reference
+stylesheets from pages, snippets, and layouts.
+
+Create a tagging mechanism for combining stylesheets or scripts into a single
+file to reduce the number of requests and speed serving.
+
+Figure out what the core team needs to get this puppy baked in with Radiant!
120 Rakefile
@@ -0,0 +1,120 @@
+# I think this is the one that should be moved to the extension Rakefile template
+
+# In rails 1.2, plugins aren't available in the path until they're loaded.
+# Check to see if the rspec plugin is installed first and require
+# it if it is. If not, use the gem version.
+
+# Determine where the RSpec plugin is by loading the boot
+unless defined? RADIANT_ROOT
+ ENV["RAILS_ENV"] = "test"
+ case
+ when ENV["RADIANT_ENV_FILE"]
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
+ else
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
+ end
+end
+
+require 'rake'
+require 'rake/rdoctask'
+require 'rake/testtask'
+
+rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
+$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
+require 'spec/rake/spectask'
+# require 'spec/translator'
+
+# Cleanup the RADIANT_ROOT constant so specs will load the environment
+Object.send(:remove_const, :RADIANT_ROOT)
+
+extension_root = File.expand_path(File.dirname(__FILE__))
+
+task :default => :spec
+task :stats => "spec:statsetup"
+
+desc "Run all specs in spec directory"
+Spec::Rake::SpecTask.new(:spec) do |t|
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
+ t.spec_files = FileList['spec/**/*_spec.rb']
+end
+
+namespace :spec do
+ desc "Run all specs in spec directory with RCov"
+ Spec::Rake::SpecTask.new(:rcov) do |t|
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.rcov = true
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
+ end
+
+ desc "Print Specdoc for all specs"
+ Spec::Rake::SpecTask.new(:doc) do |t|
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ end
+
+ [:models, :controllers, :views, :helpers].each do |sub|
+ desc "Run the specs under spec/#{sub}"
+ Spec::Rake::SpecTask.new(sub) do |t|
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
+ end
+ end
+
+ # Hopefully no one has written their extensions in pre-0.9 style
+ # desc "Translate specs from pre-0.9 to 0.9 style"
+ # task :translate do
+ # translator = ::Spec::Translator.new
+ # dir = RAILS_ROOT + '/spec'
+ # translator.translate(dir, dir)
+ # end
+
+ # Setup specs for stats
+ task :statsetup do
+ require 'code_statistics'
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
+ ::CodeStatistics::TEST_TYPES << "Model specs"
+ ::CodeStatistics::TEST_TYPES << "View specs"
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
+ end
+
+ namespace :db do
+ namespace :fixtures do
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
+ task :load => :environment do
+ require 'active_record/fixtures'
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
+ end
+ end
+ end
+ end
+end
+
+desc 'Generate documentation for the styles_n_scripts extension.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'StylesNScriptsExtension'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+# For extensions that are in transition
+desc 'Test the styles_n_scripts extension.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+# Load any custom rakefiles for extension
+Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
@@ -0,0 +1,8 @@
+class Admin::CssController < Admin::TextAssetController
+ model_class CssAsset
+
+ only_allow_access_to :index, :new, :edit, :remove,
+ :when => [:developer, :admin],
+ :denied_url => { :controller => 'page', :action => 'index' },
+ :denied_message => 'You must have developer privileges to perform this action.'
+end
@@ -0,0 +1,8 @@
+class Admin::JsController < Admin::TextAssetController
+ model_class JsAsset
+
+ only_allow_access_to :index, :new, :edit, :remove,
+ :when => [:developer, :admin],
+ :denied_url => { :controller => 'page', :action => 'index' },
+ :denied_message => 'You must have developer privileges to perform this action.'
+end
@@ -0,0 +1,23 @@
+class Admin::TextAssetController < Admin::AbstractModelController
+
+ def initialize
+ super
+ @cache = TextAssetResponseCache.instance
+ end
+
+ def new
+ self.model = model_class.new
+ render :action => "edit" if handle_new_or_edit_post
+ end
+
+ def remove
+ self.model = model_class.find(params[:id])
+ if request.post?
+ model.destroy
+ announce_removed
+ clear_model_cache
+ redirect_to model_index_url # <-- Added this line to clear cache on remove
+ end
+ end
+
+end
@@ -0,0 +1,49 @@
+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_class], url)
+ end
+ end
+
+
+ def show_uncached_text_asset(filename, asset_class, url)
+ case asset_class
+ when 'css_asset'
+ text_asset = CssAsset.find_by_filename(filename)
+ mime_type = StylesNScripts::Config[:css_mime_type]
+ when 'js_asset'
+ text_asset = JsAsset.find_by_filename(filename)
+ mime_type = StylesNScripts::Config[:js_mime_type]
+ end
+ unless text_asset.nil?
+ response.headers['Content-Type'] = mime_type
+ response.body = text_asset.content
+
+ # for text_assets, we cache no matter what (there's 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
@@ -0,0 +1,2 @@
+module Admin::CssHelper
+end
@@ -0,0 +1,2 @@
+module Admin::JsHelper
+end
@@ -0,0 +1,2 @@
+module TextAssetSiteHelper
+end
@@ -0,0 +1,11 @@
+class CssAsset < TextAsset
+
+ def content
+ if self.minify?
+ CSSMin.minify(self.raw_content)
+ else
+ super
+ end
+ end
+
+end
@@ -0,0 +1,11 @@
+class JsAsset < TextAsset
+
+ def content
+ if self.minify?
+ JSMin.minify(self.raw_content)
+ else
+ super
+ end
+ end
+
+end
@@ -0,0 +1,14 @@
+class TextAsset < ActiveRecord::Base
+ set_inheritance_column :class_name
+
+ validates_presence_of :filename, :message => 'required'
+ validates_length_of :filename, :maximum => 100, :message => '%d-character limit'
+ validates_uniqueness_of :filename, :scope => :class_name, :message => 'filename already in use'
+ # 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'
+
+ def content
+ self.raw_content
+ end
+
+end
@@ -0,0 +1,12 @@
+class TextAssetResponseCache < ResponseCache
+ def initialize(options={})
+ @@defaults[:directory] = "#{RAILS_ROOT}/#{StylesNScripts::Config[:response_cache_directory]}"
+ @@defaults[:expire_time] = 1.year
+ super(options)
+ end
+
+ def self.instance
+ # can't use @@instance as this class is inherited
+ @@tarc_instance ||= new
+ end
+end
@@ -0,0 +1,36 @@
+<% if @css_asset.new_record? -%>
+<h1>New Stylesheet</h1>
+<% else -%>
+<h1>Edit Stylesheet</h1>
+<% end -%>
+
+<form method="post" action="">
+ <%= hidden_field "css_asset", "lock_version" %>
+ <div class="form-area">
+ <p class="title">
+ <label for="css_asset_filename">Filename</label>
+ <%= text_field "css_asset", "filename", :class => 'textbox', :maxlength => 100 %>
+ </p>
+
+ <p class="content">
+ <label for="css_asset_raw_content">Body</label>
+ <%= text_area "css_asset", "raw_content", :class => "textarea", :style => "width: 100%" %>
+ </p>
+
+ <div class="row">
+ <p>
+ <%= check_box("css_asset", "minify") %>
+ <label for="css_asset_minify">Minify Output?</label>
+ </p>
+ </div>
+
+ <span class="clear">&nbsp;</span>
+
+ <%= updated_stamp @css_asset %>
+ </div>
+ <p class="buttons">
+ <%= submit_tag(@css_asset.new_record? ? 'Create Stylesheet' : 'Save Changes', :class => 'button') %> <%= save_model_and_continue_editing_button(@css_asset) %> or <%= link_to "Cancel", css_asset_index_url %>
+ </p>
+</form>
+
+<%= focus 'css_asset_filename' %>
Oops, something went wrong.

0 comments on commit 273848f

Please sign in to comment.