diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb
index 74c35285..0ae9a811 100644
--- a/middleman-core/lib/middleman-core/application.rb
+++ b/middleman-core/lib/middleman-core/application.rb
@@ -14,7 +14,7 @@ require 'active_support/core_ext/integer/inflections'
require 'active_support/core_ext/float/rounding'
# Simple callback library
-require 'vendored-middleman-deps/hooks-0.2.0/lib/hooks'
+require 'vendored-middleman-deps/hooks-0.3.3/lib/hooks'
# Our custom logger
require 'middleman-core/logger'
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/CHANGES.textile b/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/CHANGES.textile
deleted file mode 100644
index 3beea91d..00000000
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/CHANGES.textile
+++ /dev/null
@@ -1,9 +0,0 @@
-h2. 0.2.0
-
-h3. Changes
- * Callback blocks are now executed on the instance using @instance_exec@. If you need to access the class (former context) use @self.class@.
-
-h2. 0.1.4
-
-h3. Bugfixes
- * An uninitialized @inheritable_attr@ doesn't crash since it is not cloned anymore. Note that an uncloneable attribute value still causes an exception.
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/Gemfile b/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/Gemfile
deleted file mode 100644
index a1b93f3e..00000000
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/Gemfile
+++ /dev/null
@@ -1,3 +0,0 @@
-source :rubygems
-
-gemspec
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/README.rdoc b/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/README.rdoc
deleted file mode 100644
index 4a209500..00000000
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/README.rdoc
+++ /dev/null
@@ -1,107 +0,0 @@
-= Hooks
-
-Generic hooks with callbacks for Ruby.
-
-
-== Introduction
-
-_Hooks_ lets you define hooks declaratively in your ruby class. You can add callbacks to your hook, which will be run as soon as _you_ run the hook!
-
-It's almost like ActiveSupport::Callbacks but 76,6% less complex. Instead, it is not more than 60 lines of code, one method compilation, no +method_missing+ and no magic.
-
-Also, you may pass additional arguments to your callbacks when invoking a hook.
-
-== Example
-
-Let's take... a cat.
-
- require 'hooks'
-
- class Cat
- include Hooks
-
- define_hook :after_dinner
-
-Now you can add callbacks to your hook declaratively in your class.
-
- after_dinner do
- puts "Ice cream for #{self}!"
- end
-
- after_dinner :have_a_desert # => refers to Cat#have_a_desert
-
- def have_a_desert
- puts "Hell, yeah!"
- end
-
-This will run the block and #have_a_desert from above.
-
- cat.run_hook :after_dinner
- # => Ice cream for #!
- Hell, yeah!
-
-Callback blocks and methods will be executed with instance context. Note how +self+ in the block refers to the Cat instance.
-
-
-== Inheritance
-
-Hooks are inherited, here's a complete example to put it all together.
-
- class Garfield < Cat
-
- after_dinner :want_some_more
-
- def want_some_more
- puts "Is that all?"
- end
- end
-
-
- Garfield.new.run_hook :after_dinner
- # => Ice cream for #!
- Hell, yeah!
- Is that all?
-
-Note how the callbacks are invoked in the order they were inherited.
-
-
-== Options for Callbacks
-
-You're free to pass any number of arguments to #run_callback, those will be passed to the callbacks.
-
- cat.run_hook :before_dinner, cat, Time.now
-
-The callbacks should be ready for receiving parameters.
-
- before_dinner :wash_pawns
- before_dinner do |who, when|
- ...
- end
-
- def wash_pawns(who, when)
-
-
-Not sure why a cat should have ice cream for dinner. Beside that, I was tempted naming this gem _hooker_.
-
-
-== Installation
-
- gem install hooks
-
-
-== Anybody using it?
-
-* Hooks is already used in [Apotomo:http://github.com/apotonick/apotomo], a hot widget framework for Rails. Look at +lib/apotomo/widget.rb+ for examples and into +lib/apotomo/tree_node.rb+ to learn how modules-driven code might benefit from hooks.
-
-== Similar libraries
-
-* http://github.com/nakajima/aspectory
-* http://github.com/auser/backcall
-* http://github.com/mmcgrana/simple_callbacks
-
-
-== License
-
-Copyright (c) 2010, Nick Sutterer
-
-Released under the MIT License.
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/hooks_test.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/hooks_test.rb
deleted file mode 100644
index 83cb24e4..00000000
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/hooks_test.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-require 'test_helper'
-
-class HooksTest < Test::Unit::TestCase
- class TestClass
- include Hooks
-
- def executed
- @executed ||= [];
- end
- end
-
-
- context "Hooks.define_hook" do
- setup do
- @klass = Class.new(TestClass)
-
- @mum = @klass.new
- @mum.class.define_hook :after_eight
- end
-
- should "provide accessors to the stored callbacks" do
- assert_equal [], @klass._after_eight_callbacks
- @klass._after_eight_callbacks << :dine
- assert_equal [:dine], @klass._after_eight_callbacks
- end
-
- should "respond to Class.callbacks_for_hook" do
- assert_equal [], @klass.callbacks_for_hook(:after_eight)
- @klass.after_eight :dine
- assert_equal [:dine], @klass.callbacks_for_hook(:after_eight)
- end
-
- context "creates a public writer for the hook that" do
- should "accepts method names" do
- @klass.after_eight :dine
- assert_equal [:dine], @klass._after_eight_callbacks
- end
-
- should "accepts blocks" do
- @klass.after_eight do true; end
- assert @klass._after_eight_callbacks.first.kind_of? Proc
- end
-
- should "be inherited" do
- @klass.after_eight :dine
- subklass = Class.new(@klass)
-
- assert_equal [:dine], subklass._after_eight_callbacks
- end
- end
-
- context "Hooks#run_hook" do
- should "run without parameters" do
- @mum.instance_eval do
- def a; executed << :a; nil; end
- def b; executed << :b; end
-
- self.class.after_eight :b
- self.class.after_eight :a
- end
-
- @mum.run_hook(:after_eight)
-
- assert_equal [:b, :a], @mum.executed
- end
-
- should "accept arbitrary parameters" do
- @mum.instance_eval do
- def a(me, arg); executed << arg+1; end
- end
- @mum.class.after_eight :a
- @mum.class.after_eight lambda { |me, arg| me.executed << arg-1 }
-
- @mum.run_hook(:after_eight, @mum, 1)
-
- assert_equal [2, 0], @mum.executed
- end
-
- should "execute block callbacks in instance context" do
- @mum.class.after_eight { executed << :c }
- @mum.run_hook(:after_eight)
- assert_equal [:c], @mum.executed
- end
- end
-
- context "in class context" do
- should "run a callback block" do
- executed = []
- @klass.after_eight do
- executed << :klass
- end
- @klass.run_hook :after_eight
-
- assert_equal [:klass], executed
- end
-
- should "run a class methods" do
- executed = []
- @klass.instance_eval do
- after_eight :have_dinner
-
- def have_dinner(executed)
- executed << :have_dinner
- end
- end
- @klass.run_hook :after_eight, executed
-
- assert_equal [:have_dinner], executed
- end
- end
- end
-
- context "Deriving" do
- setup do
- @klass = Class.new(TestClass)
-
- @mum = @klass.new
- @mum.class.define_hook :after_eight
- end
-
- should "inherit the hook" do
- @klass.class_eval do
- after_eight :take_shower
-
- def take_shower
- executed << :take_shower
- end
- end
-
- @kid = Class.new(@klass) do
- after_eight :have_dinner
-
- def have_dinner
- executed << :have_dinner
- end
- end.new
-
- assert_equal [:take_shower, :have_dinner], @kid.run_hook(:after_eight)
- end
- end
-end
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/inheritable_attribute_test.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/inheritable_attribute_test.rb
deleted file mode 100644
index 27e10a2d..00000000
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/inheritable_attribute_test.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-require 'test_helper'
-
-class HooksTest < Test::Unit::TestCase
- context "Hooks.define_hook" do
- setup do
- @klass = Class.new(Object) do
- extend Hooks::InheritableAttribute
- end
-
- @mum = @klass.new
- @klass.inheritable_attr :drinks
- end
-
- should "provide a reader with empty inherited attributes, already" do
- assert_equal nil, @klass.drinks
- end
-
- should "provide a reader with empty inherited attributes in a derived class" do
- assert_equal nil, Class.new(@klass).drinks
- #@klass.drinks = true
- #Class.new(@klass).drinks # TODO: crashes.
- end
-
- should "provide an attribute copy in subclasses" do
- @klass.drinks = []
- assert @klass.drinks.object_id != Class.new(@klass).drinks.object_id
- end
-
- should "provide a writer" do
- @klass.drinks = [:cabernet]
- assert_equal [:cabernet], @klass.drinks
- end
-
- should "inherit attributes" do
- @klass.drinks = [:cabernet]
-
- subklass_a = Class.new(@klass)
- subklass_a.drinks << :becks
-
- subklass_b = Class.new(@klass)
-
- assert_equal [:cabernet], @klass.drinks
- assert_equal [:cabernet, :becks], subklass_a.drinks
- assert_equal [:cabernet], subklass_b.drinks
- end
-
- should "not inherit attributes if we set explicitely" do
- @klass.drinks = [:cabernet]
- subklass = Class.new(@klass)
-
- subklass.drinks = [:merlot] # we only want merlot explicitely.
- assert_equal [:merlot], subklass.drinks # no :cabernet, here
- end
- end
-end
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/test_helper.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/test_helper.rb
deleted file mode 100644
index f0b0b3cd..00000000
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/test/test_helper.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'rubygems'
-
-# wycats says...
-require 'bundler'
-Bundler.setup
-require 'test/unit'
-require 'shoulda'
-require 'hooks'
-
-$:.unshift File.dirname(__FILE__) # add current dir to LOAD_PATHS
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/.gitignore b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/.gitignore
new file mode 100644
index 00000000..80e3957f
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/.gitignore
@@ -0,0 +1,2 @@
+Gemfile.lock
+
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/.travis.yml b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/.travis.yml
new file mode 100644
index 00000000..03107eec
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/.travis.yml
@@ -0,0 +1,5 @@
+language: ruby
+rvm:
+ - 1.8.7
+ - 1.9.3
+ - 2.0.0
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/CHANGES.md b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/CHANGES.md
new file mode 100644
index 00000000..277fda61
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/CHANGES.md
@@ -0,0 +1,33 @@
+## 0.3.3
+
+* Fix a bug where the hook writer method (e.g. `#after_dark`) wasn't available on the instance even when `InstanceHooks` was included.
+
+## 0.3.2
+
+* Added `Hooks::InstanceHooks` to add hooks and/or callbacks on instance level. Thanks to @mpapis for that suggestion.
+
+## 0.3.1
+
+* Fix a bug, string hook names are now treated as symbols.
+
+## 0.3.0
+
+* The callback chain can now be halted by configuring the hook as `halts_on_falsey: true` and returning `nil` or `false` from the callback.
+* Internal refactorings: hooks are now encapsulated in `Hook` instances and run their callback chains.
+
+## 0.2.2
+
+* `#run_hook` now returns the list of callback results.
+
+## 0.2.1
+
+* You can now pass multiple hook names to `#define_hooks`.
+
+## 0.2.0
+
+h3. Changes
+* Callback blocks are now executed on the instance using `instance_exec`. If you need to access the class (former context) use `self.class`.
+
+## 0.1.4
+
+* An uninitialized `inheritable_attr` doesn't crash since it is not cloned anymore. Note that an uncloneable attribute value still causes an exception.
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/Gemfile b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/Gemfile
new file mode 100644
index 00000000..fa75df15
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/Gemfile
@@ -0,0 +1,3 @@
+source 'https://rubygems.org'
+
+gemspec
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/LICENSE.txt b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/LICENSE.txt
new file mode 100644
index 00000000..ffbc659d
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2011-2013 Nick Sutterer
+
+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.
\ No newline at end of file
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/README.md b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/README.md
new file mode 100644
index 00000000..436d988d
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/README.md
@@ -0,0 +1,202 @@
+# Hooks
+
+_Generic hooks with callbacks for Ruby._
+
+
+## Introduction
+
+_Hooks_ lets you define hooks declaratively in your ruby class. You can add callbacks to your hook, which will be run as soon as _you_ run the hook!
+
+It's almost like ActiveSupport::Callbacks but 76,6% less complex. Instead, it is not more than a few lines of code, one method compilation, no `method_missing` and no magic.
+
+Also, you may pass additional arguments to your callbacks when invoking a hook.
+
+## Example
+
+Let's take... a cat.
+
+```ruby
+require 'hooks'
+
+class Cat
+ include Hooks
+
+ define_hooks :before_dinner, :after_dinner
+```
+
+Now you can add callbacks to your hook declaratively in your class.
+
+```ruby
+ before_dinner :wash_paws
+
+ after_dinner do
+ puts "Ice cream for #{self}!"
+ end
+
+ after_dinner :have_a_desert # => refers to Cat#have_a_desert
+
+ def have_a_desert
+ puts "Hell, yeah!"
+ end
+```
+
+This will run the block and `#have_a_desert` from above.
+
+```ruby
+cat.run_hook :after_dinner
+# => Ice cream for #!
+ Hell, yeah!
+```
+
+Callback blocks and methods will be executed with instance context. Note how `self` in the block refers to the Cat instance.
+
+
+## Inheritance
+
+Hooks are inherited, here's a complete example to put it all together.
+
+```ruby
+class Garfield < Cat
+
+ after_dinner :want_some_more
+
+ def want_some_more
+ puts "Is that all?"
+ end
+end
+
+
+Garfield.new.run_hook :after_dinner
+# => Ice cream for #!
+ Hell, yeah!
+ Is that all?
+```
+
+Note how the callbacks are invoked in the order they were inherited.
+
+
+## Options for Callbacks
+
+You're free to pass any number of arguments to #run_callback, those will be passed to the callbacks.
+
+```ruby
+cat.run_hook :before_dinner, cat, Time.now
+```
+
+The callbacks should be ready for receiving parameters.
+
+```ruby
+before_dinner :wash_pawns
+before_dinner do |who, when|
+ ...
+end
+
+def wash_pawns(who, when)
+```
+
+Not sure why a cat should have ice cream for dinner. Beside that, I was tempted naming this gem _hooker_.
+
+
+## Running And Halting Hooks
+
+Using `#run_hook` doesn't only run all callbacks for this hook but also returns an array of the results from each callback method or block.
+
+```ruby
+class Garfield
+ include Hooks
+ define_hook :after_dark
+
+ after_dark { "Chase mice" }
+ after_dark { "Enjoy supper" }
+end
+
+Garfield.new.run_hook :after_dark
+# => ["Chase mice", "Enjoy supper"]
+```
+
+This is handy if you need to collect data from your callbacks without having to access a global (brrr) variable.
+
+With the `:halts_on_falsey` option you can halt the callback chain when a callback returns `nil` or `false`.
+
+```ruby
+class Garfield
+ include Hooks
+ define_hook :after_dark, halts_on_falsey: true
+
+ after_dark { "Chase mice" }
+ after_dark { nil }
+ after_dark { "Enjoy supper" }
+end
+
+result = Garfield.new.run_hook :after_dark
+# => ["Chase mice"]
+```
+
+This will only run the first two callbacks. Note that the result doesn't contain the `nil` value. You even can check if the chain was halted.
+
+```ruby
+result.halted? #=> true
+```
+
+## Instance Hooks
+
+You can also define hooks and/or add callbacks per instance. This is helpful if your class should define a basic set of hooks and callbacks that are then extended by instances.
+
+```ruby
+class Cat
+ include Hooks
+ include Hooks::InstanceHooks
+
+ define_hook :after_dark
+
+ after_dark { "Chase mice" }
+end
+```
+
+Note that you have to include `Hooks::InstanceHooks` to get this additional functionality.
+
+See how callbacks can be added to a separate object, now.
+
+```ruby
+garfield = Cat.new
+
+garfield.after_dark :sleep
+garfield.run_hook(:after_dark) # => invoke "Chase mice" hook and #sleep
+```
+
+This will copy all callbacks from the `after_dark` hook to the instance and add a second hook. This all happens on the `garfield` instance, only, and leaves the class untouched.
+
+Naturally, adding new hooks works like-wise.
+
+```ruby
+garfield.define_hook :before_six
+garfield.before_six { .. }
+```
+This feature was added in 0.3.2.
+
+
+## Installation
+
+In your Gemfile, do
+
+```ruby
+gem "hooks"
+```
+
+## Anybody using it?
+
+* Hooks is already used in [Apotomo](http://github.com/apotonick/apotomo), a hot widget framework for Rails.
+* The [datamappify](https://github.com/fredwu/datamappify) gem uses hooks and the author Fred Wu contributed to this gem!
+
+## Similar libraries
+
+* http://github.com/nakajima/aspectory
+* http://github.com/auser/backcall
+* http://github.com/mmcgrana/simple_callbacks
+
+
+## License
+
+Copyright (c) 2013, Nick Sutterer
+
+Released under the MIT License.
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/Rakefile b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/Rakefile
similarity index 100%
rename from middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/Rakefile
rename to middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/Rakefile
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/hooks.gemspec b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/hooks.gemspec
similarity index 71%
rename from middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/hooks.gemspec
rename to middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/hooks.gemspec
index 326f75c0..fe7c1cf4 100644
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/hooks.gemspec
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/hooks.gemspec
@@ -1,7 +1,7 @@
lib = File.expand_path('../lib/', __FILE__)
$:.unshift lib unless $:.include?(lib)
-require 'hooks'
+require 'hooks/version'
Gem::Specification.new do |s|
s.name = "hooks"
@@ -9,14 +9,17 @@ Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.authors = ["Nick Sutterer"]
s.email = ["apotonick@gmail.com"]
- s.homepage = "http://nicksda.apotomo.de/tag/hooks"
+ s.homepage = "http://nicksda.apotomo.de/2010/09/hooks-and-callbacks-for-ruby-but-simple/"
s.summary = %q{Generic hooks with callbacks for Ruby.}
s.description = %q{Declaratively define hooks, add callbacks and run them with the options you like.}
-
+ s.license = "MIT"
+
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.require_paths = ["lib"]
-
- s.add_development_dependency "shoulda"
+ s.license = 'MIT'
+
+ s.add_development_dependency "minitest", ">= 5.0.0"
s.add_development_dependency "rake"
+ s.add_development_dependency "pry"
end
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/lib/hooks.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks.rb
similarity index 54%
rename from middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/lib/hooks.rb
rename to middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks.rb
index 9c4ca879..02ad0964 100644
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/lib/hooks.rb
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks.rb
@@ -1,35 +1,41 @@
require File.join(File.dirname(__FILE__), "hooks/inheritable_attribute")
+require File.join(File.dirname(__FILE__), "hooks/hook")
# Almost like ActiveSupport::Callbacks but 76,6% less complex.
#
# Example:
#
# class CatWidget < Apotomo::Widget
-# define_hook :after_dinner
+# define_hooks :before_dinner, :after_dinner
#
# Now you can add callbacks to your hook declaratively in your class.
#
-# after_dinner do puts "Ice cream!" end
+# before_dinner :wash_paws
+# after_dinner { puts "Ice cream!" }
# after_dinner :have_a_desert # => refers to CatWidget#have_a_desert
#
# Running the callbacks happens on instances. It will run the block and #have_a_desert from above.
#
# cat.run_hook :after_dinner
module Hooks
- VERSION = "0.2.0"
-
def self.included(base)
- base.extend InheritableAttribute
- base.extend ClassMethods
+ base.class_eval do
+ extend InheritableAttribute
+ extend ClassMethods
+ inheritable_attr :_hooks
+ self._hooks= HookSet.new
+ end
end
module ClassMethods
- def define_hook(name)
- accessor_name = "_#{name}_callbacks"
+ def define_hooks(*names)
+ options = extract_options!(names)
- setup_hook_accessors(accessor_name)
- define_hook_writer(name, accessor_name)
+ names.each do |name|
+ setup_hook(name, options)
+ end
end
+ alias_method :define_hook, :define_hooks
# Like Hooks#run_hook but for the class. Note that +:callbacks+ must be class methods.
#
@@ -46,13 +52,7 @@ module Hooks
end
def run_hook_for(name, scope, *args)
- callbacks_for_hook(name).each do |callback|
- if callback.kind_of? Symbol
- scope.send(callback, *args)
- else
- scope.instance_exec(*args, &callback)
- end
- end
+ _hooks[name].run(scope, *args)
end
# Returns the callbacks for +name+. Handy if you want to run the callbacks yourself, say when
@@ -67,28 +67,37 @@ module Hooks
#
# would run callbacks in the object _instance_ context, passing +self+ as block parameter.
def callbacks_for_hook(name)
- send("_#{name}_callbacks")
+ _hooks[name]
end
private
-
- def define_hook_writer(hook, accessor_name)
- self.send(:define_method, hook.to_sym) do |&block|
- if self.class.respond_to?(hook)
- self.class.send(hook.to_sym, &block)
- end
- end
-
- instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def #{hook}(method=nil, &block)
- #{accessor_name} << (block || method)
- end
- RUBY_EVAL
+ def setup_hook(name, options)
+ _hooks[name] = Hook.new(options)
+ define_hook_writer(name)
end
- def setup_hook_accessors(accessor_name)
- inheritable_attr(accessor_name)
- send("#{accessor_name}=", []) # initialize ivar.
+ def define_hook_writer(name)
+ self.send(:define_method, name.to_sym) do |&block|
+ if self.class.respond_to?(name)
+ self.class.send(name.to_sym, &block)
+ end
+ end
+ instance_eval *hook_writer_args(name)
+ end
+
+ def hook_writer_args(name)
+ # DISCUSS: isn't there a simpler way to define a dynamic method? should the internal logic be handled by HooksSet instead?
+ str = <<-RUBY_EVAL
+ def #{name}(method=nil, &block)
+ _hooks[:#{name}] << (block || method)
+ end
+ RUBY_EVAL
+
+ [str, __FILE__, __LINE__ + 1]
+ end
+
+ def extract_options!(args)
+ args.last.is_a?(Hash) ? args.pop : {}
end
end
@@ -106,4 +115,22 @@ module Hooks
def run_hook(name, *args)
self.class.run_hook_for(name, self, *args)
end
+
+ class HookSet < Hash
+ def [](name)
+ super(name.to_sym)
+ end
+
+ def []=(name, values)
+ super(name.to_sym, values)
+ end
+
+ def clone
+ super.tap do |cloned|
+ each { |name, callbacks| cloned[name] = callbacks.clone }
+ end
+ end
+ end
end
+
+require File.join(File.dirname(__FILE__), "hooks/instance_hooks")
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/hook.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/hook.rb
new file mode 100644
index 00000000..0f4d65a7
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/hook.rb
@@ -0,0 +1,81 @@
+module Hooks
+ class Hook < Array
+ def initialize(options)
+ super()
+ @options = options
+ end
+
+ # The chain contains the return values of the executed callbacks.
+ #
+ # Example:
+ #
+ # class Person
+ # define_hook :before_eating
+ #
+ # before_eating :wash_hands
+ # before_eating :locate_food
+ # before_eating :sit_down
+ #
+ # def wash_hands; :washed_hands; end
+ # def locate_food; :located_food; false; end
+ # def sit_down; :sat_down; end
+ # end
+ #
+ # result = person.run_hook(:before_eating)
+ # result.chain #=> [:washed_hands, false, :sat_down]
+ #
+ # If :halts_on_falsey is enabled:
+ #
+ # class Person
+ # define_hook :before_eating, :halts_on_falsey => true
+ # # ...
+ # end
+ #
+ # result = person.run_hook(:before_eating)
+ # result.chain #=> [:washed_hands]
+ def run(scope, *args)
+ inject(Results.new) do |results, callback|
+ executed = execute_callback(scope, callback, *args)
+
+ return results.halted! unless continue_execution?(executed)
+ results << executed
+ end
+ end
+
+ private
+ def execute_callback(scope, callback, *args)
+ if callback.kind_of?(Symbol)
+ scope.send(callback, *args)
+ else
+ scope.instance_exec(*args, &callback)
+ end
+ end
+
+ def continue_execution?(result)
+ @options[:halts_on_falsey] ? result : true
+ end
+
+ class Results < Array
+ # so much code for nothing...
+ def initialize(*)
+ super
+ @halted = false
+ end
+
+ def halted!
+ @halted = true
+ self
+ end
+
+ # Returns true or false based on whether all callbacks
+ # in the hook chain were successfully executed.
+ def halted?
+ @halted
+ end
+
+ def not_halted?
+ not @halted
+ end
+ end
+ end
+end
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/lib/hooks/inheritable_attribute.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/inheritable_attribute.rb
similarity index 98%
rename from middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/lib/hooks/inheritable_attribute.rb
rename to middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/inheritable_attribute.rb
index 88f24b4f..352e28d6 100644
--- a/middleman-core/lib/vendored-middleman-deps/hooks-0.2.0/lib/hooks/inheritable_attribute.rb
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/inheritable_attribute.rb
@@ -22,12 +22,12 @@ module Hooks
def #{name}=(v)
@#{name} = v
end
-
+
def #{name}
return @#{name} unless superclass.respond_to?(:#{name}) and value = superclass.#{name}
@#{name} ||= value.clone # only do this once.
end
}
end
- end
+ end
end
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/instance_hooks.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/instance_hooks.rb
new file mode 100644
index 00000000..0f523fa4
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/instance_hooks.rb
@@ -0,0 +1,25 @@
+module Hooks
+ module InstanceHooks
+ include ClassMethods
+
+ def run_hook(name, *args)
+ run_hook_for(name, self, *args)
+ end
+
+ private
+ def _hooks
+ @_hooks ||= self.class._hooks.clone # TODO: generify that with representable_attrs.
+ end
+
+ module ClassMethods
+ def define_hook_writer(name)
+ super
+ class_eval *hook_writer_args(name)
+ end
+ end
+
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+ end
+end
\ No newline at end of file
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/version.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/version.rb
new file mode 100644
index 00000000..788aaf77
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/lib/hooks/version.rb
@@ -0,0 +1,3 @@
+module Hooks
+ VERSION = "0.3.3"
+end
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/hook_test.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/hook_test.rb
new file mode 100644
index 00000000..5d539be1
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/hook_test.rb
@@ -0,0 +1,31 @@
+require 'test_helper'
+
+class HookTest < MiniTest::Spec
+ subject { Hooks::Hook.new({}) }
+
+ it "exposes array behaviour for callbacks" do
+ subject << :play_music
+ subject << :drink_beer
+
+ subject.to_a.must_equal [:play_music, :drink_beer]
+ end
+end
+
+class ResultsTest < MiniTest::Spec
+ subject { Hooks::Hook::Results.new }
+
+ describe "#halted?" do
+ it "defaults to false" do
+ subject.halted?.must_equal false
+ end
+
+ it "responds to #halted!" do
+ subject.halted!
+ subject.halted?.must_equal true
+ end
+
+ it "responds to #not_halted?" do
+ subject.not_halted?.must_equal true
+ end
+ end
+end
\ No newline at end of file
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/hooks_test.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/hooks_test.rb
new file mode 100644
index 00000000..151a4b3d
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/hooks_test.rb
@@ -0,0 +1,216 @@
+require 'test_helper'
+
+class HooksTest < MiniTest::Spec
+ class TestClass
+ include Hooks
+
+ def executed
+ @executed ||= [];
+ end
+ end
+
+
+ describe "::define_hook" do
+ let(:klass) do
+ Class.new(TestClass) do
+ define_hook :after_eight
+ end
+ end
+
+ subject { klass.new }
+
+ it "respond to Class.callbacks_for_hook" do
+ assert_equal [], klass.callbacks_for_hook(:after_eight)
+ klass.after_eight :dine
+ assert_equal [:dine], klass.callbacks_for_hook(:after_eight)
+ end
+
+ it 'symbolizes strings when defining a hook' do
+ subject.class.define_hooks :before_one, 'after_one'
+ assert_equal [], klass.callbacks_for_hook(:before_one)
+ assert_equal [], klass.callbacks_for_hook(:after_one)
+ assert_equal [], klass.callbacks_for_hook('after_one')
+ end
+
+ it "accept multiple hook names" do
+ subject.class.define_hooks :before_ten, :after_ten
+ assert_equal [], klass.callbacks_for_hook(:before_ten)
+ assert_equal [], klass.callbacks_for_hook(:after_ten)
+ end
+
+ describe "creates a public writer for the hook that" do
+ it "accepts method names" do
+ klass.after_eight :dine
+ assert_equal [:dine], klass._hooks[:after_eight]
+ end
+
+ it "accepts blocks" do
+ klass.after_eight do true; end
+ assert klass._hooks[:after_eight].first.kind_of? Proc
+ end
+
+ it "be inherited" do
+ klass.after_eight :dine
+ subklass = Class.new(klass)
+
+ assert_equal [:dine], subklass._hooks[:after_eight]
+ end
+ # TODO: check if options are not shared!
+ end
+
+ describe "Hooks#run_hook" do
+ it "run without parameters" do
+ subject.instance_eval do
+ def a; executed << :a; nil; end
+ def b; executed << :b; end
+
+ self.class.after_eight :b
+ self.class.after_eight :a
+ end
+
+ subject.run_hook(:after_eight)
+
+ assert_equal [:b, :a], subject.executed
+ end
+
+ it "returns empty Results when no callbacks defined" do
+ subject.run_hook(:after_eight).must_equal Hooks::Hook::Results.new
+ end
+
+ it "accept arbitrary parameters" do
+ subject.instance_eval do
+ def a(me, arg); executed << arg+1; end
+ end
+ subject.class.after_eight :a
+ subject.class.after_eight lambda { |me, arg| me.executed << arg-1 }
+
+ subject.run_hook(:after_eight, subject, 1)
+
+ assert_equal [2, 0], subject.executed
+ end
+
+ it "execute block callbacks in instance context" do
+ subject.class.after_eight { executed << :c }
+ subject.run_hook(:after_eight)
+ assert_equal [:c], subject.executed
+ end
+
+ it "returns all callbacks in order" do
+ subject.class.after_eight { :dinner_out }
+ subject.class.after_eight { :party_hard }
+ subject.class.after_eight { :taxi_home }
+
+ results = subject.run_hook(:after_eight)
+
+ assert_equal [:dinner_out, :party_hard, :taxi_home], results
+ assert_equal false, results.halted?
+ assert_equal true, results.not_halted?
+ end
+
+ describe "halts_on_falsey: true" do
+ let(:klass) do
+ Class.new(TestClass) do
+ define_hook :after_eight, :halts_on_falsey => true
+ end
+ end
+
+ [nil, false].each do |falsey|
+ it "returns successful callbacks in order (with #{falsey.inspect})" do
+ ordered = []
+
+ subject.class.after_eight { :dinner_out }
+ subject.class.after_eight { :party_hard; falsey }
+ subject.class.after_eight { :taxi_home }
+
+ results = subject.run_hook(:after_eight)
+
+ assert_equal [:dinner_out], results
+ assert_equal true, results.halted?
+ end
+ end
+ end
+
+ describe "halts_on_falsey: false" do
+ [nil, false].each do |falsey|
+ it "returns all callbacks in order (with #{falsey.inspect})" do
+ ordered = []
+
+ subject.class.after_eight { :dinner_out }
+ subject.class.after_eight { :party_hard; falsey }
+ subject.class.after_eight { :taxi_home }
+
+ results = subject.run_hook(:after_eight)
+
+ assert_equal [:dinner_out, falsey, :taxi_home], results
+ assert_equal false, results.halted?
+ end
+ end
+ end
+ end
+
+ describe "in class context" do
+ it "runs callback block" do
+ executed = []
+ klass.after_eight do
+ executed << :klass
+ end
+ klass.run_hook(:after_eight)
+
+ executed.must_equal([:klass])
+ end
+
+ it "runs instance methods" do
+ executed = []
+ klass.instance_eval do
+ after_eight :have_dinner
+
+ def have_dinner(executed)
+ executed << :have_dinner
+ end
+ end
+ klass.run_hook(:after_eight, executed)
+
+ executed.must_equal([:have_dinner])
+ end
+ end
+ end
+
+ describe "Inheritance" do
+ let (:superclass) {
+ Class.new(TestClass) do
+ define_hook :after_eight
+
+ after_eight :take_shower
+ end
+ }
+
+ let (:subclass) { Class.new(superclass) do after_eight :have_dinner end }
+
+ it "inherits callbacks from the hook" do
+ subclass.callbacks_for_hook(:after_eight).must_equal [:take_shower, :have_dinner]
+ end
+
+ it "doesn't mix up superclass hooks" do
+ subclass.superclass.callbacks_for_hook(:after_eight).must_equal [:take_shower]
+ end
+ end
+end
+
+class HookSetTest < MiniTest::Spec
+ subject { Hooks::HookSet.new }
+
+ let (:first_hook) { Hooks::Hook.new(:halts_on_falsey => true) }
+ let (:second_hook) { Hooks::Hook.new(:halts_on_falsey => false) }
+
+ it "responds to #clone" do
+ subject[:after_eight] = [first_hook]
+
+ clone = subject.clone
+
+ clone[:after_eight] << second_hook
+
+ subject.must_equal(:after_eight => [first_hook])
+ clone.must_equal(:after_eight => [first_hook, second_hook])
+ end
+ # TODO: test if options get cloned.
+end
\ No newline at end of file
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/inheritable_attribute_test.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/inheritable_attribute_test.rb
new file mode 100644
index 00000000..4cc14e58
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/inheritable_attribute_test.rb
@@ -0,0 +1,53 @@
+require 'test_helper'
+
+class HooksTest < MiniTest::Spec
+ describe "Hooks.define_hook" do
+ subject {
+ Class.new(Object) do
+ extend Hooks::InheritableAttribute
+ inheritable_attr :drinks
+ end
+ }
+
+ it "provides a reader with empty inherited attributes, already" do
+ assert_equal nil, subject.drinks
+ end
+
+ it "provides a reader with empty inherited attributes in a derived class" do
+ assert_equal nil, Class.new(subject).drinks
+ #subject.drinks = true
+ #Class.new(subject).drinks # TODO: crashes.
+ end
+
+ it "provides an attribute copy in subclasses" do
+ subject.drinks = []
+ assert subject.drinks.object_id != Class.new(subject).drinks.object_id
+ end
+
+ it "provides a writer" do
+ subject.drinks = [:cabernet]
+ assert_equal [:cabernet], subject.drinks
+ end
+
+ it "inherits attributes" do
+ subject.drinks = [:cabernet]
+
+ subklass_a = Class.new(subject)
+ subklass_a.drinks << :becks
+
+ subklass_b = Class.new(subject)
+
+ assert_equal [:cabernet], subject.drinks
+ assert_equal [:cabernet, :becks], subklass_a.drinks
+ assert_equal [:cabernet], subklass_b.drinks
+ end
+
+ it "does not inherit attributes if we set explicitely" do
+ subject.drinks = [:cabernet]
+ subklass = Class.new(subject)
+
+ subklass.drinks = [:merlot] # we only want merlot explicitely.
+ assert_equal [:merlot], subklass.drinks # no :cabernet, here
+ end
+ end
+end
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/instance_hooks_test.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/instance_hooks_test.rb
new file mode 100644
index 00000000..ffac3fb5
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/instance_hooks_test.rb
@@ -0,0 +1,55 @@
+require "test_helper"
+
+class InstanceHooksTest < HooksTest
+ describe "#define_hook" do
+ let(:klass) { Class.new(TestClass) do
+ include Hooks::InstanceHooks
+ end }
+
+ subject { klass.new.tap do |obj|
+ obj.instance_eval do
+ def dine; executed << :dine; end
+ end
+ end }
+
+ it "adds hook to instance" do
+ subject.define_hook :after_eight
+
+ assert_equal [], subject.callbacks_for_hook(:after_eight)
+ end
+
+ it "copies existing class hook" do
+ klass.define_hook :after_eight
+ klass.after_eight :dine
+
+ assert_equal [:dine], subject.callbacks_for_hook(:after_eight)
+ end
+
+ describe "#after_eight (adding callbacks)" do
+ before do
+ subject.define_hook :after_eight
+ subject.after_eight :dine
+ end
+
+ it "adds #after_eight hook" do
+ assert_equal [:dine], subject.callbacks_for_hook(:after_eight)
+ end
+
+ it "responds to #run_hook" do
+ subject.run_hook :after_eight
+ subject.executed.must_equal [:dine]
+ end
+ end
+
+ describe "#after_eight from class (no define_hook in instance)" do
+ it "responds to #after_eight" do
+ klass.define_hook :after_eight
+
+ subject.after_eight :dine
+
+ subject.run_hook :after_eight
+ subject.executed.must_equal [:dine]
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/test_helper.rb b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/test_helper.rb
new file mode 100644
index 00000000..2ece0d0e
--- /dev/null
+++ b/middleman-core/lib/vendored-middleman-deps/hooks-0.3.3/test/test_helper.rb
@@ -0,0 +1,3 @@
+require 'minitest/autorun'
+require 'pry'
+require 'hooks'