diff --git a/Gemfile.lock b/Gemfile.lock index 039bfde..6136b04 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -36,18 +36,20 @@ GEM activesupport (3.0.11) arel (2.0.10) builder (2.1.2) - contest (0.1.2) + contest (0.1.3) erubis (2.6.6) abstract (>= 1.0.0) i18n (0.5.0) - json (1.6.5) + json (1.7.4) mail (2.2.19) activesupport (>= 2.3.6) i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.17.2) - mocha (0.9.12) + metaclass (0.0.1) + mime-types (1.19) + mocha (0.12.2) + metaclass (~> 0.0.1) polyglot (0.3.3) rack (1.2.5) rack-mount (0.6.14) @@ -71,13 +73,13 @@ GEM rake (0.9.2.2) rdoc (3.12) json (~> 1.4) - sqlite3 (1.3.5) - test-unit (2.2.0) + sqlite3 (1.3.6) + test-unit (2.5.1) thor (0.14.6) treetop (1.4.10) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.31) + tzinfo (0.3.33) PLATFORMS ruby diff --git a/doc/README.textile b/doc/README.textile index dd63aaa..0701ea1 100644 --- a/doc/README.textile +++ b/doc/README.textile @@ -39,6 +39,20 @@ h3. Additional Instructions for using the Rails Asset Pipeline In order to use themes_for_rails with the asset pipeline, you will need to configure a few settings and place your themes in the asset pipeline directory. +You will need to enable asset digests in your rails app. This is normally enabled in production mode but you can add it to your development environment to +test locally. + +
+# config/environments/development.rb
+
+config.assets.digest = true
+
+
+ +h4. PLEASE ADD themes_for_rails TO THE ASSETS GROUP IN YOUR GEMFILE! +In order to automatically add the themes to your asset pipeline you need to add the themes_for_rails gem to the assets group in your Gemfile. + + First, move your assets into the asset pipeline. For example:
@@ -50,9 +64,11 @@ $app_root
       stylesheets/ <-- default asset pipeline folder
       themes/      <-- your themes root
         [theme_name]
-          images/
-          stylesheets/
-          javascripts/
+          assets/          <-- this is your theme_assets_dir
+            [theme_name]   <-- this ensures that the assets are namespaced and don't clobber eachother.
+              images/
+              stylesheets/
+              javascripts/
           views/           <- you can override application views
             layouts/         <- layout .rhtml or .liquid templates
 
@@ -64,8 +80,7 @@ Create an initializer for themes in your {Rails.root}/config/initializers direct ThemesForRails.config do |config| # # If you have placed your themes like the example path above within the asset pipeline: - config.themes_dir = 'assets' - config.assets_dir = 'app/assets/themes' + config.assets_dir = 'app/assets/themes/:name/assets' # ... end @@ -94,9 +109,11 @@ $app_root stylesheets/ <-- default asset pipeline folder themes/ <-- your themes root [theme_name] - images/ - stylesheets/ - javascripts/ + assets/ <-- this is your theme_assets_dir + [theme_name] <-- this ensures that the assets are namespaced and don't clobber eachother. + images/ + stylesheets/ + javascripts/ views/ themes/ <-- note themes folder lives under views in this scenario [theme_name] @@ -111,7 +128,7 @@ ThemesForRails.config do |config| # # If you have placed your themes like the example path above within the asset pipeline: config.themes_dir = 'assets' - config.assets_dir = 'app/assets/themes' + config.assets_dir = 'app/assets/themes/:name/assets' config.views_dir = 'app/views/themes' # ... end @@ -207,15 +224,15 @@ h3. Url Helpers In your views you should be able to access your assets like this (given the theme 'default' is set):
-current_theme_image_path('logo.png')   # => /themes/default/images/logo.png
-current_theme_stylesheet_path('style') # => /themes/default/stylesheets/logo.css
-current_theme_javascript_path('app')   # => /themes/default/stylesheets/app.js
+current_theme_image_path('logo.png')   # => /assets/default/images/logo.png
+current_theme_stylesheet_path('style') # => /assets/default/stylesheets/logo.css
+current_theme_javascript_path('app')   # => /assets/default/stylesheets/app.js
 
Or a given theme:
-current_theme_image_path('logo.png', 'purple')   # => /themes/purple/images/logo.png
+current_theme_image_path('logo.png', 'purple')   # => /assets/purple/images/logo.png
 
In your application views, there are theme specific helper tags @@ -360,4 +377,4 @@ h2. Last but not least If you are using this gem, please, take a minute to recommend me at Working With Rails. -Recommend Me \ No newline at end of file +Recommend Me diff --git a/lib/tasks/themes_for_rails.rake b/lib/tasks/themes_for_rails.rake index 0708bc0..e19a9fa 100644 --- a/lib/tasks/themes_for_rails.rake +++ b/lib/tasks/themes_for_rails.rake @@ -19,4 +19,22 @@ namespace :themes do end desc "Updates the cached (public) theme folders" task :update_cache => [:remove_cache, :create_cache] + + desc "Add theme view paths to ActionView PathSet" + task :add_theme_to_view_paths => :environment do + raise 'you must provide THEME for theme support to work' unless theme = ENV['THEME'] + puts "adding theme [#{theme}] to view path" + require 'themes_for_rails/common_methods' + include ThemesForRails::CommonMethods + + theme_view_path=theme_view_path_for(theme) + ActionController::Base.view_paths.paths.prepend(theme_view_path) if theme + end +end + +namespace :cache_digests do + puts "patching cache_digests with themes_for_rails support" + # Add themes support to cache_digest gem rake tasks + task :nested_dependencies => ['themes:add_theme_to_view_paths'] + task :dependencies => ['themes:add_theme_to_view_paths'] end diff --git a/lib/themes_for_rails.rb b/lib/themes_for_rails.rb index 71c1790..92fe749 100644 --- a/lib/themes_for_rails.rb +++ b/lib/themes_for_rails.rb @@ -35,7 +35,22 @@ def add_themes_path_to_sass raise "Sass is not available. What are you trying to do?" end end - + + def check_asset_pipeline + config.asset_digests_enabled ||= Rails.application.config.respond_to?(:assets) && Rails.application.config.assets.digest == true + end + + def add_themes_assets_to_asset_pipeline + Rails.logger.info "Start adding themes to assets [#{ThemesForRails.config.asset_digests_enabled?}]" + if ThemesForRails.config.asset_digests_enabled? + available_theme_names.each do |theme_name| + theme_asset_path = ThemesForRails.config.assets_dir.gsub(":root", ThemesForRails.config.base_dir).gsub(":name", theme_name.to_s) + Rails.logger.info "== Adding theme [#{theme_name}] asset dir [#{theme_asset_path}] to asset pipeline" + Rails.application.config.assets.paths.prepend(theme_asset_path) unless Rails.application.config.assets.paths.include?(theme_asset_path) + end unless ThemesForRails.config.base_dir =~ %r!/app/assets/! + end + end + def already_configured_in_sass?(sass_dir) Sass::Plugin.template_location_array.map(&:first).include?(sass_dir) end diff --git a/lib/themes_for_rails/action_mailer.rb b/lib/themes_for_rails/action_mailer.rb index 438ae18..f2431f3 100644 --- a/lib/themes_for_rails/action_mailer.rb +++ b/lib/themes_for_rails/action_mailer.rb @@ -11,7 +11,7 @@ module ActionMailer end def mail_with_theme(headers = {}, &block) - theme_opts = headers[:theme] || self.class.default[:theme] + theme_opts = headers[:theme] || headers['X-theme'] || self.class.default[:theme] theme(theme_opts) if theme_opts mail_without_theme(headers, &block) diff --git a/lib/themes_for_rails/action_view.rb b/lib/themes_for_rails/action_view.rb index 61c2553..b9a2bfc 100644 --- a/lib/themes_for_rails/action_view.rb +++ b/lib/themes_for_rails/action_view.rb @@ -1,6 +1,6 @@ # encoding: utf-8 module ThemesForRails - + module ActionView extend ActiveSupport::Concern @@ -10,35 +10,39 @@ module ActionView end def current_theme_stylesheet_path(asset) - base_theme_stylesheet_path(:theme => self.theme_name, :asset => "#{asset}.css") + base_theme_stylesheet_path(:theme => self.theme_name, :asset => digest_for_stylesheet("#{asset}.css", self.theme_name)) end - + def current_theme_javascript_path(asset) - base_theme_javascript_path(:theme => self.theme_name, :asset => "#{asset}.js") + base_theme_javascript_path(:theme => self.theme_name, :asset => digest_for_javascript("#{asset}.js", self.theme_name)) end def current_theme_image_path(asset) - base_theme_image_path(:theme => self.theme_name, :asset => asset) + image, extension = name_ext(asset) + base_theme_image_path(:theme => self.theme_name, :asset => digest_for_image("#{image}.#{extension}", self.theme_name)) end def theme_stylesheet_path(asset, new_theme_name = self.theme_name) - base_theme_stylesheet_path(:theme => new_theme_name, :asset => "#{asset}.css") + base_theme_stylesheet_path(:theme => new_theme_name, :asset => digest_for_stylesheet("#{asset}.css", new_theme_name)) end def theme_javascript_path(asset, new_theme_name = self.theme_name) - base_theme_javascript_path(:theme => new_theme_name, :asset => "#{asset}.js") + base_theme_javascript_path(:theme => new_theme_name, :asset => digest_for_javascript("#{asset}.js", new_theme_name)) end def theme_image_path(asset, new_theme_name = self.theme_name) - base_theme_image_path(:theme => new_theme_name, :asset => asset) + image, extension = name_ext(asset) + base_theme_image_path(:theme => new_theme_name, :asset => digest_for_image("#{image}.#{extension}", new_theme_name)) end - + def theme_image_tag(source, options = {}) - image_tag(theme_image_path(source), options) + image, extension = name_ext(source) + image_tag(theme_image_path(digest_for_image("#{image}.#{extension}", self.theme_name)), options) end def theme_image_submit_tag(source, options = {}) - image_submit_tag(theme_image_path(source), options) + image, extension = name_ext(source) + image_submit_tag(theme_image_path(digest_for_image("#{image}.#{extension}", self.theme_name)), options) end def theme_javascript_include_tag(*files) diff --git a/lib/themes_for_rails/common_methods.rb b/lib/themes_for_rails/common_methods.rb index c7fe876..b2f41da 100644 --- a/lib/themes_for_rails/common_methods.rb +++ b/lib/themes_for_rails/common_methods.rb @@ -7,7 +7,7 @@ module CommonMethods def theme_name @cached_theme_name ||= begin case @theme_name - when Symbol then + when Symbol then self.respond_to?(@theme_name, true) ? self.send(@theme_name) : @theme_name.to_s when String then @theme_name else @@ -15,7 +15,7 @@ def theme_name end end end - + def theme_name=(name) @theme_name = name end @@ -26,23 +26,55 @@ def set_theme(name) add_theme_view_path end end - + public - + def valid_theme? !self.theme_name.nil? end - + # will add the view path for the current theme def add_theme_view_path add_theme_view_path_for(self.theme_name) end - + # will add the view path for a given theme name def add_theme_view_path_for(name) self.view_paths.insert 0, ::ActionView::FileSystemResolver.new(theme_view_path_for(name)) end + def digest_for_image(asset, theme_context) + if ThemesForRails.config.asset_digests_enabled? + asset_paths.digest_for("#{theme_context}/images/#{asset}") || asset + else + asset + end + end + + def digest_for_javascript(asset, theme_context) + if ThemesForRails.config.asset_digests_enabled? + asset_paths.digest_for("#{theme_context}/javascripts/#{asset}") || asset + else + asset + end + end + + def digest_for_stylesheet(asset, theme_context) + if ThemesForRails.config.asset_digests_enabled? + #Rails.application.config.assets.digests["#{theme_context}/stylesheets/#{asset}"] || asset + asset_paths.digest_for("#{theme_context}/stylesheets/#{asset}") || asset + else + asset + end + end + + def name_ext(file_name) + ext = File.extname(file_name) + name = File.basename(file_name, ext) + ext.slice!(0) if ext.length > 0 + return name, ext + end + def public_theme_path theme_view_path("/") end @@ -63,4 +95,4 @@ def theme_asset_path_for(theme_name) interpolate(ThemesForRails.config.assets_dir, theme_name) end end -end \ No newline at end of file +end diff --git a/lib/themes_for_rails/config.rb b/lib/themes_for_rails/config.rb index ad0360d..9a6064f 100644 --- a/lib/themes_for_rails/config.rb +++ b/lib/themes_for_rails/config.rb @@ -3,13 +3,14 @@ module ThemesForRails class Config attr_writer :base_dir, :themes_dir, :assets_dir, :views_dir, :themes_routes_dir - attr_accessor :use_sass, :default_theme + attr_accessor :use_sass, :default_theme, :asset_digests_enabled include Interpolation def initialize(&block) @use_sass = true @default_theme = 'default' + @asset_digests_enabled = nil yield if block_given? end @@ -70,5 +71,9 @@ def use_sass? def sass_is_available? !!defined?Sass::Plugin end + + def asset_digests_enabled? + @asset_digests_enabled + end end -end \ No newline at end of file +end diff --git a/lib/themes_for_rails/digested_action_view.rb b/lib/themes_for_rails/digested_action_view.rb new file mode 100644 index 0000000..4276a8c --- /dev/null +++ b/lib/themes_for_rails/digested_action_view.rb @@ -0,0 +1,64 @@ +# encoding: utf-8 +module ThemesForRails + + module DigestedActionView + + extend ActiveSupport::Concern + + included do + include ThemesForRails::CommonMethods + end + + def current_theme_stylesheet_path(asset) + digest_for_stylesheet("#{asset}.css", self.theme_name) + end + + def current_theme_javascript_path(asset) + digest_for_javascript("#{asset}.js", self.theme_name) + end + + def current_theme_image_path(asset) + image, extension = name_ext(asset) + digest_for_image("#{image}.#{extension}", self.theme_name) + end + + def theme_stylesheet_path(asset, new_theme_name = self.theme_name) + digest_for_stylesheet("#{asset}.css", new_theme_name) + end + + def theme_javascript_path(asset, new_theme_name = self.theme_name) + digest_for_javascript("#{asset}.js", new_theme_name) + end + + def theme_image_path(asset, new_theme_name = self.theme_name) + path_to_image(digest_for_image(asset, new_theme_name)) + end + + def theme_image_tag(source, options = {}) + image_tag(theme_image_path("#{source}", self.theme_name), options) + end + + def theme_image_submit_tag(source, options = {}) + image, extension = name_ext(source) + image_submit_tag(theme_image_path("#{image}.#{extension}", self.theme_name), options) + end + + def theme_javascript_include_tag(*files) + options = files.extract_options! + options.merge!({ :type => "text/javascript" }) + files_with_options = files.collect {|file| theme_javascript_path(file) } + files_with_options += [options] + + javascript_include_tag(*files_with_options) + end + + def theme_stylesheet_link_tag(*files) + options = files.extract_options! + options.merge!({ :type => "text/css" }) + files_with_options = files.collect {|file| theme_stylesheet_path(file) } + files_with_options += [options] + + stylesheet_link_tag(*files_with_options) + end + end +end diff --git a/lib/themes_for_rails/railtie.rb b/lib/themes_for_rails/railtie.rb index 276446d..bc98bc7 100644 --- a/lib/themes_for_rails/railtie.rb +++ b/lib/themes_for_rails/railtie.rb @@ -9,11 +9,21 @@ class Railtie < ::Rails::Railtie ThemesForRails.config.send "#{key}=".to_sym, value end - # Adding theme stylesheets path to sass, automatically. + # Adding theme stylesheets path to sass, automatically. ThemesForRails.add_themes_path_to_sass if ThemesForRails.config.use_sass? - + + # Check if asset pipeline enabled + ThemesForRails.check_asset_pipeline + + # Adding theme assets to the asset pipeline, automatically. + ThemesForRails.add_themes_assets_to_asset_pipeline if ThemesForRails.config.asset_digests_enabled? + ActiveSupport.on_load(:action_view) do include ThemesForRails::ActionView + if ThemesForRails.config.asset_digests_enabled? + require 'themes_for_rails/digested_action_view' + include ThemesForRails::DigestedActionView + end end ActiveSupport.on_load(:action_controller) do diff --git a/lib/themes_for_rails/routes.rb b/lib/themes_for_rails/routes.rb index d52301b..192848e 100644 --- a/lib/themes_for_rails/routes.rb +++ b/lib/themes_for_rails/routes.rb @@ -6,12 +6,15 @@ def themes_for_rails theme_dir = ThemesForRails.config.themes_routes_dir constraints = { :theme => /[\w\.]*/ } - match "#{theme_dir}/:theme/stylesheets/*asset" => 'themes_for_rails/assets#stylesheets', - :as => :base_theme_stylesheet, :constraints => constraints - match "#{theme_dir}/:theme/javascripts/*asset" => 'themes_for_rails/assets#javascripts', - :as => :base_theme_javascript, :constraints => constraints - match "#{theme_dir}/:theme/images/*asset" => 'themes_for_rails/assets#images', - :as => :base_theme_image, :constraints => constraints + # Lets not pollute the routes if they aren't being used. + unless ThemesForRails.config.asset_digests_enabled? + match "#{theme_dir}/:theme/stylesheets/*asset" => 'themes_for_rails/assets#stylesheets', + :as => :base_theme_stylesheet, :constraints => constraints + match "#{theme_dir}/:theme/javascripts/*asset" => 'themes_for_rails/assets#javascripts', + :as => :base_theme_javascript, :constraints => constraints + match "#{theme_dir}/:theme/images/*asset" => 'themes_for_rails/assets#images', + :as => :base_theme_image, :constraints => constraints + end end end diff --git a/lib/themes_for_rails/url_helpers.rb b/lib/themes_for_rails/url_helpers.rb index 43dbc47..096e6ac 100644 --- a/lib/themes_for_rails/url_helpers.rb +++ b/lib/themes_for_rails/url_helpers.rb @@ -3,24 +3,24 @@ module ThemesForRails module UrlHelpers extend ActiveSupport::Concern - + included do - helper_method :current_theme_stylesheet_path, - :current_theme_javascript_path, + helper_method :current_theme_stylesheet_path, + :current_theme_javascript_path, :current_theme_image_path end - + def current_theme_stylesheet_path(asset) - base_theme_stylesheet_path(:theme => self.theme_name, :asset => "#{asset}.css") + base_theme_stylesheet_path(:theme => self.theme_name, :asset => digest_for_stylesheet("#{asset}.css", self.theme_name)) end def current_theme_javascript_path(asset) - base_theme_javascript_path(:theme => self.theme_name, :asset => "#{asset}.js") + base_theme_javascript_path(:theme => self.theme_name, :asset => digest_for_javascript("#{asset}.js", self.theme_name)) end def current_theme_image_path(asset) - image, extension = asset.split(".") - base_theme_image_path(:theme => self.theme_name, :asset => "#{image}.#{extension}") + image, extension = name_ext(asset) + base_theme_image_path(:theme => self.theme_name, :asset => digest_for_image("#{image}.#{extension}", self.theme_name)) end end