diff --git a/middleman-core/lib/middleman-core/contracts.rb b/middleman-core/lib/middleman-core/contracts.rb index 349728e2..16fff201 100644 --- a/middleman-core/lib/middleman-core/contracts.rb +++ b/middleman-core/lib/middleman-core/contracts.rb @@ -1,5 +1,6 @@ if ENV['TEST'] || ENV['CONTRACTS'] == 'true' require 'contracts' + require 'hamster' module Contracts class IsA @@ -27,21 +28,7 @@ if ENV['TEST'] || ENV['CONTRACTS'] == 'true' end end - # class MethodDefined - # def self.[](val) - # @lookup ||= {} - # @lookup[val] ||= new(val) - # end - - # def initialize(val) - # @val = val - # end - - # def valid?(val) - # val.method_defined? @val - # end - # end - + VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector) ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']] end else @@ -125,8 +112,8 @@ else class Frozen < Callable end - # class MethodDefined < Callable - # end + class VectorOf < Callable + end end end diff --git a/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb b/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb index 740b8188..fcf1d11f 100644 --- a/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb +++ b/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb @@ -1,3 +1,5 @@ +require 'hamster' + module Middleman module CoreExtensions module Collections @@ -9,13 +11,13 @@ module Middleman attr_reader :descriptors def initialize - @descriptors = [] + @descriptors = ::Hamster.set end def method_missing(name, *args, &block) internal = :"_internal_#{name}" if respond_to?(internal) - @descriptors << send(internal, *args, &block) + @descriptors = @descriptors.add(send(internal, *args, &block)) else super end diff --git a/middleman-core/lib/middleman-core/core_extensions/i18n.rb b/middleman-core/lib/middleman-core/core_extensions/i18n.rb index 2f5f3bb9..6561621b 100644 --- a/middleman-core/lib/middleman-core/core_extensions/i18n.rb +++ b/middleman-core/lib/middleman-core/core_extensions/i18n.rb @@ -12,7 +12,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension def initialize(*) super - + require 'i18n' # Don't fail on invalid locale, that's not what our current diff --git a/middleman-core/lib/middleman-core/preview_server.rb b/middleman-core/lib/middleman-core/preview_server.rb index 5b596ff8..2d71f99e 100644 --- a/middleman-core/lib/middleman-core/preview_server.rb +++ b/middleman-core/lib/middleman-core/preview_server.rb @@ -179,15 +179,15 @@ module Middleman http_opts[:SSLEnable] = true if ssl_certificate || ssl_private_key - raise "You must provide both :ssl_certificate and :ssl_private_key" unless ssl_private_key && ssl_certificate + raise 'You must provide both :ssl_certificate and :ssl_private_key' unless ssl_private_key && ssl_certificate http_opts[:SSLCertificate] = OpenSSL::X509::Certificate.new File.read ssl_certificate http_opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new File.read ssl_private_key else # use a generated self-signed cert http_opts[:SSLCertName] = [ - %w(CN localhost), - %w(CN #{host}) - ].uniq + %w(CN localhost), + %w(CN #{host}) + ].uniq end end diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index b454ecf9..129fa9b5 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -1,6 +1,7 @@ # Used for merging results of metadata callbacks require 'active_support/core_ext/hash/deep_merge' require 'monitor' +require 'hamster' # Ignores Middleman::Extensions.register :sitemap_ignore, auto_activate: :before_configuration do @@ -37,6 +38,8 @@ require 'middleman-core/contracts' module Middleman # Sitemap namespace module Sitemap + ManipulatorDescriptor = Struct.new :name, :manipulator, :priority, :custom_name + # The Store class # # The Store manages a collection of Resource objects, which represent @@ -46,20 +49,21 @@ module Middleman class Store include Contracts - # @return [Middleman::Application] + Contract IsA['Middleman::Application'] attr_reader :app + Contract Num attr_reader :update_count # Initialize with parent app # @param [Middleman::Application] app + Contract IsA['Middleman::Application'] => Any def initialize(app) @app = app @resources = [] @update_count = 0 - # TODO: Should this be a set or hash? - @resource_list_manipulators = [] + @resource_list_manipulators = ::Hamster.vector @needs_sitemap_rebuild = true @lock = Monitor.new @@ -76,24 +80,29 @@ module Middleman # @param [Numeric] priority Sets the order of this resource list manipulator relative to the rest. By default this is 50, and manipulators run in the order they are registered, but if a priority is provided then this will run ahead of or behind other manipulators. # @param [Symbol] custom_name The method name to execute. # @return [void] - Contract Symbol, RespondTo['manipulate_resource_list'], Maybe[Num], Maybe[Symbol] => Any + Contract Symbol, RespondTo[:manipulate_resource_list], Maybe[Num], Maybe[Symbol] => Any def register_resource_list_manipulator(name, manipulator, priority=50, custom_name=nil) # The third argument used to be a boolean - handle those who still pass one priority = 50 unless priority.is_a? Numeric - @resource_list_manipulators << [name, manipulator, priority, custom_name] + @resource_list_manipulators = @resource_list_manipulators.push( + ManipulatorDescriptor.new(name, manipulator, priority, custom_name) + ) + # The index trick is used so that the sort is stable - manipulators with the same priority # will always be ordered in the same order as they were registered. n = 0 @resource_list_manipulators = @resource_list_manipulators.sort_by do |m| n += 1 - [m[2], n] + [m[:priority], n] end + rebuild_resource_list!(:registered_new) end # Rebuild the list of resources from scratch, using registed manipulators # @return [void] - def rebuild_resource_list!(_=nil) + Contract Maybe[Symbol] => Any + def rebuild_resource_list!(_name=nil) @lock.synchronize do @needs_sitemap_rebuild = true end @@ -178,11 +187,13 @@ module Middleman @app.logger.debug '== Rebuilding resource list' - @resources = @resource_list_manipulators.reduce([]) do |result, (_, manipulator, _, custom_name)| - newres = manipulator.send(custom_name || :manipulate_resource_list, result) + @resources = @resource_list_manipulators.reduce([]) do |result, m| + newres = m[:manipulator].send(m[:custom_name] || :manipulate_resource_list, result) # Reset lookup cache reset_lookup_cache! + + # Rebuild cache newres.each do |resource| @_lookup_by_path[resource.path] = resource @_lookup_by_destination_path[resource.destination_path] = resource diff --git a/middleman-core/lib/middleman-core/sources.rb b/middleman-core/lib/middleman-core/sources.rb index 34d2ab8a..10e14988 100644 --- a/middleman-core/lib/middleman-core/sources.rb +++ b/middleman-core/lib/middleman-core/sources.rb @@ -1,3 +1,4 @@ +require 'hamster' require 'middleman-core/contracts' require 'backports/2.0.0/enumerable/lazy' @@ -47,10 +48,10 @@ module Middleman @options = options # Set of procs wanting to be notified of changes - @on_change_callbacks = [] + @on_change_callbacks = ::Hamster.vector # Global ignores - @ignores = {} + @ignores = ::Hamster.hash # Whether we're "running", which means we're in a stable # watch state after all initialization and config. @@ -72,7 +73,8 @@ module Middleman # @return [void] Contract Symbol, Symbol, Or[Regexp, Proc] => Any def ignore(name, type, regex=nil, &block) - @ignores[name] = { type: type, validator: (block_given? ? block : regex) } + @ignores = @ignores.put(name, type: type, + validator: (block_given? ? block : regex)) bump_count find_new_files! if @running @@ -238,11 +240,10 @@ module Middleman # Add callback to be run on file change or deletion # # @param [Symbol] type The change type. - # @return [Set] - Contract Symbol, Proc => ArrayOf[CallbackDescriptor] + # @return [void] + Contract Symbol, Proc => Any def on_change(type, &block) - @on_change_callbacks << CallbackDescriptor.new(type, block) - @on_change_callbacks + @on_change_callbacks = @on_change_callbacks.push(CallbackDescriptor.new(type, block)) end # Backwards compatible change handler. @@ -328,7 +329,7 @@ module Middleman # @param [Set] callback_descriptors The registered callbacks. # @param [Array] files The files that were changed. # @return [void] - Contract ArrayOf[CallbackDescriptor], ArrayOf[SourceFile], ArrayOf[SourceFile] => Any + Contract VectorOf[CallbackDescriptor], ArrayOf[SourceFile], ArrayOf[SourceFile] => Any def run_callbacks(callback_descriptors, updated_files, removed_files) callback_descriptors.each do |callback| if callback[:type] == :all