diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb index 0b67d48f..5110aa36 100644 --- a/middleman-core/lib/middleman-core/application.rb +++ b/middleman-core/lib/middleman-core/application.rb @@ -10,7 +10,7 @@ require 'active_support/json' require 'active_support/core_ext/integer/inflections' # 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' @@ -198,7 +198,7 @@ module Middleman # Evaluate a passed block if given @config_context.instance_exec(&block) if block_given? - + super end 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'