diff --git a/.travis.yml b/.travis.yml index 973cd828..3f6d0093 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ rvm: - 1.8.7 - 1.9.2 - 1.9.3 - - jruby script: "bundle exec rake test" branches: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d86ee96..6bb9df06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * Serve purely static folders directly (without source/ and config.rb) * Set ignored files and disable directory_indexes from YAML frontmatter * Automatically load helper modules in helpers/ directory +* Add pid for cleanup 2.0.14 ==== diff --git a/Gemfile b/Gemfile index 59a0f87a..3a355227 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ group :development do gem "rake", "~> 0.9.2" gem "rspec", "~> 2.7" gem "rdoc", "~> 3.9" - gem "yard" + # gem "yard" end group :test do @@ -15,8 +15,9 @@ group :test do gem "slim" gem "coffee-filter", "~> 0.1.1" gem "liquid", "~> 2.2" - gem "jquery-rails" - gem "bootstrap-rails", "0.0.5" + gem "jquery-rails", "~> 2.0.0" + # gem "bootstrap-rails", "0.0.5" + # gem "zurb-foundation" end gem "middleman-core", :path => "middleman-core" diff --git a/README.md b/README.md index 3dc76eff..11af304e 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,9 @@ http://rubydoc.info/github/middleman/middleman # Community -The official community forum is available on Convore at: +The official community forum is available at: - https://convore.com/middleman/ + http://forum.middlemanapp.com/ # Bug Reports diff --git a/Rakefile b/Rakefile index 47e21e80..1435472a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ require 'rubygems' unless defined?(Gem) # require 'fileutils' unless defined?(FileUtils) require 'rake' -require 'yard' +# require 'yard' require File.expand_path("../middleman-core/lib/middleman-core/version.rb", __FILE__) @@ -84,7 +84,7 @@ end desc "Run tests for all middleman gems" task :default => :test -desc "Generate documentation" -task :doc do - YARD::CLI::Yardoc.new.run -end \ No newline at end of file +# desc "Generate documentation" +# task :doc do +# YARD::CLI::Yardoc.new.run +# end \ No newline at end of file diff --git a/gem_rake_helper.rb b/gem_rake_helper.rb index d4fe0f51..fe3ea4b4 100644 --- a/gem_rake_helper.rb +++ b/gem_rake_helper.rb @@ -1,7 +1,7 @@ require 'rubygems' unless defined?(Gem) require 'rake' require 'cucumber/rake/task' -require 'yard' +# require 'yard' require 'bundler' Bundler::GemHelper.install_tasks :name => GEM_NAME @@ -20,6 +20,6 @@ Cucumber::Rake::Task.new(:test, 'Run features that should pass') do |t| t.cucumber_opts = "--color --tags ~@wip --strict --format #{ENV['CUCUMBER_FORMAT'] || 'pretty'}" end -YARD::Rake::YardocTask.new +# YARD::Rake::YardocTask.new task :default => :test \ No newline at end of file diff --git a/middleman-core/features/builder.feature b/middleman-core/features/builder.feature index 2bc6dc62..f833b8bc 100644 --- a/middleman-core/features/builder.feature +++ b/middleman-core/features/builder.feature @@ -17,7 +17,6 @@ Feature: Builder Then the following files should not exist: | _partial | | layout | - | other_layout | | layouts/custom | | layouts/content_for | diff --git a/middleman-core/features/extensionless_text_files.feature b/middleman-core/features/extensionless_text_files.feature new file mode 100644 index 00000000..a544f2e0 --- /dev/null +++ b/middleman-core/features/extensionless_text_files.feature @@ -0,0 +1,58 @@ +Feature: Text Files Without Extensions Should Build and Preview + + Scenario: Building Text Files without directory indexes + + Given a fixture app "extensionless-text-files-app" + And a file named "config.rb" with: + """ + """ + And a successfully built app at "extensionless-text-files-app" + When I cd to "build" + Then the following files should exist: + | CNAME | + | LICENSE | + | README | + + Scenario: Building Text Files with directory indexes + + Given a fixture app "extensionless-text-files-app" + And a file named "config.rb" with: + """ + activate :directory_indexes + """ + And a successfully built app at "extensionless-text-files-app" + When I cd to "build" + Then the following files should exist: + | CNAME | + | LICENSE | + | README | + Then the following files should not exist: + | CNAME/index.html | + | LICENSE/index.html | + | README/index.html | + + Scenario: Previewing Text Files without directory indexes + Given "directory_indexes" feature is "disabled" + Given the Server is running at "extensionless-text-files-app" + When I go to "/CNAME" + Then I should see "test.github.com" + When I go to "/LICENSE" + Then I should see "You have the right to remain classy." + When I go to "/README" + Then I should see "Bork bork bork" + + Scenario: Previewing Text Files with directory indexes + Given "directory_indexes" feature is "enabled" + Given the Server is running at "extensionless-text-files-app" + When I go to "/CNAME" + Then I should see "test.github.com" + When I go to "/LICENSE" + Then I should see "You have the right to remain classy." + When I go to "/README" + Then I should see "Bork bork bork" + # When I go to "/CNAME/index.html" + # Then I should see "File Not Found" + # When I go to "/LICENSE/index.html" + # Then I should see "File Not Found" + # When I go to "/README/index.html" + # Then I should see "File Not Found" \ No newline at end of file diff --git a/middleman-core/fixtures/extensionless-text-files-app/config.rb b/middleman-core/fixtures/extensionless-text-files-app/config.rb new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/fixtures/extensionless-text-files-app/source/CNAME b/middleman-core/fixtures/extensionless-text-files-app/source/CNAME new file mode 100644 index 00000000..c8dc4813 --- /dev/null +++ b/middleman-core/fixtures/extensionless-text-files-app/source/CNAME @@ -0,0 +1 @@ +test.github.com \ No newline at end of file diff --git a/middleman-core/fixtures/extensionless-text-files-app/source/LICENSE b/middleman-core/fixtures/extensionless-text-files-app/source/LICENSE new file mode 100644 index 00000000..8bf82c48 --- /dev/null +++ b/middleman-core/fixtures/extensionless-text-files-app/source/LICENSE @@ -0,0 +1 @@ +You have the right to remain classy. \ No newline at end of file diff --git a/middleman-core/fixtures/extensionless-text-files-app/source/README b/middleman-core/fixtures/extensionless-text-files-app/source/README new file mode 100644 index 00000000..44c2bac0 --- /dev/null +++ b/middleman-core/fixtures/extensionless-text-files-app/source/README @@ -0,0 +1 @@ +Bork bork bork \ No newline at end of file diff --git a/middleman-core/fixtures/extensionless-text-files-app/source/index.html b/middleman-core/fixtures/extensionless-text-files-app/source/index.html new file mode 100644 index 00000000..0b90e2b8 --- /dev/null +++ b/middleman-core/fixtures/extensionless-text-files-app/source/index.html @@ -0,0 +1 @@ +sup \ No newline at end of file diff --git a/middleman-core/fixtures/frontmatter-settings-app/source/alternate.erb b/middleman-core/fixtures/frontmatter-settings-app/source/layouts/alternate.erb similarity index 100% rename from middleman-core/fixtures/frontmatter-settings-app/source/alternate.erb rename to middleman-core/fixtures/frontmatter-settings-app/source/layouts/alternate.erb diff --git a/middleman-core/lib/middleman-core.rb b/middleman-core/lib/middleman-core.rb index b8053dcd..09403671 100755 --- a/middleman-core/lib/middleman-core.rb +++ b/middleman-core/lib/middleman-core.rb @@ -18,76 +18,78 @@ end # Simple callback library require "middleman-core/vendor/hooks-0.2.0/lib/hooks" +require "middleman-core/version" + # Top-level Middleman object module Middleman WINDOWS = !!(RUBY_PLATFORM =~ /(mingw|bccwin|wince|mswin32)/i) unless const_defined?(:WINDOWS) JRUBY = !!(RbConfig::CONFIG["RUBY_INSTALL_NAME"] =~ /^jruby/i) unless const_defined?(:JRUBY) DARWIN = RbConfig::CONFIG['target_os'] =~ /darwin/i unless const_defined?(:DARWIN) LINUX = RbConfig::CONFIG['target_os'] =~ /linux/i unless const_defined?(:LINUX) - + # Auto-load modules on-demand autoload :Base, "middleman-core/base" autoload :Cache, "middleman-core/cache" autoload :Templates, "middleman-core/templates" autoload :Watcher, "middleman-core/watcher" - + module Cli autoload :Base, "middleman-core/cli" autoload :Build, "middleman-core/cli/build" autoload :Init, "middleman-core/cli/init" autoload :Server, "middleman-core/cli/server" end - + # Custom Renderers module Renderers autoload :ERb, "middleman-core/renderers/erb" end - + module Sitemap autoload :Store, "middleman-core/sitemap/store" autoload :Page, "middleman-core/sitemap/page" autoload :Template, "middleman-core/sitemap/template" end - + module CoreExtensions # File Change Notifier autoload :FileWatcher, "middleman-core/core_extensions/file_watcher" - + # In-memory Sitemap autoload :Sitemap, "middleman-core/core_extensions/sitemap" - + # Add Builder callbacks autoload :Builder, "middleman-core/core_extensions/builder" - + # Custom Feature API autoload :Extensions, "middleman-core/core_extensions/extensions" - + # Asset Path Pipeline autoload :Assets, "middleman-core/core_extensions/assets" - + # Data looks at the data/ folder for YAML files and makes them available # to dynamic requests. autoload :Data, "middleman-core/core_extensions/data" - + # Parse YAML from templates autoload :FrontMatter, "middleman-core/core_extensions/front_matter" - + # External helpers looks in the helpers/ folder for helper modules autoload :ExternalHelpers, "middleman-core/core_extensions/external_helpers" - + # DefaultHelpers are the built-in dynamic template helpers. autoload :DefaultHelpers, "middleman-core/core_extensions/default_helpers" # Extended version of Padrino's rendering autoload :Rendering, "middleman-core/core_extensions/rendering" - + # Pass custom options to views autoload :Routing, "middleman-core/core_extensions/routing" - + # Catch and show exceptions at the Rack level autoload :ShowExceptions, "middleman-core/core_extensions/show_exceptions" end - + module Extensions # Provide Apache-style index.html files for directories autoload :DirectoryIndexes, "middleman-core/extensions/directory_indexes" @@ -95,18 +97,18 @@ module Middleman # Lorem provides a handful of helpful prototyping methods to generate # words, paragraphs, fake images, names and email addresses. autoload :Lorem, "middleman-core/extensions/lorem" - + # AutomaticImageSizes inspects the images used in your dynamic templates # and automatically adds width and height attributes to their HTML # elements. autoload :AutomaticImageSizes, "middleman-core/extensions/automatic_image_sizes" - + # AssetHost allows you to setup multiple domains to host your static # assets. Calls to asset paths in dynamic templates will then rotate # through each of the asset servers to better spread the load. autoload :AssetHost, "middleman-core/extensions/asset_host" end - + module Extensions class << self def registered @@ -114,9 +116,9 @@ module Middleman end def register(name, namespace=nil, version=nil, &block) - # If we've already got a matching extension that passed the + # If we've already got a matching extension that passed the # version check, bail out. - return if registered.has_key?(name.to_sym) && + return if registered.has_key?(name.to_sym) && !registered[name.to_sym].is_a?(String) if block_given? @@ -154,12 +156,12 @@ module Middleman end end end - + # Where to look in gems for extensions to auto-register EXTENSION_FILE = File.join("lib", "middleman_extension.rb") unless const_defined?(:EXTENSION_FILE) - + class << self - + # Recursively convert a normal Hash into a HashWithIndifferentAccess # # @private @@ -181,7 +183,7 @@ module Middleman data end end - + # Automatically load extensions from available RubyGems # which contain the EXTENSION_FILE # @@ -190,12 +192,12 @@ module Middleman extensions = rubygems_latest_specs.select do |spec| spec_has_file?(spec, EXTENSION_FILE) end - + extensions.each do |spec| require spec.name end end - + # Backwards compatible means of finding all the latest gemspecs # available on the system # @@ -209,7 +211,7 @@ module Middleman ::Gem.source_index.latest_specs end end - + # Where a given Gem::Specification has a specific file. Used # to discover extensions and Sprockets-supporting gems. # @@ -221,9 +223,9 @@ module Middleman full_path = File.join(spec.full_gem_path, path) File.exists?(full_path) end - + # Create a new Class which is based on Middleman::Base - # Used to create a safe sandbox into which extensions and + # Used to create a safe sandbox into which extensions and # configuration can be included later without impacting # other classes and instances. # @@ -231,34 +233,34 @@ module Middleman def server(&block) Class.new(Middleman::Base) end - + # Creates a new Rack::Server # # @param [Hash] options to pass to Rack::Server.new # @return [Rack::Server] def start_server(options={}) require "webrick" - + opts = { :Port => options[:port] || 4567, :Host => options[:host] || "0.0.0.0", :AccessLog => [] } - + opts[:Logger] = WEBrick::Log::new("/dev/null", 7) if !options[:logging] - + app_class = options[:app] ||= ::Middleman.server.inst opts[:app] = app_class - + # Disable for Beta 1. See if people notice. # require "thin" # ::Thin::Logging.silent = !options[:logging] # opts[:server] = 'thin' opts[:server] = 'webrick' - + server = ::Rack::Server.new(opts) server.start server end end -end \ No newline at end of file +end diff --git a/middleman-core/lib/middleman-core/cli/build.rb b/middleman-core/lib/middleman-core/cli/build.rb index a16fd4b1..d43d29fe 100644 --- a/middleman-core/lib/middleman-core/cli/build.rb +++ b/middleman-core/lib/middleman-core/cli/build.rb @@ -197,10 +197,11 @@ module Middleman::Cli # Sort order, images, fonts, js/css and finally everything else. sort_order = %w(.png .jpeg .jpg .gif .bmp .svg .svgz .ico .woff .otf .ttf .eot .js .css) - @app.sitemap.all_paths.select do |p| - File.extname(p) == ".css" + # Pre-request CSS to give Compass a chance to build sprites + @app.sitemap.pages.select do |p| + p.ext == ".css" end.each do |p| - Middleman::Cli::Build.shared_rack.get("/" + p.gsub(/\s/, "%20")) + Middleman::Cli::Build.shared_rack.get(p.request_path.gsub(/\s/, "%20")) end # Double-check for compass sprites @@ -210,23 +211,17 @@ module Middleman::Cli # find files in the build folder when it needs to generate sprites for the # css files - # TODO: deal with pages, not paths - paths = @app.sitemap.all_paths.sort do |a, b| - a_ext = File.extname(a) - b_ext = File.extname(b) - - a_idx = sort_order.index(a_ext) || 100 - b_idx = sort_order.index(b_ext) || 100 + pages = @app.sitemap.pages.sort do |a, b| + a_idx = sort_order.index(a.ext) || 100 + b_idx = sort_order.index(b.ext) || 100 a_idx <=> b_idx end # Loop over all the paths and build them. - paths.each do |path| - page = @app.sitemap.page(path) - + pages.each do |page| next if page.ignored? - next if @config[:glob] && !File.fnmatch(@config[:glob], path) + next if @config[:glob] && !File.fnmatch(@config[:glob], page.path) base.tilt_template(page) diff --git a/middleman-core/lib/middleman-core/core_extensions/builder.rb b/middleman-core/lib/middleman-core/core_extensions/builder.rb index 7f67222b..4b901b69 100644 --- a/middleman-core/lib/middleman-core/core_extensions/builder.rb +++ b/middleman-core/lib/middleman-core/core_extensions/builder.rb @@ -6,45 +6,7 @@ module Middleman::CoreExtensions::Builder # @private def registered(app) app.define_hook :after_build - app.extend ClassMethods - app.send :include, InstanceMethods - app.delegate :build_reroute, :to => :"self.class" end alias :included :registered end - - # Build Class Methods - module ClassMethods - # Get a list of callbacks which can modify a files build path - # Each callback takes a destination path and a request path and - # returns a new destination path, or false if it doesn't want to reroute. - # @return [Array] - def build_reroute(&block) - @build_rerouters ||= [] - @build_rerouters << block if block_given? - @build_rerouters - end - end - - # Build Instance Methods - module InstanceMethods - # Run through callbacks and get the new values - # - # @param [String] destination The current destination path of the built file - # @param [String] request_path The request path of the file - # @return [String] The new destination path - def reroute_builder(destination, request_path) - result = [destination, request_path] - - build_reroute.each do |block| - output = block.call(destination, request_path) - if output - result = output - break - end - end - - result - end - end end diff --git a/middleman-core/lib/middleman-core/core_extensions/front_matter.rb b/middleman-core/lib/middleman-core/core_extensions/front_matter.rb index f1fdcb80..6437b000 100644 --- a/middleman-core/lib/middleman-core/core_extensions/front_matter.rb +++ b/middleman-core/lib/middleman-core/core_extensions/front_matter.rb @@ -187,6 +187,8 @@ module Middleman::CoreExtensions::FrontMatter def parse_front_matter(content) yaml_regex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m if content =~ yaml_regex + content = content[($1.size + $2.size)..-1] + begin data = YAML.load($1) rescue => e @@ -194,7 +196,6 @@ module Middleman::CoreExtensions::FrontMatter return false end - content = content.split(yaml_regex).last else return false end diff --git a/middleman-core/lib/middleman-core/core_extensions/rendering.rb b/middleman-core/lib/middleman-core/core_extensions/rendering.rb index cf66a2c4..31e9ffde 100644 --- a/middleman-core/lib/middleman-core/core_extensions/rendering.rb +++ b/middleman-core/lib/middleman-core/core_extensions/rendering.rb @@ -74,7 +74,7 @@ module Middleman::CoreExtensions::Rendering end # Certain output file types don't use layouts - needs_layout = !%w(.js .css .txt).include?(extension) + needs_layout = !%w(.js .json .css .txt).include?(extension) # If we need a layout and have a layout, use it if needs_layout && layout_path = fetch_layout(engine, opts) diff --git a/middleman-core/lib/middleman-core/core_extensions/sitemap.rb b/middleman-core/lib/middleman-core/core_extensions/sitemap.rb index 4366d976..188b26c1 100644 --- a/middleman-core/lib/middleman-core/core_extensions/sitemap.rb +++ b/middleman-core/lib/middleman-core/core_extensions/sitemap.rb @@ -19,8 +19,12 @@ module Middleman::CoreExtensions::Sitemap # Files starting with an underscore, but not a double-underscore :partials => proc { |file, path| (file.match(/\/_/) && !file.match(/\/__/)) }, + :layout => proc { |file, path| + file.match(/^source\/layout\./) || file.match(/^source\/layouts\//) + }, + # Files without any output extension (layouts, partials) - :extensionless => proc { |file, path| !path.match(/\./) }, + # :extensionless => proc { |file, path| !path.match(/\./) }, } # Include instance methods diff --git a/middleman-core/lib/middleman-core/sitemap/page.rb b/middleman-core/lib/middleman-core/sitemap/page.rb index b14faca7..8b25b084 100644 --- a/middleman-core/lib/middleman-core/sitemap/page.rb +++ b/middleman-core/lib/middleman-core/sitemap/page.rb @@ -72,7 +72,7 @@ module Middleman::Sitemap @_template ||= ::Middleman::Sitemap::Template.new(self) end - # Extension of the path + # Extension of the path (i.e. '.js') # @return [String] def ext File.extname(path) @@ -192,8 +192,9 @@ module Middleman::Sitemap # This path can be affected by proxy callbacks. # @return [String] def destination_path - # TODO: memoize this value - store.reroute_callbacks.inject(self.path) do |destination, callback| + # memoizing this means that reroute callbacks should be in place before the sitemap + # gets built + @destination_path ||= store.reroute_callbacks.inject(self.path) do |destination, callback| callback.call(destination, self) end end @@ -235,29 +236,27 @@ module Middleman::Sitemap if eponymous_directory? base_path = eponymous_directory_path - prefix = /^#{base_path.sub("/", "\\/")}/ + prefix = %r|^#{base_path.sub("/", "\\/")}| else base_path = path.sub("#{app.index_file}", "") - prefix = /^#{base_path.sub("/", "\\/")}/ + prefix = %r|^#{base_path.sub("/", "\\/")}| end - store.all_paths.select do |sub_path| - sub_path =~ prefix - end.select do |sub_path| - path != sub_path - end.select do |sub_path| - inner_path = sub_path.sub(prefix, "") - parts = inner_path.split("/") - if parts.length == 1 - true - elsif parts.length == 2 - parts.last == app.index_file - else - false - end - end.map do |p| - store.page(p) - end.reject { |p| p.ignored? } + store.pages.select do |sub_page| + if sub_page == self || sub_page.path !~ prefix || sub_page.ignored? + false + else + inner_path = sub_page.path.sub(prefix, "") + parts = inner_path.split("/") + if parts.length == 1 + true + elsif parts.length == 2 + parts.last == app.index_file + else + false + end + end + end end # This page's sibling pages diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index b14dac67..245c74d4 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -24,6 +24,12 @@ module Middleman::Sitemap @reroute_callbacks = [] end + # A list of all pages + # @return [Array] + def pages + @pages.values + end + # Check to see if we know about a specific path # @param [String] path # @return [Boolean] @@ -82,23 +88,11 @@ module Middleman::Sitemap def page_by_destination(destination_path) # TODO: memoize this destination_path = normalize_path(destination_path) - @pages.values.find {|p| p.destination_path == destination_path } - end - - # Loop over known pages - # @yield [path, page] - # @return [void] - def each - @pages.each do |k, v| - yield k, v + pages.find do |p| + p.destination_path == destination_path || + p.destination_path == destination_path.sub("/#{@app.index_file}", "") end end - - # Get all known paths - # @return [Array] - def all_paths - @pages.keys - end # Whether a path is ignored # @param [String] path @@ -116,42 +110,6 @@ module Middleman::Sitemap false end - # Get a list of ignored paths - # @return [Array] - def ignored_paths - @pages.values.select(&:ignored?).map(&:path) - end - - # Whether the given path is generic - # @param [String] path - # @return [Boolean] - def generic?(path) - generic_paths.include?(normalize_path(path)) - end - - # Get a list of generic paths - # @return [Array] - def generic_paths - app.cache.fetch :generic_paths do - @pages.values.select(&:generic?).map(&:path) - end - end - - # Whether the given path is proxied - # @param [String] path - # @return [Boolean] - def proxied?(path) - proxied_paths.include?(normalize_path(path)) - end - - # Get a list of proxied paths - # @return [Array] - def proxied_paths - app.cache.fetch :proxied_paths do - @pages.values.select(&:proxy?).map(&:path) - end - end - # Remove a file from the store # @param [String] file # @return [void] diff --git a/middleman-core/lib/middleman-core/version.rb b/middleman-core/lib/middleman-core/version.rb index 3bc6ccb7..16ba6679 100644 --- a/middleman-core/lib/middleman-core/version.rb +++ b/middleman-core/lib/middleman-core/version.rb @@ -4,10 +4,10 @@ require "rubygems" module Middleman # Current Version # @return [String] - VERSION = '3.0.0.beta.1' unless const_defined?(:VERSION) + VERSION = '3.0.0.beta.2' unless const_defined?(:VERSION) # Parsed version for RubyGems # @private # @return [String] GEM_VERSION = ::Gem::Version.create(VERSION) unless const_defined?(:GEM_VERSION) -end \ No newline at end of file +end diff --git a/middleman-core/lib/middleman-core/watcher.rb b/middleman-core/lib/middleman-core/watcher.rb index aa5ecec3..8888d9b6 100644 --- a/middleman-core/lib/middleman-core/watcher.rb +++ b/middleman-core/lib/middleman-core/watcher.rb @@ -3,6 +3,8 @@ require "net/http" require "win32/process" if ::Middleman::WINDOWS +require "fileutils" + module Middleman class Watcher class << self @@ -63,12 +65,26 @@ module Middleman if @options[:"disable-watcher"] bootup else + pid_name = ".mm-pid-#{@options[:port]||4567}" + + if File.exists?(pid_name) + current_pid = File.open(pid_name, 'rb') { |f| f.read } + begin + Process.kill("INT", -current_pid.to_i) + rescue + ensure + FileUtils.rm(pid_name) + end + end + @server_job = fork { trap("INT") { exit(0) } trap("TERM") { exit(0) } trap("QUIT") { exit(0) } bootup } + + File.open(pid_name, "w+") { |f| f.write(Process.getpgrp) } end end diff --git a/middleman-core/middleman-core.gemspec b/middleman-core/middleman-core.gemspec index 09df535b..9876ce08 100644 --- a/middleman-core/middleman-core.gemspec +++ b/middleman-core/middleman-core.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.require_path = "lib" # Core - s.add_dependency("rack", ["~> 1.3.5"]) + s.add_dependency("rack", ["~> 1.4.0"]) s.add_dependency("tilt", ["~> 1.3.1"]) # Builder @@ -29,7 +29,7 @@ Gem::Specification.new do |s| s.add_dependency("thor", ["~> 0.14.0"]) # Helpers - s.add_dependency("activesupport", ["~> 3.1.0"]) + s.add_dependency("activesupport", ["~> 3.2.0"]) # Watcher s.add_dependency("fssm", ["~> 0.2.8"]) diff --git a/middleman-more/features/compass-sprites.feature b/middleman-more/features/compass-sprites.feature index e4c060a6..2de35eb1 100644 --- a/middleman-more/features/compass-sprites.feature +++ b/middleman-more/features/compass-sprites.feature @@ -1,4 +1,6 @@ Feature: Compass sprites should be generated on build and copied Scenario: Building a clean site with sprites Given a successfully built app at "compass-sprites-app" - Then the output should contain "images/icon-" \ No newline at end of file + When I cd to "build" + Then the following files should exist: + | images/icon-s1a8aa64128.png | \ No newline at end of file diff --git a/middleman-more/features/minify_css.feature b/middleman-more/features/minify_css.feature index 3695ad87..80b3f8ce 100644 --- a/middleman-more/features/minify_css.feature +++ b/middleman-more/features/minify_css.feature @@ -5,13 +5,15 @@ Feature: Minify CSS Given "minify_css" feature is "disabled" And the Server is running at "minify-css-app" When I go to "/stylesheets/site.css" - Then I should see "55" lines + Then I should see "60" lines + And I should see "only screen and (device-width" Scenario: Rendering external css with the feature enabled Given "minify_css" feature is "enabled" And the Server is running at "minify-css-app" When I go to "/stylesheets/site.css" Then I should see "1" lines + And I should see "only screen and (device-width" Scenario: Rendering external css with passthrough compressor Given the Server is running at "passthrough-app" diff --git a/middleman-more/features/sprockets_gems.feature b/middleman-more/features/sprockets_gems.feature index 2e2e4d09..38e5e2bc 100644 --- a/middleman-more/features/sprockets_gems.feature +++ b/middleman-more/features/sprockets_gems.feature @@ -3,8 +3,13 @@ Feature: Sprockets Gems Given the Server is running at "sprockets-app" When I go to "/library/js/jquery_include.js" Then I should see "var jQuery =" - + # Scenario: Sprockets can pull CSS from gem # Given the Server is running at "sprockets-app" # When I go to "/library/css/bootstrap_include.css" - # Then I should see "Bootstrap" \ No newline at end of file + # Then I should see "Bootstrap" + + Scenario: Sprockets can pull js from vendored assets + Given the Server is running at "sprockets-app" + When I go to "/library/js/vendored_include.js" + Then I should see "var vendored_js_included = true;" \ No newline at end of file diff --git a/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass b/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass index d143e05e..0cf9e7da 100755 --- a/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass +++ b/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass @@ -1 +1,5 @@ -@import "compass/reset" \ No newline at end of file +@import "compass/reset" + +@media handheld, only screen and (device-width: 768px) + body + display: block \ No newline at end of file diff --git a/middleman-more/fixtures/sprockets-app/source/library/js/vendored_include.js b/middleman-more/fixtures/sprockets-app/source/library/js/vendored_include.js new file mode 100644 index 00000000..c73b504a --- /dev/null +++ b/middleman-more/fixtures/sprockets-app/source/library/js/vendored_include.js @@ -0,0 +1 @@ +//= require "vendored_js" \ No newline at end of file diff --git a/middleman-more/fixtures/sprockets-app/vendor/assets/javascripts/vendored_js.js b/middleman-more/fixtures/sprockets-app/vendor/assets/javascripts/vendored_js.js new file mode 100644 index 00000000..b27f81d8 --- /dev/null +++ b/middleman-more/fixtures/sprockets-app/vendor/assets/javascripts/vendored_js.js @@ -0,0 +1 @@ +var vendored_js_included = true; \ No newline at end of file diff --git a/middleman-more/lib/middleman-more/core_extensions/compass.rb b/middleman-more/lib/middleman-more/core_extensions/compass.rb index 872da23b..72e51896 100644 --- a/middleman-more/lib/middleman-more/core_extensions/compass.rb +++ b/middleman-more/lib/middleman-more/core_extensions/compass.rb @@ -34,11 +34,12 @@ module Middleman::CoreExtensions::Compass end end - # if build? - # ::Compass.configuration do |config| - # config.environment = :production - # end - # end + if build? + ::Compass.configuration do |config| + config.environment = :production + config.project_path = File.join(root, build_dir) + end + end run_hook :compass_config, ::Compass.configuration run_hook :after_compass_config diff --git a/middleman-more/lib/middleman-more/core_extensions/sprockets.rb b/middleman-more/lib/middleman-more/core_extensions/sprockets.rb index 370534ea..c0f1c996 100644 --- a/middleman-more/lib/middleman-more/core_extensions/sprockets.rb +++ b/middleman-more/lib/middleman-more/core_extensions/sprockets.rb @@ -3,80 +3,42 @@ require "sprockets" # Sprockets extension module Middleman::CoreExtensions::Sprockets - + # Setup extension class << self - + # Once registered def registered(app) # Default compression to off app.set :js_compressor, false app.set :css_compressor, false - - # Cut off every extension after .js (which sprockets eats up) - app.build_reroute do |destination, request_path| - if !request_path.match(/\.js\./i) - false - else - [ - destination.gsub(/\.js(\..*)$/, ".js"), - request_path.gsub(/\.js(\..*)$/, ".js") - ] - end - end - + # Once Middleman is setup app.ready do - # Create sprockets env for JS + # Create sprockets env for JS and CSS js_env = Middleman::CoreExtensions::Sprockets::JavascriptEnvironment.new(self) + css_env = Middleman::CoreExtensions::Sprockets::StylesheetEnvironment.new(self) - # Add any gems with vendor/assets/javascripts to paths - vendor_dir = File.join("vendor", "assets", "javascripts") - gems_with_js = ::Middleman.rubygems_latest_specs.select do |spec| - ::Middleman.spec_has_file?(spec, vendor_dir) - end.each do |spec| - js_env.append_path File.join(spec.full_gem_path, vendor_dir) - end + # Add any gems with (vendor|app|.)/assets/javascripts to paths + # also add similar directories from project root (like in rails) + root_paths = [%w{ app }, %w{ assets }, %w{ vendor }, %w{ app assets }, %w{ vendor assets }] + try_js_paths = root_paths.map{|rp| File.join(rp, 'javascripts')} + try_css_paths = root_paths.map{|rp| File.join(rp, 'stylesheets')} - # Add any gems with app/assets/javascripts to paths - app_dir = File.join("app", "assets", "javascripts") - gems_with_js = ::Middleman.rubygems_latest_specs.select do |spec| - ::Middleman.spec_has_file?(spec, app_dir) - end.each do |spec| - js_env.append_path File.join(spec.full_gem_path, app_dir) - end - - # Intercept requests to /javascripts and pass to sprockets - map "/#{js_dir}" do - run js_env + { try_js_paths => js_env, try_css_paths => css_env }.each do |paths, env| + ([root] + ::Middleman.rubygems_latest_specs.map(&:full_gem_path)).each do |root_path| + paths.map{|p| File.join(root_path, p)}. + select{|p| File.directory?(p)}. + each{|path| env.append_path(path)} + end end # Setup Sprockets Sass options sass.each { |k, v| ::Sprockets::Sass.options[k] = v } - - # Create sprockets env for CSS - css_env = Middleman::CoreExtensions::Sprockets::StylesheetEnvironment.new(self) - - # Add any gems with vendor/assets/stylesheets to paths - vendor_dir = File.join("vendor", "assets", "stylesheets") - gems_with_css = ::Middleman.rubygems_latest_specs.select do |spec| - ::Middleman.spec_has_file?(spec, vendor_dir) - end.each do |spec| - css_env.append_path File.join(spec.full_gem_path, vendor_dir) - end - - # Add any gems with app/assets/stylesheets to paths - app_dir = File.join("app", "assets", "stylesheets") - gems_with_css = ::Middleman.rubygems_latest_specs.select do |spec| - ::Middleman.spec_has_file?(spec, app_dir) - end.each do |spec| - css_env.append_path File.join(spec.full_gem_path, app_dir) - end - - # Intercept requests to /stylesheets and pass to sprockets - map("/#{css_dir}") do - run css_env - end + + # Intercept requests to /javascripts and /stylesheets and pass to sprockets + map("/#{js_dir}") { run js_env } + map("/#{css_dir}"){ run css_env } end end alias :included :registered @@ -88,7 +50,7 @@ module Middleman::CoreExtensions::Sprockets def initialize(app) @app = app super app.source_dir - + # Make the app context available to Sprockets context_class.send(:define_method, :app) { app } context_class.class_eval do @@ -101,26 +63,26 @@ module Middleman::CoreExtensions::Sprockets end end end - + # During development, don't use the asset cache def find_asset(path, options = {}) expire_index! if @app.development? super end end - + # Javascript specific environment class JavascriptEnvironment < MiddlemanEnvironment - + # Init def initialize(app) super - + expire_index! # Remove old compressor unregister_bundle_processor 'application/javascript', :js_compressor - + # Register compressor from config register_bundle_processor 'application/javascript', :js_compressor do |context, data| if context.pathname.to_s =~ /\.min\./ @@ -129,30 +91,30 @@ module Middleman::CoreExtensions::Sprockets app.js_compressor.compress(data) end end if app.js_compressor - + # configure search paths append_path app.js_dir end - + # Clear cache on error def javascript_exception_response(exception) expire_index! super(exception) end end - + # CSS specific environment class StylesheetEnvironment < MiddlemanEnvironment - + # Init def initialize(app) super - + expire_index! # Remove old compressor unregister_bundle_processor 'text/css', :css_compressor - + # Register compressor from config register_bundle_processor 'text/css', :css_compressor do |context, data| if context.pathname.to_s =~ /\.min\./ @@ -161,15 +123,15 @@ module Middleman::CoreExtensions::Sprockets app.css_compressor.compress(data) end end if app.css_compressor - + # configure search paths append_path app.css_dir end - + # Clear cache on error def css_exception_response(exception) expire_index! super(exception) end end -end \ No newline at end of file +end diff --git a/middleman-more/lib/middleman-more/extensions/minify_css.rb b/middleman-more/lib/middleman-more/extensions/minify_css.rb index a7a8c0b9..f90c8848 100644 --- a/middleman-more/lib/middleman-more/extensions/minify_css.rb +++ b/middleman-more/lib/middleman-more/extensions/minify_css.rb @@ -12,8 +12,8 @@ module Middleman::Extensions # Tell Sprockets to use the built in CSSMin app.after_configuration do if !css_compressor - require "middleman-more/extensions/minify_css/cssmin" - set :css_compressor, ::CSSMin + require "middleman-more/extensions/minify_css/rainpress" + set :css_compressor, ::Rainpress end end end diff --git a/middleman-more/lib/middleman-more/extensions/minify_css/cssmin.rb b/middleman-more/lib/middleman-more/extensions/minify_css/cssmin.rb deleted file mode 100644 index 8a84885b..00000000 --- a/middleman-more/lib/middleman-more/extensions/minify_css/cssmin.rb +++ /dev/null @@ -1,60 +0,0 @@ -#-- -# Copyright (c) 2008 Ryan Grove -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of this project nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#++ - -# Minify CSS -module CSSMin - - # Compress a CSS string - # @param [String] input - # @return [String] - def self.compress(input) - css = input.is_a?(IO) ? input.read : input.dup.to_s - css.gsub!(/\/\*[\s\S]*?\*\//, '') - css.gsub!(/\s+/, ' ') - css.gsub!(/"\\"\}\\""/, '___BMH___') - css.gsub!(/(?:^|\})[^\{:]+\s+:+[^\{]*\{/) do |match| - match.gsub(':', '___PSEUDOCLASSCOLON___') - end - css.gsub!(/\s+([!\{\};:>+\(\)\],])/, '\1') - css.gsub!('___PSEUDOCLASSCOLON___', ':') - css.gsub!(/([!\{\}:;>+\(\[,])\s+/, '\1') - css.gsub!(/([^;\}])\}/, '\1;}') - css.gsub!(/([\s:])([+-]?0)(?:%|em|ex|px|in|cm|mm|pt|pc)/i, '\1\2') - css.gsub!(/:(?:0 )+0;/, ':0;') - css.gsub!('background-position:0;', 'background-position:0 0;') - css.gsub!(/(:|\s)0+\.(\d+)/, '\1.\2') - css.gsub!(/rgb\s*\(\s*([0-9,\s]+)\s*\)/) do |match| - '#' << $1.scan(/\d+/).map{|n| n.to_i.to_s(16).rjust(2, '0') }.join - end - css.gsub!(/([^"'=\s])\s*#([0-9a-f])\2([0-9a-f])\3([0-9a-f])\4/i, '\1 #\2\3\4') - css.gsub!(/[^\}]+\{;\}\n/, '') - css.gsub!('___BMH___', '"\"}\""') - css.gsub!(/;;+/, ';') - css.strip - end -end \ No newline at end of file diff --git a/middleman-more/lib/middleman-more/extensions/minify_css/rainpress.rb b/middleman-more/lib/middleman-more/extensions/minify_css/rainpress.rb new file mode 100644 index 00000000..20790696 --- /dev/null +++ b/middleman-more/lib/middleman-more/extensions/minify_css/rainpress.rb @@ -0,0 +1,168 @@ +# == Information +# +# This is the main class of Rainpress, create an instance of it to compress +# your CSS-styles. +# +# Author:: Uwe L. Korn +# +# Options: +# +# * :comments - if set to false, comments will not be removed +# * :newlines - if set to false, newlines will not be removed +# * :spaces - if set to false, spaces will not be removed +# * :colors - if set to false, colors will not be modified +# * :misc - if set to false, miscellaneous compression parts will be skipped +class Rainpress + # Quick-compress the styles. + # This eliminates the need to create an instance of the class + def self.compress(style, options = {}) + self.new(style, options).compress! + end + + def initialize(style, opts = {}) + @style = style + @opts = { + :comments => true, + :newlines => true, + :spaces => true, + :colors => true, + :misc => true + } + @opts.merge! opts + end + + # Run the compressions and return the newly compressed text + def compress! + remove_comments! if @opts[:comments] + remove_newlines! if @opts[:newlines] + remove_spaces! if @opts[:spaces] + shorten_colors! if @opts[:colors] + do_misc! if @opts[:misc] + @style + end + + # Remove all comments out of the CSS-Document + # + # Only /* text */ comments are supported. + # Attention: If you are doing css hacks for IE using the comment tricks, + # they will be removed using this function. Please consider for IE css style + # corrections the usage of conditionals comments in your (X)HTML document. + def remove_comments! + input = @style + @style = '' + + while input.length > 0 do + pos = input.index("/*"); + + # No more comments + if pos == nil + @style += input + input = ''; + else # Comment beginning at pos + @style += input[0..(pos-1)] if pos > 0 # only append text if there is some + input = input[(pos+2)..-1] + # Comment ending at pos + pos = input.index("*/") + input = input[(pos+2)..-1] + end + end + end + + # Remove all newline characters + # + # We take care of Windows(\r\n), Unix(\n) and Mac(\r) newlines. + def remove_newlines! + @style.gsub! /\n|\r/, '' + end + + # Remove unneeded spaces + # + # 1. Turn mutiple spaces into a single + # 2. Remove spaces around ;:{}, + # 3. Remove tabs + def remove_spaces! + @style.gsub! /\s*(\s|;|:|\}|\{|,)\s*/, '\1' + @style.gsub! "\t", '' + end + + # Replace color values with their shorter equivalent + # + # 1. Turn rgb(,,)-colors into #-values + # 2. Shorten #AABBCC down to #ABC + # 3. Replace names with their shorter hex-equivalent + # * white -> #fff + # * black -> #000 + # 4. Replace #-values with their shorter name + # * #f00 -> red + def shorten_colors! + # rgb(50,101,152) to #326598 + @style.gsub! /rgb\s*\(\s*([0-9,\s]+)\s*\)/ do |match| + out = '#' + $1.split(',').each do |num| + out += '0' if num.to_i < 16 + out += num.to_i.to_s(16) # convert to hex + end + out + end + # Convert #AABBCC to #ABC, keep if preceed by a '=' + @style.gsub! /([^\"'=\s])(\s*)#([\da-f])\3([\da-f])\4([\da-f])\5/i, '\1#\3\4\5' + + # At the moment we assume that colours only appear before ';' or '}' and + # after a ':', if there could be an occurence of a color before or after + # an other character, submit either a bug report or, better, a patch that + # enables Rainpress to take care of this. + + # shorten several names to numbers + ## shorten white -> #fff + @style.gsub! /:\s*white\s*(;|\})/, ':#fff\1' + + ## shorten black -> #000 + @style.gsub! /:\s*black\s*(;|\})/, ':#000\1' + + # shotern several numbers to names + ## shorten #f00 or #ff0000 -> red + @style.gsub! /:\s*#f{1,2}0{2,4}(;|\})/i, ':red\1' + end + + # Do miscellaneous compression methods on the style. + def do_misc! + # Replace 0(pt,px,em,%) with 0 but only when preceded by : or a white-space + @style.gsub! /([\s:]+)(0)(px|em|%|in|cm|mm|pc|pt|ex)/i, '\1\2' + + # Replace :0 0 0 0(;|}) with :0(;|}) + @style.gsub! /:0 0 0 0(;|\})/, ':0\1' + + # Replace :0 0 0(;|}) with :0(;|}) + @style.gsub! /:0 0 0(;|\})/, ':0\1' + + # Replace :0 0(;|}) with :0(;|}) + @style.gsub! /:0 0(;|\})/, ':0\1' + + # Replace background-position:0; with background-position:0 0; + @style.gsub! 'background-position:0;', 'background-position:0 0;' + + # Replace 0.6 to .6, but only when preceded by : or a white-space + @style.gsub! /[:\s]0+\.(\d+)/ do |match| + match.sub '0', '' # only first '0' !! + end + + # Replace multiple ';' with a single ';' + @style.gsub! /[;]+/, ';' + + # Replace ;} with } + @style.gsub! ';}', '}' + + # Replace font-weight:normal; with 400 + @style.gsub! /font-weight[\s]*:[\s]*normal[\s]*(;|\})/i,'font-weight:400\1' + @style.gsub! /font[\s]*:[\s]*normal[\s;\}]*/ do |match| + match.sub 'normal', '400' + end + + # Replace font-weight:bold; with 700 + @style.gsub! /font-weight[\s]*:[\s]*bold[\s]*(;|\})/,'font-weight:700\1' + @style.gsub! /font[\s]*:[\s]*bold[\s;\}]*/ do |match| + match.sub 'bold', '700' + end + end + +end \ No newline at end of file diff --git a/middleman-more/middleman-more.gemspec b/middleman-more/middleman-more.gemspec index b982be44..7c1560b9 100644 --- a/middleman-more/middleman-more.gemspec +++ b/middleman-more/middleman-more.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |s| s.add_dependency("sass", ["~> 3.1.7"]) s.add_dependency("compass", ["0.12.rc.1"]) s.add_dependency("coffee-script", ["~> 2.2.0"]) - s.add_dependency("execjs", ["~> 1.2.7"]) + s.add_dependency("execjs", ["~> 1.2"]) s.add_dependency("sprockets", ["~> 2.1.0"]) s.add_dependency("sprockets-sass", ["~> 0.6.0"]) s.add_dependency("redcarpet", ["~> 2.0.0"])